[recovery_netstack] Use 'packet' crate for parsing/serialization
Test: Existing unit tests
Change-Id: Id0c14b87179e53c3fd1c94bf18e714f7bfd233dc
diff --git a/bin/recovery_netstack/core/BUILD.gn b/bin/recovery_netstack/core/BUILD.gn
index 8c6a77c..7b57629 100644
--- a/bin/recovery_netstack/core/BUILD.gn
+++ b/bin/recovery_netstack/core/BUILD.gn
@@ -11,6 +11,7 @@
with_unit_tests = true
deps = [
+ "//garnet/public/rust/packet",
"//garnet/public/rust/zerocopy",
"//third_party/rust-crates/rustc_deps:byteorder",
"//third_party/rust-crates/rustc_deps:failure",
diff --git a/bin/recovery_netstack/core/src/device/arp.rs b/bin/recovery_netstack/core/src/device/arp.rs
index 25bd6ad..7e8d684 100644
--- a/bin/recovery_netstack/core/src/device/arp.rs
+++ b/bin/recovery_netstack/core/src/device/arp.rs
@@ -8,13 +8,12 @@
use std::hash::Hash;
use std::time::Duration;
+use packet::{BufferMut, InnerSerializer, Serializer};
+
use crate::device::ethernet::EthernetArpDevice;
use crate::device::DeviceLayerTimerId;
use crate::ip::Ipv4Addr;
-use crate::wire::{
- arp::{ArpPacket, ArpPacketSerializer, HType, PType},
- BufferAndRange, SerializationRequest,
-};
+use crate::wire::arp::{ArpPacket, ArpPacketBuilder, HType, PType};
use crate::{Context, EventDispatcher, TimerId, TimerIdInner};
use log::debug;
@@ -90,9 +89,9 @@
/// Send an ARP packet in a device layer frame.
///
/// `send_arp_frame` accepts a device ID, a destination hardware address,
- /// and a `SerializationRequest`. It computes the routing information and
- /// serializes the request in a device layer frame and sends it.
- fn send_arp_frame<D: EventDispatcher, S: SerializationRequest>(
+ /// and a `Serializer`. It computes the routing information, serializes the
+ /// request in a device layer frame, and sends it.
+ fn send_arp_frame<D: EventDispatcher, S: Serializer>(
ctx: &mut Context<D>, device_id: u64, dst: Self::HardwareAddr, body: S,
);
@@ -135,13 +134,13 @@
D: EventDispatcher,
P: PType + Eq + Hash,
AD: ArpDevice<P>,
- B: AsRef<[u8]> + AsMut<[u8]>,
+ B: BufferMut,
>(
ctx: &mut Context<D>, device_id: u64, src_addr: AD::HardwareAddr, dst_addr: AD::HardwareAddr,
- mut buffer: BufferAndRange<B>,
+ mut buffer: B,
) {
// TODO(wesleyac) Add support for gratuitous ARP and probe/announce.
- let packet = if let Ok(packet) = ArpPacket::<_, AD::HardwareAddr, P>::parse(buffer.as_mut()) {
+ let packet = if let Ok(packet) = buffer.parse::<ArpPacket<_, AD::HardwareAddr, P>>() {
let addressed_to_me =
Some(packet.target_protocol_address()) == AD::get_protocol_addr(ctx, device_id);
let table = &mut AD::get_arp_state(ctx, device_id).table;
@@ -209,14 +208,16 @@
ctx,
device_id,
packet.sender_hardware_address(),
- ArpPacketSerializer::new(
- ArpOp::Response,
- self_hw_addr,
- packet.target_protocol_address(),
- packet.sender_hardware_address(),
- packet.sender_protocol_address(),
- )
- .serialize_outer(),
+ InnerSerializer::new_vec(
+ ArpPacketBuilder::new(
+ ArpOp::Response,
+ self_hw_addr,
+ packet.target_protocol_address(),
+ packet.sender_hardware_address(),
+ packet.sender_protocol_address(),
+ ),
+ buffer,
+ ),
);
}
} else {
@@ -256,7 +257,7 @@
ctx,
device_id,
AD::BROADCAST,
- ArpPacketSerializer::new(
+ ArpPacketBuilder::new(
ArpOp::Request,
self_hw_addr,
sender_protocol_addr,
@@ -265,8 +266,7 @@
// address we are sending the packet to.
AD::BROADCAST,
lookup_addr,
- )
- .serialize_outer(),
+ ),
);
// TODO(wesleyac): Configurable timeout.
@@ -356,14 +356,15 @@
#[cfg(test)]
mod tests {
+ use packet::ParseBuffer;
+
use super::*;
use crate::device::ethernet::{set_ip_addr, EtherType, Mac};
use crate::ip::{Ipv4Addr, Subnet};
use crate::testutil;
use crate::testutil::DummyEventDispatcher;
- use crate::wire::arp::{peek_arp_types, ArpPacketSerializer};
+ use crate::wire::arp::{peek_arp_types, ArpPacketBuilder};
use crate::wire::ethernet::EthernetFrame;
- use crate::wire::{BufferAndRange, InnerSerializationRequest};
use crate::StackState;
const TEST_LOCAL_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
@@ -396,8 +397,9 @@
for packet_num in 0..3 {
assert_eq!(ctx.dispatcher.frames_sent().len(), packet_num + 1);
- let (frame, _) =
- EthernetFrame::parse(&ctx.dispatcher.frames_sent()[packet_num].1[..]).unwrap();
+ let mut buf = &ctx.dispatcher.frames_sent()[packet_num].1[..];
+
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Arp)));
assert_eq!(frame.src_mac(), TEST_LOCAL_MAC);
assert_eq!(EthernetArpDevice::BROADCAST, frame.dst_mac());
@@ -406,7 +408,7 @@
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
- let arp = ArpPacket::<_, Mac, Ipv4Addr>::parse(frame.body()).unwrap();
+ let arp = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
assert_eq!(arp.operation(), ArpOp::Request);
assert_eq!(arp.sender_hardware_address(), TEST_LOCAL_MAC);
assert_eq!(arp.target_hardware_address(), EthernetArpDevice::BROADCAST);
@@ -432,13 +434,13 @@
Subnet::new(TEST_LOCAL_IPV4, 24),
);
- let mut buf = InnerSerializationRequest::new(ArpPacketSerializer::new(
+ let mut buf = ArpPacketBuilder::new(
ArpOp::Request,
TEST_REMOTE_MAC,
TEST_REMOTE_IPV4,
TEST_LOCAL_MAC,
TEST_LOCAL_IPV4,
- ))
+ )
.serialize_outer();
let (hw, proto) = peek_arp_types(buf.as_ref()).unwrap();
assert_eq!(hw, ArpHardwareType::Ethernet);
@@ -449,7 +451,7 @@
1,
TEST_REMOTE_MAC,
TEST_LOCAL_MAC,
- BufferAndRange::new_from(&mut buf, ..),
+ buf,
);
assert_eq!(
@@ -465,7 +467,9 @@
assert_eq!(ctx.dispatcher.frames_sent().len(), 1);
- let (frame, _) = EthernetFrame::parse(&ctx.dispatcher.frames_sent()[0].1[..]).unwrap();
+ let mut buf = &ctx.dispatcher.frames_sent()[0].1[..];
+
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Arp)));
assert_eq!(frame.src_mac(), TEST_LOCAL_MAC);
assert_eq!(frame.dst_mac(), TEST_REMOTE_MAC);
@@ -474,7 +478,7 @@
assert_eq!(hw, ArpHardwareType::Ethernet);
assert_eq!(proto, EtherType::Ipv4);
- let arp = ArpPacket::<_, Mac, Ipv4Addr>::parse(frame.body()).unwrap();
+ let arp = buf.parse::<ArpPacket<_, Mac, Ipv4Addr>>().unwrap();
assert_eq!(arp.operation(), ArpOp::Response);
assert_eq!(arp.sender_hardware_address(), TEST_LOCAL_MAC);
assert_eq!(arp.target_hardware_address(), TEST_REMOTE_MAC);
diff --git a/bin/recovery_netstack/core/src/device/ethernet.rs b/bin/recovery_netstack/core/src/device/ethernet.rs
index 24f702f..55cc15e 100644
--- a/bin/recovery_netstack/core/src/device/ethernet.rs
+++ b/bin/recovery_netstack/core/src/device/ethernet.rs
@@ -7,14 +7,14 @@
use std::fmt::{self, Display, Formatter};
use log::debug;
+use packet::{Buf, ParseBuffer, Serializer};
use zerocopy::{AsBytes, FromBytes, Unaligned};
use crate::device::arp::{ArpDevice, ArpHardwareType, ArpState};
use crate::device::DeviceId;
use crate::ip::{Ip, IpAddr, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr, Subnet};
use crate::wire::arp::peek_arp_types;
-use crate::wire::ethernet::{EthernetFrame, EthernetFrameSerializer};
-use crate::wire::{BufferAndRange, SerializationRequest};
+use crate::wire::ethernet::{EthernetFrame, EthernetFrameBuilder};
use crate::{Context, EventDispatcher};
/// A media access control (MAC) address.
@@ -189,7 +189,7 @@
ctx: &mut Context<D>, device_id: u64, local_addr: A, body: S,
) where
A: IpAddr,
- S: SerializationRequest,
+ S: Serializer,
{
specialize_ip_addr!(
fn lookup_dst_mac<D>(ctx: &mut Context<D>, device_id: u64, local_addr: Self) -> Option<Mac>
@@ -216,7 +216,7 @@
if let Some(dst_mac) = A::lookup_dst_mac(ctx, device_id, local_addr) {
let src_mac = get_device_state(ctx, device_id).mac;
let buffer = body
- .encapsulate(EthernetFrameSerializer::new(
+ .encapsulate(EthernetFrameBuilder::new(
src_mac,
dst_mac,
A::Version::ETHER_TYPE,
@@ -229,7 +229,8 @@
/// Receive an Ethernet frame from the network.
pub fn receive_frame<D: EventDispatcher>(ctx: &mut Context<D>, device_id: u64, bytes: &mut [u8]) {
- let (frame, body_range) = if let Ok(frame) = EthernetFrame::parse(&mut bytes[..]) {
+ let mut buffer = Buf::new(bytes, ..);
+ let frame = if let Ok(frame) = buffer.parse::<EthernetFrame<_>>() {
frame
} else {
// TODO(joshlf): Do something else?
@@ -239,7 +240,6 @@
if let Some(Ok(ethertype)) = frame.ethertype() {
let (src, dst) = (frame.src_mac(), frame.dst_mac());
let device = DeviceId::new_ethernet(device_id);
- let buffer = BufferAndRange::new_from(bytes, body_range);
match ethertype {
EtherType::Arp => {
let types = if let Ok(types) = peek_arp_types(buffer.as_ref()) {
@@ -257,8 +257,8 @@
types => debug!("got ARP packet for unsupported types: {:?}", types),
}
}
- EtherType::Ipv4 => crate::ip::receive_ip_packet::<D, Ipv4>(ctx, device, buffer),
- EtherType::Ipv6 => crate::ip::receive_ip_packet::<D, Ipv6>(ctx, device, buffer),
+ EtherType::Ipv4 => crate::ip::receive_ip_packet::<D, _, Ipv4>(ctx, device, buffer),
+ EtherType::Ipv6 => crate::ip::receive_ip_packet::<D, _, Ipv6>(ctx, device, buffer),
}
} else {
// TODO(joshlf): Do something else?
@@ -310,12 +310,12 @@
type HardwareAddr = Mac;
const BROADCAST: Mac = Mac::BROADCAST;
- fn send_arp_frame<D: EventDispatcher, S: SerializationRequest>(
+ fn send_arp_frame<D: EventDispatcher, S: Serializer>(
ctx: &mut Context<D>, device_id: u64, dst: Self::HardwareAddr, body: S,
) {
let src = get_device_state(ctx, device_id).mac;
let buffer = body
- .encapsulate(EthernetFrameSerializer::new(src, dst, EtherType::Arp))
+ .encapsulate(EthernetFrameBuilder::new(src, dst, EtherType::Arp))
.serialize_outer();
ctx.dispatcher()
.send_frame(DeviceId::new_ethernet(device_id), buffer.as_ref());
diff --git a/bin/recovery_netstack/core/src/device/mod.rs b/bin/recovery_netstack/core/src/device/mod.rs
index 32c233d..a75738c 100644
--- a/bin/recovery_netstack/core/src/device/mod.rs
+++ b/bin/recovery_netstack/core/src/device/mod.rs
@@ -11,10 +11,10 @@
use std::fmt::{self, Debug, Display, Formatter};
use log::debug;
+use packet::Serializer;
use crate::device::ethernet::{EthernetDeviceState, Mac};
use crate::ip::{IpAddr, Ipv4Addr, Subnet};
-use crate::wire::SerializationRequest;
use crate::{Context, EventDispatcher};
/// An ID identifying a device.
@@ -134,7 +134,7 @@
ctx: &mut Context<D>, device: DeviceId, local_addr: A, body: S,
) where
A: IpAddr,
- S: SerializationRequest,
+ S: Serializer,
{
match device.protocol {
DeviceProtocol::Ethernet => self::ethernet::send_ip_frame(ctx, device.id, local_addr, body),
diff --git a/bin/recovery_netstack/core/src/ip/icmp.rs b/bin/recovery_netstack/core/src/ip/icmp.rs
index e87a73c..573b2e9 100644
--- a/bin/recovery_netstack/core/src/ip/icmp.rs
+++ b/bin/recovery_netstack/core/src/ip/icmp.rs
@@ -7,32 +7,27 @@
use std::mem;
use log::trace;
+use packet::{BufferMut, BufferSerializer, Serializer};
use crate::ip::{send_ip_packet, IpAddr, IpProto, Ipv4, Ipv6};
-use crate::wire::icmp::{IcmpPacketSerializer, Icmpv4Packet, Icmpv6Packet};
-use crate::wire::{BufferAndRange, SerializationRequest};
+use crate::wire::icmp::{IcmpPacketBuilder, IcmpParseArgs, Icmpv4Packet, Icmpv6Packet};
use crate::{Context, EventDispatcher};
/// Receive an ICMP message in an IP packet.
-pub fn receive_icmp_packet<D: EventDispatcher, A: IpAddr, B: AsRef<[u8]> + AsMut<[u8]>>(
- ctx: &mut Context<D>, src_ip: A, dst_ip: A, buffer: BufferAndRange<B>,
+pub fn receive_icmp_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
+ ctx: &mut Context<D>, src_ip: A, dst_ip: A, buffer: B,
) -> bool {
trace!("receive_icmp_packet({}, {})", src_ip, dst_ip);
- // specialize_ip_addr! can't handle trait bounds with type arguments, so
- // create AsMutU8 which is equivalent to AsMut<[u8]>, but without the type
- // arguments. Ew.
- trait AsU8: AsRef<[u8]> + AsMut<[u8]> {}
- impl<A: AsRef<[u8]> + AsMut<[u8]>> AsU8 for A {}
specialize_ip_addr!(
- fn receive_icmp_packet<D, B>(ctx: &mut Context<D>, src_ip: Self, dst_ip: Self, buffer: BufferAndRange<B>) -> bool
+ fn receive_icmp_packet<D, B>(ctx: &mut Context<D>, src_ip: Self, dst_ip: Self, buffer: B) -> bool
where
D: EventDispatcher,
- B: AsU8,
+ B: BufferMut,
{
Ipv4Addr => {
let mut buffer = buffer;
- let (packet, body_range) = match Icmpv4Packet::parse(buffer.as_mut(), src_ip, dst_ip) {
+ let packet = match buffer.parse_with::<_, Icmpv4Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
Ok(packet) => packet,
Err(err) => return false,
};
@@ -43,21 +38,17 @@
let code = echo_request.code();
// drop packet so we can re-use the underlying buffer
mem::drop(echo_request);
- // slice the buffer to be only the body range
- buffer.slice(body_range);
increment_counter!(ctx, "receive_icmp_packet::echo_request");
// we're responding to the sender, so these are flipped
let (src_ip, dst_ip) = (dst_ip, src_ip);
- send_ip_packet(ctx, dst_ip, IpProto::Icmp, |src_ip| {
- buffer.encapsulate(IcmpPacketSerializer::<Ipv4, B, _>::new(
- src_ip,
- dst_ip,
- code,
- req.reply(),
- ))
- });
+ send_ip_packet(
+ ctx,
+ dst_ip,
+ IpProto::Icmp,
+ |src_ip| BufferSerializer::new_vec(buffer).encapsulate(IcmpPacketBuilder::<Ipv4, &[u8], _>::new(src_ip, dst_ip, code, req.reply())),
+ );
true
}
Icmpv4Packet::EchoReply(echo_reply) => {
@@ -73,11 +64,9 @@
}
Ipv6Addr => {
let mut buffer = buffer;
- let (packet, body_range) = match Icmpv6Packet::parse(buffer.as_mut(), src_ip, dst_ip) {
+ let packet = match buffer.parse_with::<_, Icmpv6Packet<_>>(IcmpParseArgs::new(src_ip, dst_ip)) {
Ok(packet) => packet,
- Err(err) => {
- return false
- },
+ Err(err) => return false,
};
match packet {
@@ -86,21 +75,17 @@
let code = echo_request.code();
// drop packet so we can re-use the underlying buffer
mem::drop(echo_request);
- // slice the buffer to be only the body range
- buffer.slice(body_range);
increment_counter!(ctx, "receive_icmp_packet::echo_request");
// we're responding to the sender, so these are flipped
let (src_ip, dst_ip) = (dst_ip, src_ip);
- send_ip_packet(ctx, dst_ip, IpProto::Icmp, |src_ip| {
- buffer.encapsulate(IcmpPacketSerializer::<Ipv6, B, _>::new(
- src_ip,
- dst_ip,
- code,
- req.reply(),
- ))
- });
+ send_ip_packet(
+ ctx,
+ dst_ip,
+ IpProto::Icmp,
+ |src_ip| BufferSerializer::new_vec(buffer).encapsulate(IcmpPacketBuilder::<Ipv6, &[u8], _>::new(src_ip, dst_ip, code, req.reply())),
+ );
true
}
Icmpv6Packet::EchoReply(echo_reply) => {
@@ -122,6 +107,8 @@
#[cfg(test)]
mod tests {
+ use packet::Buf;
+
use super::*;
use crate::ip::{Ip, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
use crate::testutil::DummyEventDispatcher;
@@ -135,10 +122,8 @@
let src = <Ipv4 as Ip>::LOOPBACK_ADDRESS;
let dst = Ipv4Addr::new([192, 168, 1, 5]);
let mut bytes = REQUEST_IP_PACKET_BYTES.to_owned();
- let len = bytes.len();
- let buf = BufferAndRange::new_from(&mut bytes, 20..len);
- receive_icmp_packet(&mut ctx, src, dst, buf);
+ receive_icmp_packet(&mut ctx, src, dst, Buf::new(&mut bytes[20..], ..));
assert_eq!(
ctx.state()
.test_counters
@@ -172,10 +157,8 @@
let src = <Ipv6 as Ip>::LOOPBACK_ADDRESS;
let dst = Ipv6Addr::new([0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let mut bytes = REQUEST_IP_PACKET_BYTES.to_owned();
- let len = bytes.len();
- let buf = BufferAndRange::new_from(&mut bytes, 40..len);
- receive_icmp_packet(&mut ctx, src, dst, buf);
+ receive_icmp_packet(&mut ctx, src, dst, Buf::new(&mut bytes[40..], ..));
assert_eq!(
ctx.state()
.test_counters
diff --git a/bin/recovery_netstack/core/src/ip/mod.rs b/bin/recovery_netstack/core/src/ip/mod.rs
index 1d1b3b6..3c36c28 100644
--- a/bin/recovery_netstack/core/src/ip/mod.rs
+++ b/bin/recovery_netstack/core/src/ip/mod.rs
@@ -15,14 +15,14 @@
use log::{debug, trace};
use std::fmt::Debug;
use std::mem;
-use std::ops::Range;
+
+use packet::{BufferMut, BufferSerializer, ParsablePacket, ParseBufferMut, Serializer};
+use zerocopy::{ByteSlice, ByteSliceMut};
use crate::device::DeviceId;
-use crate::error::ParseError;
use crate::ip::forwarding::{Destination, ForwardingTable};
-use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketSerializer};
-use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketSerializer};
-use crate::wire::{BufferAndRange, SerializationRequest};
+use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketBuilder};
+use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
use crate::{Context, EventDispatcher};
// default IPv4 TTL or IPv6 hops
@@ -41,28 +41,29 @@
table: ForwardingTable<I>,
}
-fn dispatch_receive_ip_packet<D: EventDispatcher, I: IpAddr, B: AsRef<[u8]> + AsMut<[u8]>>(
- ctx: &mut Context<D>, proto: IpProto, src_ip: I, dst_ip: I, mut buffer: BufferAndRange<B>,
+fn dispatch_receive_ip_packet<D: EventDispatcher, I: IpAddr, B: BufferMut>(
+ ctx: &mut Context<D>, proto: IpProto, src_ip: I, dst_ip: I, mut buffer: B,
) -> bool {
increment_counter!(ctx, "dispatch_receive_ip_packet");
match proto {
IpProto::Icmp | IpProto::Icmpv6 => icmp::receive_icmp_packet(ctx, src_ip, dst_ip, buffer),
- IpProto::Tcp | IpProto::Udp => crate::transport::receive_ip_packet(ctx, src_ip, dst_ip, proto, buffer),
+ IpProto::Tcp | IpProto::Udp => {
+ crate::transport::receive_ip_packet(ctx, src_ip, dst_ip, proto, buffer)
+ }
}
}
/// Receive an IP packet from a device.
-pub fn receive_ip_packet<D: EventDispatcher, I: Ip>(
- ctx: &mut Context<D>, device: DeviceId, mut buffer: BufferAndRange<&mut [u8]>,
+pub fn receive_ip_packet<D: EventDispatcher, B: BufferMut, I: Ip>(
+ ctx: &mut Context<D>, device: DeviceId, mut buffer: B,
) {
trace!("receive_ip_packet({})", device);
- let (mut packet, body_range) =
- if let Ok((packet, body_range)) = <I as IpExt>::Packet::parse(buffer.as_mut()) {
- (packet, body_range)
- } else {
- // TODO(joshlf): Do something with ICMP here?
- return;
- };
+ let mut packet = if let Ok(packet) = buffer.parse_mut::<<I as IpExt<_>>::Packet>() {
+ packet
+ } else {
+ // TODO(joshlf): Do something with ICMP here?
+ return;
+ };
trace!("receive_ip_packet: parsed packet: {:?}", packet);
if I::LOOPBACK_SUBNET.contains(packet.dst_ip()) {
@@ -84,8 +85,6 @@
let dst_ip = packet.dst_ip();
// drop packet so we can re-use the underlying buffer
mem::drop(packet);
- // slice the buffer to be only the body range
- buffer.slice(body_range);
dispatch_receive_ip_packet(ctx, proto, src_ip, dst_ip, buffer)
} else {
// TODO(joshlf): Log unrecognized protocol number
@@ -96,9 +95,18 @@
if ttl > 1 {
trace!("receive_ip_packet: forwarding");
packet.set_ttl(ttl - 1);
+ let meta = packet.parse_metadata();
// drop packet so we can re-use the underlying buffer
mem::drop(packet);
- crate::device::send_ip_frame(ctx, dest.device, dest.next_hop, buffer);
+ // Undo the effects of parsing so that the body of the buffer
+ // contains the entire IP packet again (not just the body).
+ buffer.undo_parse(meta);
+ crate::device::send_ip_frame(
+ ctx,
+ dest.device,
+ dest.next_hop,
+ BufferSerializer::new_vec(buffer),
+ );
return;
} else {
// TTL is 0 or would become 0 after decrement; see "TTL" section,
@@ -184,7 +192,9 @@
}
/// Add a route to the forwarding table.
-pub fn add_device_route<D: EventDispatcher, A: IpAddr>(ctx: &mut Context<D>, subnet: Subnet<A>, device: DeviceId) {
+pub fn add_device_route<D: EventDispatcher, A: IpAddr>(
+ ctx: &mut Context<D>, subnet: Subnet<A>, device: DeviceId,
+) {
specialize_ip_addr!(
fn generic_add_route(state: &mut IpLayerState, subnet: Subnet<Self>, device: DeviceId) {
Ipv4Addr => { state.v4.table.add_device_route(subnet, device) }
@@ -212,18 +222,27 @@
ctx: &mut Context<D>, dst_ip: A, proto: IpProto, get_body: F,
) where
A: IpAddr,
- S: SerializationRequest,
+ S: Serializer,
F: FnOnce(A) -> S,
{
trace!("send_ip_packet({}, {})", dst_ip, proto);
increment_counter!(ctx, "send_ip_packet");
if A::Version::LOOPBACK_SUBNET.contains(dst_ip) {
increment_counter!(ctx, "send_ip_packet::loopback");
- let buffer = get_body(A::Version::LOOPBACK_ADDRESS).serialize(0, 0, 0);
+ // TODO(joshlf): Currently, we serialize using the normal Serializer
+ // functionality. I wonder if, in the case of delivering to loopback, we
+ // can do something more efficient?
+ let mut buffer = get_body(A::Version::LOOPBACK_ADDRESS).serialize_outer();
// TODO(joshlf): Respond with some kind of error if we don't have a
// handler for that protocol? Maybe simulate what would have happened
// (w.r.t ICMP) if this were a remote host?
- dispatch_receive_ip_packet(ctx, proto, A::Version::LOOPBACK_ADDRESS, dst_ip, buffer);
+ dispatch_receive_ip_packet(
+ ctx,
+ proto,
+ A::Version::LOOPBACK_ADDRESS,
+ dst_ip,
+ buffer.as_buf_mut(),
+ );
} else if let Some(dest) = lookup_route(&ctx.state().ip, dst_ip) {
let (src_ip, _) = crate::device::get_ip_addr(ctx, dest.device)
.expect("IP device route set for device without IP address");
@@ -237,7 +256,7 @@
get_body(src_ip),
);
} else {
- println!("No route to host");
+ debug!("No route to host");
// TODO(joshlf): No route to host
}
}
@@ -256,7 +275,7 @@
ctx: &mut Context<D>, src_ip: A, dst_ip: A, proto: IpProto, body: S,
) where
A: IpAddr,
- S: SerializationRequest,
+ S: Serializer,
{
// TODO(joshlf): Figure out how to compute a route with the restrictions
// mentioned in the doc comment.
@@ -280,7 +299,7 @@
body: S,
) where
A: IpAddr,
- S: SerializationRequest,
+ S: Serializer,
{
assert!(!A::Version::LOOPBACK_SUBNET.contains(src_ip));
assert!(!A::Version::LOOPBACK_SUBNET.contains(dst_ip));
@@ -291,14 +310,14 @@
)
where
D: EventDispatcher,
- S: SerializationRequest,
+ S: Serializer,
{
Ipv4Addr => {
- let body = body.encapsulate(Ipv4PacketSerializer::new(src_ip, dst_ip, ttl, proto));
+ let body = body.encapsulate(Ipv4PacketBuilder::new(src_ip, dst_ip, ttl, proto));
crate::device::send_ip_frame(ctx, device, next_hop, body);
}
Ipv6Addr => {
- let body = body.encapsulate(Ipv6PacketSerializer::new(src_ip, dst_ip, ttl, proto));
+ let body = body.encapsulate(Ipv6PacketBuilder::new(src_ip, dst_ip, ttl, proto));
crate::device::send_ip_frame(ctx, device, next_hop, body);
}
}
@@ -319,36 +338,34 @@
//
// This trait adds extra associated types that are useful for our implementation
// here, but which consumers outside of the ip module do not need to see.
-trait IpExt<'a>: Ip {
- type Packet: IpPacket<'a, Self>;
+trait IpExt<B: ByteSlice>: Ip {
+ type Packet: IpPacket<B, Self>;
}
-impl<'a, I: Ip> IpExt<'a> for I {
+impl<B: ByteSlice, I: Ip> IpExt<B> for I {
default type Packet = !;
}
-impl<'a> IpExt<'a> for Ipv4 {
- type Packet = Ipv4Packet<&'a mut [u8]>;
+impl<B: ByteSlice> IpExt<B> for Ipv4 {
+ type Packet = Ipv4Packet<B>;
}
-impl<'a> IpExt<'a> for Ipv6 {
- type Packet = Ipv6Packet<&'a mut [u8]>;
+impl<B: ByteSlice> IpExt<B> for Ipv6 {
+ type Packet = Ipv6Packet<B>;
}
// `Ipv4Packet` or `Ipv6Packet`
-trait IpPacket<'a, I: Ip>: Sized + Debug {
- fn parse(bytes: &'a mut [u8]) -> Result<(Self, Range<usize>), ParseError>;
+trait IpPacket<B: ByteSlice, I: Ip>: Sized + Debug + ParsablePacket<B, ()> {
fn src_ip(&self) -> I::Addr;
fn dst_ip(&self) -> I::Addr;
fn proto(&self) -> Result<IpProto, u8>;
fn ttl(&self) -> u8;
- fn set_ttl(&mut self, ttl: u8);
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut;
}
-impl<'a> IpPacket<'a, Ipv4> for Ipv4Packet<&'a mut [u8]> {
- fn parse(bytes: &'a mut [u8]) -> Result<(Ipv4Packet<&'a mut [u8]>, Range<usize>), ParseError> {
- Ipv4Packet::parse(bytes)
- }
+impl<B: ByteSlice> IpPacket<B, Ipv4> for Ipv4Packet<B> {
fn src_ip(&self) -> Ipv4Addr {
Ipv4Packet::src_ip(self)
}
@@ -361,15 +378,15 @@
fn ttl(&self) -> u8 {
Ipv4Packet::ttl(self)
}
- fn set_ttl(&mut self, ttl: u8) {
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut,
+ {
Ipv4Packet::set_ttl(self, ttl)
}
}
-impl<'a> IpPacket<'a, Ipv6> for Ipv6Packet<&'a mut [u8]> {
- fn parse(bytes: &'a mut [u8]) -> Result<(Ipv6Packet<&'a mut [u8]>, Range<usize>), ParseError> {
- Ipv6Packet::parse(bytes)
- }
+impl<B: ByteSlice> IpPacket<B, Ipv6> for Ipv6Packet<B> {
fn src_ip(&self) -> Ipv6Addr {
Ipv6Packet::src_ip(self)
}
@@ -382,7 +399,10 @@
fn ttl(&self) -> u8 {
Ipv6Packet::hop_limit(self)
}
- fn set_ttl(&mut self, ttl: u8) {
+ fn set_ttl(&mut self, ttl: u8)
+ where
+ B: ByteSliceMut,
+ {
Ipv6Packet::set_hop_limit(self, ttl)
}
}
diff --git a/bin/recovery_netstack/core/src/lib.rs b/bin/recovery_netstack/core/src/lib.rs
index 07210f0..ff0c52c 100644
--- a/bin/recovery_netstack/core/src/lib.rs
+++ b/bin/recovery_netstack/core/src/lib.rs
@@ -21,6 +21,7 @@
// TODO(joshlf): Remove this once all of the elements in the crate are actually
// used.
#![allow(unused)]
+#![deny(unused_imports)]
#[macro_use]
mod macros;
@@ -35,10 +36,7 @@
pub mod types;
-use crate::device::{
- ethernet::Mac, receive_frame, DeviceId, DeviceLayerEventDispatcher, DeviceLayerTimerId,
-};
-use crate::transport::udp::UdpEventDispatcher;
+use crate::device::{ethernet::Mac, DeviceId, DeviceLayerEventDispatcher, DeviceLayerTimerId};
use crate::transport::{TransportLayerEventDispatcher, TransportLayerTimerId};
use crate::device::DeviceLayerState;
diff --git a/bin/recovery_netstack/core/src/transport/mod.rs b/bin/recovery_netstack/core/src/transport/mod.rs
index 6d9b72e..8760d6f 100644
--- a/bin/recovery_netstack/core/src/transport/mod.rs
+++ b/bin/recovery_netstack/core/src/transport/mod.rs
@@ -61,9 +61,10 @@
use std::collections::HashMap;
use std::hash::Hash;
+use packet::BufferMut;
+
use crate::ip::{IpAddr, IpProto};
use crate::transport::udp::UdpEventDispatcher;
-use crate::wire::BufferAndRange;
use crate::{Context, EventDispatcher};
/// The state associated with the transport layer.
@@ -103,8 +104,8 @@
/// `receive_ip_packet` receives a transport layer packet. If the given protocol
/// is supported, the packet is delivered to that protocol, and
/// `receive_ip_packet` returns `true`. Otherwise, it returns `false`.
-pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: AsMut<[u8]>>(
- ctx: &mut Context<D>, src_ip: A, dst_ip: A, proto: IpProto, buffer: BufferAndRange<B>,
+pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
+ ctx: &mut Context<D>, src_ip: A, dst_ip: A, proto: IpProto, buffer: B,
) -> bool {
match proto {
IpProto::Tcp => {
diff --git a/bin/recovery_netstack/core/src/transport/tcp/mod.rs b/bin/recovery_netstack/core/src/transport/tcp/mod.rs
index f47b599..793e07a 100644
--- a/bin/recovery_netstack/core/src/transport/tcp/mod.rs
+++ b/bin/recovery_netstack/core/src/transport/tcp/mod.rs
@@ -13,9 +13,10 @@
use std::collections::HashMap;
use std::num::NonZeroU16;
+use packet::BufferMut;
+
use crate::ip::{Ip, IpAddr, Ipv4, Ipv6};
-use crate::wire::tcp::TcpSegment;
-use crate::wire::BufferAndRange;
+use crate::wire::tcp::{TcpParseArgs, TcpSegment};
use crate::{Context, EventDispatcher};
use self::conn::Conn;
@@ -55,17 +56,18 @@
}
/// Receive a TCP segment in an IP packet.
-pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: AsMut<[u8]>>(
- ctx: &mut Context<D>, src_ip: A, dst_ip: A, mut buffer: BufferAndRange<B>,
+pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
+ ctx: &mut Context<D>, src_ip: A, dst_ip: A, mut buffer: B,
) {
println!("received tcp packet: {:x?}", buffer.as_mut());
- let (segment, body_range) =
- if let Ok((segment, body_range)) = TcpSegment::parse(buffer.as_mut(), src_ip, dst_ip) {
- (segment, body_range)
- } else {
- // TODO(joshlf): Do something with ICMP here?
- return;
- };
+ let segment = if let Ok(segment) =
+ buffer.parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(src_ip, dst_ip))
+ {
+ segment
+ } else {
+ // TODO(joshlf): Do something with ICMP here?
+ return;
+ };
if segment.syn() {
let _key = TwoTuple {
diff --git a/bin/recovery_netstack/core/src/transport/udp.rs b/bin/recovery_netstack/core/src/transport/udp.rs
index 4343d80..323c838 100644
--- a/bin/recovery_netstack/core/src/transport/udp.rs
+++ b/bin/recovery_netstack/core/src/transport/udp.rs
@@ -7,12 +7,12 @@
use std::hash::Hash;
use std::num::NonZeroU16;
+use packet::{BufferMut, BufferSerializer, Serializer};
use zerocopy::ByteSlice;
use crate::ip::{Ip, IpAddr, IpProto, Ipv4Addr, Ipv6Addr};
use crate::transport::{ConnAddrMap, ListenerAddrMap};
-use crate::wire::udp::{UdpPacket, UdpPacketSerializer};
-use crate::wire::{BufferAndRange, SerializationRequest};
+use crate::wire::udp::{UdpPacket, UdpPacketBuilder, UdpParseArgs};
use crate::{Context, EventDispatcher, StackState};
/// The state associated with the UDP protocol.
@@ -128,17 +128,18 @@
}
/// Receive a UDP packet in an IP packet.
-pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: AsMut<[u8]>>(
- ctx: &mut Context<D>, src_ip: A, dst_ip: A, mut buffer: BufferAndRange<B>,
+pub fn receive_ip_packet<D: EventDispatcher, A: IpAddr, B: BufferMut>(
+ ctx: &mut Context<D>, src_ip: A, dst_ip: A, mut buffer: B,
) {
println!("received udp packet: {:x?}", buffer.as_mut());
- let (packet, body_range) =
- if let Ok((packet, body_range)) = UdpPacket::parse(buffer.as_mut(), src_ip, dst_ip) {
- (packet, body_range)
- } else {
- // TODO(joshlf): Do something with ICMP here?
- return;
- };
+ let packet = if let Ok(packet) =
+ buffer.parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(src_ip, dst_ip))
+ {
+ packet
+ } else {
+ // TODO(joshlf): Do something with ICMP here?
+ return;
+ };
let (state, dispatcher) = ctx.state_and_dispatcher();
let state = get_inner_state(state);
@@ -167,8 +168,8 @@
/// # Panics
///
/// `send_udp_conn` panics if `conn` is not associated with a connection for this IP version.
-pub fn send_udp_conn<D: EventDispatcher, I: Ip>(
- ctx: &mut Context<D>, conn: &D::UdpConn, body: &[u8],
+pub fn send_udp_conn<D: EventDispatcher, I: Ip, B: BufferMut>(
+ ctx: &mut Context<D>, conn: &D::UdpConn, body: B,
) {
let state = get_inner_state::<_, I::Addr>(ctx.state());
let Conn {
@@ -187,7 +188,7 @@
local_addr,
remote_addr,
IpProto::Udp,
- body.encapsulate(UdpPacketSerializer::new(
+ BufferSerializer::new_vec(body).encapsulate(UdpPacketBuilder::new(
local_addr,
remote_addr,
Some(local_port),
@@ -207,9 +208,9 @@
///
/// `send_udp_listener` panics if `listener` is not associated with a listener
/// for this IP version.
-pub fn send_udp_listener<D: EventDispatcher, A: IpAddr>(
+pub fn send_udp_listener<D: EventDispatcher, A: IpAddr, B: BufferMut>(
ctx: &mut Context<D>, listener: &D::UdpListener, local_addr: A, remote_addr: A,
- remote_port: NonZeroU16, body: &[u8],
+ remote_port: NonZeroU16, body: B,
) {
if !crate::ip::is_local_addr(ctx, local_addr) {
// TODO(joshlf): Return error.
@@ -261,7 +262,7 @@
local_addr,
remote_addr,
IpProto::Udp,
- body.encapsulate(UdpPacketSerializer::new(
+ BufferSerializer::new_vec(body).encapsulate(UdpPacketBuilder::new(
local_addr,
remote_addr,
Some(local_port),
diff --git a/bin/recovery_netstack/core/src/wire/arp.rs b/bin/recovery_netstack/core/src/wire/arp.rs
index 660af61..5608ae4 100644
--- a/bin/recovery_netstack/core/src/wire/arp.rs
+++ b/bin/recovery_netstack/core/src/wire/arp.rs
@@ -11,13 +11,13 @@
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;
-use crate::wire::util::{BufferAndRange, InnerPacketSerializer};
// 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
@@ -128,7 +128,7 @@
Err(ParseError::NotSupported),
"unsupported network protocol: {}",
proto
- )
+ );
}
};
if header.hardware_address_len() != hlen || header.protocol_address_len() != plen {
@@ -231,34 +231,27 @@
body: LayoutVerified<B, Body<HwAddr, ProtoAddr>>,
}
-impl<B: ByteSlice, HwAddr, ProtoAddr> ArpPacket<B, 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,
{
- /// Parse an ARP packet.
- ///
- /// `parse` parses `bytes` as an ARP packet and validates the header fields.
- ///
- /// If `bytes` are a valid ARP packet, but do not match the hardware address
- /// and protocol address types `HwAddr` and `ProtoAddr`, `parse` will return
- /// `Err(ParseError::NotExpected)`. If multiple hardware or protocol address
- /// types are valid in a given context, `peek_arp_types` may be used to
- /// peek at the header and determine what types are present so that the
- /// correct types can then be used in a call to `parse`.
- ///
- /// The caller may provide more bytes than necessary. This allows the caller
- /// to call `parse` on a payload which was itself padded to meet a minimum
- /// length requirement (for example, for Ethernet frames). See the
- /// `DETAILS.md` file in the repository root for more details.
- pub fn parse(bytes: B) -> Result<ArpPacket<B, HwAddr, ProtoAddr>, ParseError> {
- let (header, body) =
- LayoutVerified::<B, Header>::new_unaligned_from_prefix(bytes).ok_or_else(
- debug_err_fn!(ParseError::Format, "too few bytes for header"),
- )?;
- let (body, _) =
- LayoutVerified::<B, Body<HwAddr, ProtoAddr>>::new_unaligned_from_prefix(body)
- .ok_or_else(debug_err_fn!(ParseError::Format, "too few bytes for body"))?;
+ 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
@@ -287,7 +280,13 @@
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
@@ -314,9 +313,9 @@
self.body.tpa
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer(&self) -> ArpPacketSerializer<HwAddr, ProtoAddr> {
- ArpPacketSerializer {
+ /// 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(),
@@ -326,8 +325,8 @@
}
}
-/// A serializer for ARP packets.
-pub struct ArpPacketSerializer<HwAddr, ProtoAddr> {
+/// A builder for ARP packets.
+pub struct ArpPacketBuilder<HwAddr, ProtoAddr> {
op: ArpOp,
sha: HwAddr,
spa: ProtoAddr,
@@ -335,13 +334,13 @@
tpa: ProtoAddr,
}
-impl<HwAddr, ProtoAddr> ArpPacketSerializer<HwAddr, ProtoAddr> {
- /// Construct a new `ArpPacketSerializer`.
+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,
- ) -> ArpPacketSerializer<HwAddr, ProtoAddr> {
- ArpPacketSerializer {
+ ) -> ArpPacketBuilder<HwAddr, ProtoAddr> {
+ ArpPacketBuilder {
op: operation,
sha: sender_hardware_addr,
spa: sender_protocol_addr,
@@ -351,28 +350,27 @@
}
}
-impl<HwAddr, ProtoAddr> InnerPacketSerializer for ArpPacketSerializer<HwAddr, ProtoAddr>
+impl<HwAddr, ProtoAddr> InnerPacketBuilder for ArpPacketBuilder<HwAddr, ProtoAddr>
where
HwAddr: Copy + HType + FromBytes + AsBytes + Unaligned,
ProtoAddr: Copy + PType + FromBytes + AsBytes + Unaligned,
{
- fn size(&self) -> usize {
+ fn bytes(&self) -> usize {
mem::size_of::<Header>() + mem::size_of::<Body<HwAddr, ProtoAddr>>()
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
- assert_eq!(buffer.range().len(), 0);
- buffer.extend_forwards(self.size());
+ fn serialize(self, mut buffer: &mut [u8]) {
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut buffer = &mut buffer;
- // SECURITY: Use _zeroed constructors to ensure we zero memory to
- // prevent leaking information from packets previously stored in
- // this buffer.
- let (mut header, rest) =
- LayoutVerified::<_, Header>::new_unaligned_from_prefix_zeroed(buffer.as_mut())
- .expect("not enough bytes for an ARP packet");
- let (mut body, _) =
- LayoutVerified::<_, Body<HwAddr, ProtoAddr>>::new_unaligned_from_prefix_zeroed(rest)
- .expect("not enough bytes for an ARP packet");
+ // 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())
@@ -393,10 +391,11 @@
#[cfg(test)]
mod tests {
+ use packet::{FnSerializer, ParseBuffer, Serializer};
+
use super::*;
use crate::ip::Ipv4Addr;
use crate::wire::ethernet::EthernetFrame;
- use crate::wire::util::{InnerSerializationRequest, SerializationRequest};
const TEST_SENDER_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const TEST_TARGET_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
@@ -405,21 +404,22 @@
#[test]
fn test_parse_serialize_full() {
+ crate::testutil::set_logger_for_test();
use crate::wire::testdata::*;
- let (frame, _) = EthernetFrame::parse(ARP_REQUEST).unwrap();
+ 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 arp = ArpPacket::<_, Mac, Ipv4Addr>::parse(frame.body()).unwrap();
+ 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()); // These will be the same
+ assert_eq!(frame.src_mac(), arp.sender_hardware_address());
- let frame_bytes = InnerSerializationRequest::new(arp.serializer())
- .encapsulate(frame.serializer())
- .serialize_outer();
+ let frame_bytes = arp.builder().encapsulate(frame.builder()).serialize_outer();
assert_eq!(frame_bytes.as_ref(), ARP_REQUEST);
}
@@ -459,14 +459,16 @@
#[test]
fn test_parse() {
- let mut bytes = [
+ 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 bytes[..8]).copy_from_slice(&header_to_bytes(new_header()));
- let (hw, proto) = peek_arp_types(&bytes[..]).unwrap();
+ ][..];
+ (&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 packet = ArpPacket::<_, Mac, Ipv4Addr>::parse(&bytes[..]).unwrap();
+
+ 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);
@@ -476,27 +478,22 @@
#[test]
fn test_serialize() {
- let mut buf = [0; 28];
- {
- InnerPacketSerializer::serialize(
- ArpPacketSerializer::new(
- ArpOp::Request,
- TEST_SENDER_MAC,
- TEST_SENDER_IPV4,
- TEST_TARGET_MAC,
- TEST_TARGET_IPV4,
- ),
- &mut BufferAndRange::new_from(&mut buf[..], ..0),
- );
- }
+ 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!(
- buf,
- [
+ 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 = ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..28]).unwrap();
+ 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);
@@ -545,59 +542,50 @@
#[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_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::Format
- );
+ 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);
- (&mut buf[..8]).copy_from_slice(&header_to_bytes(header)[..]);
- assert_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::NotExpected
- );
+ 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);
- (&mut buf[..8]).copy_from_slice(&header_to_bytes(header)[..]);
- assert_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::NotExpected
- );
+ assert_header_err(header, ParseError::NotExpected);
// Test that an incorrect hardware address len is rejected.
let mut header = new_header();
header.hlen = 7;
- (&mut buf[..8]).copy_from_slice(&header_to_bytes(header)[..]);
- assert_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(header, ParseError::Format);
// Test that an incorrect protocol address len is rejected.
let mut header = new_header();
header.plen = 5;
- (&mut buf[..8]).copy_from_slice(&header_to_bytes(header)[..]);
- assert_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::Format
- );
+ 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);
- (&mut buf[..8]).copy_from_slice(&header_to_bytes(header)[..]);
- assert_eq!(
- ArpPacket::<_, Mac, Ipv4Addr>::parse(&buf[..]).unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(header, ParseError::Format);
}
#[test]
@@ -605,26 +593,26 @@
// Test that ArpPacket::serialize properly zeroes memory before
// serializing the packet.
let mut buf_0 = [0; 28];
- InnerPacketSerializer::serialize(
- ArpPacketSerializer::new(
+ InnerPacketBuilder::serialize(
+ ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
- &mut BufferAndRange::new_from(&mut buf_0[..], ..0),
+ &mut buf_0[..],
);
let mut buf_1 = [0xFF; 28];
- InnerPacketSerializer::serialize(
- ArpPacketSerializer::new(
+ InnerPacketBuilder::serialize(
+ ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
- &mut BufferAndRange::new_from(&mut buf_1[..], ..0),
+ &mut buf_1[..],
);
assert_eq!(buf_0, buf_1);
}
@@ -634,15 +622,15 @@
fn test_serialize_panic_insufficient_packet_space() {
// Test that a buffer which doesn't leave enough room for the packet is
// rejected.
- InnerPacketSerializer::serialize(
- ArpPacketSerializer::new(
+ InnerPacketBuilder::serialize(
+ ArpPacketBuilder::new(
ArpOp::Request,
TEST_SENDER_MAC,
TEST_SENDER_IPV4,
TEST_TARGET_MAC,
TEST_TARGET_IPV4,
),
- &mut BufferAndRange::new_from(&mut [0; 27], ..0),
+ &mut [0; 27],
);
}
}
diff --git a/bin/recovery_netstack/core/src/wire/ethernet.rs b/bin/recovery_netstack/core/src/wire/ethernet.rs
index babb7bb..ef3b977 100644
--- a/bin/recovery_netstack/core/src/wire/ethernet.rs
+++ b/bin/recovery_netstack/core/src/wire/ethernet.rs
@@ -4,14 +4,14 @@
//! Parsing and serialization of Ethernet frames.
-use std::ops::Range;
-
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{
+ BufferView, BufferViewMut, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer,
+};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use crate::device::ethernet::{EtherType, Mac};
use crate::error::ParseError;
-use crate::wire::util::{BufferAndRange, PacketSerializer};
// HeaderPrefix has the same memory layout (thanks to repr(C, packed)) as an
// Ethernet header prefix. Thus, we can simply reinterpret the bytes of the
@@ -57,22 +57,26 @@
body: B,
}
-impl<B: ByteSlice> EthernetFrame<B> {
- /// Parse an Ethernet frame.
- ///
- /// `parse` parses `bytes` as an Ethernet frame. It is assumed that the
- /// Frame Check Sequence (FCS) footer has already been removed. 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.
- pub fn parse(bytes: B) -> Result<(EthernetFrame<B>, Range<usize>), ParseError> {
+impl<B: ByteSlice> ParsablePacket<B, ()> for EthernetFrame<B> {
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ let header_len = self.hdr_prefix.bytes().len()
+ + self.tag.as_ref().map(|tag| tag.bytes().len()).unwrap_or(0)
+ + self.ethertype.bytes().len();
+ ParseMetadata::from_packet(header_len, self.body.len(), 0)
+ }
+
+ fn parse<BV: BufferView<B>>(mut buffer: BV, args: ()) -> Result<Self, ParseError> {
// See for details: https://en.wikipedia.org/wiki/Ethernet_frame#Frame_%E2%80%93_data_link_layer
- let (hdr_prefix, rest) =
- LayoutVerified::<B, HeaderPrefix>::new_unaligned_from_prefix(bytes).ok_or_else(
- debug_err_fn!(ParseError::Format, "too few bytes for header"),
- )?;
- if rest.len() < 48 {
+ let hdr_prefix = buffer
+ .take_obj_front::<HeaderPrefix>()
+ .ok_or_else(debug_err_fn!(
+ ParseError::Format,
+ "too few bytes for header"
+ ))?;
+ if buffer.len() < 48 {
// The minimum frame size (not including the Frame Check Sequence
// (FCS) footer, which we do not handle in this code) is 60 bytes.
// We've already consumed 12 bytes for the header prefix, so we must
@@ -86,20 +90,18 @@
// Identifier (TPID). A TPID of TPID_8021Q implies an 802.1Q tag, a TPID
// of TPID_8021AD implies an 802.1ad tag, and anything else implies that
// there is no tag - it's a normal ethertype field.
- let ethertype_or_tpid = NetworkEndian::read_u16(&rest);
+ let ethertype_or_tpid = NetworkEndian::read_u16(buffer.as_ref());
let (tag, ethertype, body) = match ethertype_or_tpid {
- self::TPID_8021Q | self::TPID_8021AD => {
- let (tag, rest) =
- LayoutVerified::<B, [u8; 4]>::new_unaligned_from_prefix(rest).unwrap();
- let (ethertype, body) =
- LayoutVerified::<B, [u8; 2]>::new_unaligned_from_prefix(rest).unwrap();
- (Some(tag), ethertype, body)
- }
- _ => {
- let (ethertype, body) =
- LayoutVerified::<B, [u8; 2]>::new_unaligned_from_prefix(rest).unwrap();
- (None, ethertype, body)
- }
+ self::TPID_8021Q | self::TPID_8021AD => (
+ Some(buffer.take_obj_front::<[u8; 4]>().unwrap()),
+ buffer.take_obj_front::<[u8; 2]>().unwrap(),
+ buffer.into_rest(),
+ ),
+ _ => (
+ None,
+ buffer.take_obj_front::<[u8; 2]>().unwrap(),
+ buffer.into_rest(),
+ ),
};
let frame = EthernetFrame {
@@ -119,14 +121,11 @@
et
);
}
-
- let hdr_len = frame.hdr_prefix.bytes().len()
- + frame.tag.as_ref().map(|tag| tag.bytes().len()).unwrap_or(0)
- + frame.ethertype.bytes().len();
- let total_len = hdr_len + frame.body.len();
- Ok((frame, hdr_len..total_len))
+ Ok(frame)
}
+}
+impl<B: ByteSlice> EthernetFrame<B> {
/// The frame body.
pub fn body(&self) -> &[u8] {
&self.body
@@ -174,9 +173,9 @@
self.header_len() + self.body.len()
}
- /// Construct a serializer with the same contents as this frame.
- pub fn serializer(&self) -> EthernetFrameSerializer {
- EthernetFrameSerializer {
+ /// Construct a builder with the same contents as this frame.
+ pub fn builder(&self) -> EthernetFrameBuilder {
+ EthernetFrameBuilder {
src_mac: self.src_mac(),
dst_mac: self.dst_mac(),
ethertype: NetworkEndian::read_u16(&self.ethertype[..]),
@@ -184,17 +183,17 @@
}
}
-/// A serializer for Ethernet frames.
-pub struct EthernetFrameSerializer {
+/// A builder for Ethernet frames.
+pub struct EthernetFrameBuilder {
src_mac: Mac,
dst_mac: Mac,
ethertype: u16,
}
-impl EthernetFrameSerializer {
- /// Construct a new `EthernetFrameSerializer`.
- pub fn new(src_mac: Mac, dst_mac: Mac, ethertype: EtherType) -> EthernetFrameSerializer {
- EthernetFrameSerializer {
+impl EthernetFrameBuilder {
+ /// Construct a new `EthernetFrameBuilder`.
+ pub fn new(src_mac: Mac, dst_mac: Mac, ethertype: EtherType) -> EthernetFrameBuilder {
+ EthernetFrameBuilder {
src_mac,
dst_mac,
ethertype: ethertype as u16,
@@ -211,116 +210,130 @@
const MIN_HEADER_BYTES: usize = 14;
const MIN_BODY_BYTES: usize = 46;
-impl PacketSerializer for EthernetFrameSerializer {
- fn max_header_bytes(&self) -> usize {
- MAX_HEADER_BYTES
- }
-
- fn min_header_bytes(&self) -> usize {
+impl PacketBuilder for EthernetFrameBuilder {
+ fn header_len(&self) -> usize {
MIN_HEADER_BYTES
}
- fn min_body_and_padding_bytes(&self) -> usize {
+ fn min_body_len(&self) -> usize {
MIN_BODY_BYTES
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
+ fn footer_len(&self) -> usize {
+ 0
+ }
+
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
// NOTE: EtherType values of 1500 and below are used to indicate the
// length of the body in bytes. We don't need to validate this because
// the EtherType enum has no variants with values in that range.
- let extend_backwards = {
- let (header, body, _) = buffer.parts_mut();
- let mut frame = {
- // SECURITY: Use _zeroed constructors to ensure we zero memory
- // to prevent leaking information from packets previously stored
- // in this buffer.
- let (prefix, ethertype) =
- LayoutVerified::<_, [u8; 2]>::new_unaligned_from_suffix_zeroed(header)
- .expect("too few bytes for Ethernet header");
- let (_, hdr_prefix) =
- LayoutVerified::<_, HeaderPrefix>::new_unaligned_from_suffix_zeroed(prefix)
- .expect("too few bytes for Ethernet header");
- EthernetFrame {
- hdr_prefix,
- tag: None,
- ethertype,
- body,
- }
- };
+ let (mut header, mut body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut header = &mut header;
- let total_len = frame.total_frame_len();
- if total_len < 60 {
- panic!(
- "total frame size of {} bytes is below minimum frame size of 60",
- total_len
- );
+ let mut frame = {
+ // SECURITY: Use _zero constructors to ensure we zero memory to
+ // prevent leaking information from packets previously stored in
+ // this buffer.
+ let hdr_prefix = header
+ .take_obj_front_zero::<HeaderPrefix>()
+ .expect("too few bytes for Ethernet header");
+ let ethertype = header
+ .take_obj_front_zero::<[u8; 2]>()
+ .expect("too few bytes for Ethernet header");
+ EthernetFrame {
+ hdr_prefix,
+ tag: None,
+ ethertype,
+ body,
}
-
- frame.hdr_prefix.src_mac = self.src_mac.bytes();
- frame.hdr_prefix.dst_mac = self.dst_mac.bytes();
- NetworkEndian::write_u16(&mut frame.ethertype[..], self.ethertype);
-
- frame.header_len()
};
- buffer.extend_backwards(extend_backwards);
+ let total_len = frame.total_frame_len();
+ if total_len < 60 {
+ panic!(
+ "total frame size of {} bytes is below minimum frame size of 60",
+ total_len
+ );
+ }
+
+ frame.hdr_prefix.src_mac = self.src_mac.bytes();
+ frame.hdr_prefix.dst_mac = self.dst_mac.bytes();
+ NetworkEndian::write_u16(&mut frame.ethertype[..], self.ethertype);
}
}
#[cfg(test)]
mod tests {
+ use packet::{Buf, BufferSerializer, ParseBuffer, SerializeBuffer, Serializer};
+
use super::*;
const DEFAULT_DST_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
const DEFAULT_SRC_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
- // Return a test buffer with values 0..60 except for the EtherType field,
- // which is EtherType::Arp.
- fn new_buf() -> [u8; 60] {
- let mut buf = [0u8; 60];
+ // Return a buffer for testing parsing with values 0..60 except for the
+ // EtherType field, which is EtherType::Arp. Also return the contents
+ // of the body.
+ fn new_parse_buf() -> ([u8; 60], [u8; 46]) {
+ let mut buf = [0; 60];
for i in 0..60 {
buf[i] = i as u8;
}
NetworkEndian::write_u16(&mut buf[12..14], EtherType::Arp as u16);
+ let mut body = [0; 46];
+ (&mut body).copy_from_slice(&buf[14..]);
+ (buf, body)
+ }
+
+ // Return a test buffer with values 0..46 to be used as a test payload for
+ // serialization.
+ fn new_serialize_buf() -> [u8; 46] {
+ let mut buf = [0; 46];
+ for i in 0..46 {
+ buf[i] = i as u8;
+ }
buf
}
#[test]
fn test_parse() {
- let buf = new_buf();
- let (frame, body_range) = EthernetFrame::parse(&buf[..]).unwrap();
- assert_eq!(body_range, 14..60);
+ let (mut buf, body) = new_parse_buf();
+ let mut buf = &mut buf[..];
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
assert_eq!(frame.hdr_prefix.dst_mac, DEFAULT_DST_MAC.bytes());
assert_eq!(frame.hdr_prefix.src_mac, DEFAULT_SRC_MAC.bytes());
assert!(frame.tag.is_none());
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Arp)));
- assert_eq!(frame.body, &buf[body_range]);
+ assert_eq!(frame.body(), &body[..]);
// For both of the TPIDs that imply the existence of a tag, make sure
// that the tag is present and correct (and that all of the normal
// checks succeed).
for tpid in [TPID_8021Q, TPID_8021AD].iter() {
- let mut buf = new_buf();
+ let (mut buf, body) = new_parse_buf();
+ let mut buf = &mut buf[..];
const TPID_OFFSET: usize = 12;
NetworkEndian::write_u16(&mut buf[TPID_OFFSET..], *tpid);
// write a valid EtherType
NetworkEndian::write_u16(&mut buf[TPID_OFFSET + 4..], EtherType::Arp as u16);
- let (frame, body_range) = EthernetFrame::parse(&buf[..]).unwrap();
- assert_eq!(body_range, 18..60);
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
assert_eq!(frame.hdr_prefix.dst_mac, DEFAULT_DST_MAC.bytes());
assert_eq!(frame.hdr_prefix.src_mac, DEFAULT_SRC_MAC.bytes());
assert_eq!(frame.ethertype(), Some(Ok(EtherType::Arp)));
// help out with type inference
- let tag: &[u8; 4] = &frame.tag.unwrap();
+ let tag: &[u8; 4] = frame.tag.as_ref().unwrap();
let got_tag = NetworkEndian::read_u32(tag);
let want_tag =
(*tpid as u32) << 16 | ((TPID_OFFSET as u32 + 2) << 8) | (TPID_OFFSET as u32 + 3);
assert_eq!(got_tag, want_tag);
- assert_eq!(frame.body, &buf[body_range]);
+ // Offset by 4 since new_parse_buf returns a body on the assumption
+ // that there's no tag.
+ assert_eq!(frame.body(), &body[4..]);
}
}
@@ -330,32 +343,41 @@
let mut buf = [0u8; 1014];
// an incorrect length results in error
NetworkEndian::write_u16(&mut buf[12..], 1001);
- assert!(EthernetFrame::parse(&buf[..]).is_err());
+ assert!((&mut buf[..]).parse::<EthernetFrame<_>>().is_err());
// a correct length results in success
NetworkEndian::write_u16(&mut buf[12..], 1000);
- let (frame, _) = EthernetFrame::parse(&buf[..]).unwrap();
- // there's no EtherType available
- assert_eq!(frame.ethertype(), None);
+ assert_eq!(
+ (&mut buf[..])
+ .parse::<EthernetFrame<_>>()
+ .unwrap()
+ .ethertype(),
+ None
+ );
// an unrecognized EtherType is returned numerically
let mut buf = [0u8; 1014];
NetworkEndian::write_u16(&mut buf[12..], 1536);
- let (frame, _) = EthernetFrame::parse(&buf[..]).unwrap();
- assert_eq!(frame.ethertype(), Some(Err(1536)));
+ assert_eq!(
+ (&mut buf[..])
+ .parse::<EthernetFrame<_>>()
+ .unwrap()
+ .ethertype(),
+ Some(Err(1536))
+ );
}
#[test]
fn test_serialize() {
- let mut buf = new_buf();
- {
- let mut buffer = BufferAndRange::new_from(&mut buf[..], (MAX_HEADER_BYTES - 4)..);
- EthernetFrameSerializer::new(DEFAULT_DST_MAC, DEFAULT_SRC_MAC, EtherType::Arp)
- .serialize(&mut buffer);
- assert_eq!(buffer.range(), 0..60);
- }
+ let buf = (&new_serialize_buf()[..])
+ .encapsulate(EthernetFrameBuilder::new(
+ DEFAULT_DST_MAC,
+ DEFAULT_SRC_MAC,
+ EtherType::Arp,
+ ))
+ .serialize_outer();
assert_eq!(
- &buf[..MAX_HEADER_BYTES - 4],
+ &buf.as_ref()[..MAX_HEADER_BYTES - 4],
[6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 0x08, 0x06]
);
}
@@ -365,35 +387,46 @@
// Test that EthernetFrame::serialize properly zeroes memory before
// serializing the header.
let mut buf_0 = [0; 60];
- EthernetFrameSerializer::new(DEFAULT_SRC_MAC, DEFAULT_DST_MAC, EtherType::Arp)
- .serialize(&mut BufferAndRange::new_from(&mut buf_0[..], 14..));
+
+ BufferSerializer::new_vec(Buf::new(&mut buf_0[..], 14..))
+ .encapsulate(EthernetFrameBuilder::new(
+ DEFAULT_SRC_MAC,
+ DEFAULT_DST_MAC,
+ EtherType::Arp,
+ ))
+ .serialize_outer();
let mut buf_1 = [0; 60];
(&mut buf_1[..14]).copy_from_slice(&[0xFF; 14]);
- EthernetFrameSerializer::new(DEFAULT_SRC_MAC, DEFAULT_DST_MAC, EtherType::Arp)
- .serialize(&mut BufferAndRange::new_from(&mut buf_1[..], 14..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_1[..], 14..))
+ .encapsulate(EthernetFrameBuilder::new(
+ DEFAULT_SRC_MAC,
+ DEFAULT_DST_MAC,
+ EtherType::Arp,
+ ))
+ .serialize_outer();
assert_eq!(&buf_0[..], &buf_1[..]);
}
#[test]
fn test_parse_error() {
// 1 byte shorter than the minimum
- let buf = [0u8; 59];
- assert!(EthernetFrame::parse(&buf[..]).is_err());
+ let mut buf = [0u8; 59];
+ assert!((&mut buf[..]).parse::<EthernetFrame<_>>().is_err());
// an ethertype of 1500 should be validated as the length of the body
let mut buf = [0u8; 60];
NetworkEndian::write_u16(&mut buf[12..], 1500);
- assert!(EthernetFrame::parse(&buf[..]).is_err());
+ assert!((&mut buf[..]).parse::<EthernetFrame<_>>().is_err());
// an ethertype of 1501 is illegal because it's in the range [1501, 1535]
let mut buf = [0u8; 60];
NetworkEndian::write_u16(&mut buf[12..], 1501);
- assert!(EthernetFrame::parse(&buf[..]).is_err());
+ assert!((&mut buf[..]).parse::<EthernetFrame<_>>().is_err());
// an ethertype of 1535 is illegal
let mut buf = [0u8; 60];
NetworkEndian::write_u16(&mut buf[12..], 1535);
- assert!(EthernetFrame::parse(&buf[..]).is_err());
+ assert!((&mut buf[..]).parse::<EthernetFrame<_>>().is_err());
}
#[test]
@@ -401,14 +434,12 @@
fn test_serialize_panic() {
// create with a body which is below the minimum length
let mut buf = [0u8; 60];
- EthernetFrameSerializer::new(
+ let buffer = SerializeBuffer::new(&mut buf[..], (60 - (MIN_BODY_BYTES - 1))..);
+ EthernetFrameBuilder::new(
Mac::new([0, 1, 2, 3, 4, 5]),
Mac::new([6, 7, 8, 9, 10, 11]),
EtherType::Arp,
)
- .serialize(&mut BufferAndRange::new_from(
- &mut buf[..],
- (60 - (MIN_BODY_BYTES - 1))..,
- ));
+ .serialize(buffer);
}
}
diff --git a/bin/recovery_netstack/core/src/wire/icmp/common.rs b/bin/recovery_netstack/core/src/wire/icmp/common.rs
index 16fd0d4..d8336b1 100644
--- a/bin/recovery_netstack/core/src/wire/icmp/common.rs
+++ b/bin/recovery_netstack/core/src/wire/icmp/common.rs
@@ -47,7 +47,7 @@
impl_from_bytes_as_bytes_unaligned!(IcmpEchoRequest);
impl_from_bytes_as_bytes_unaligned!(IcmpEchoReply);
-/// An ICMPv4 Time Exceeded message.
+/// An ICMP Time Exceeded message.
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct IcmpTimeExceeded {
diff --git a/bin/recovery_netstack/core/src/wire/icmp/icmpv4.rs b/bin/recovery_netstack/core/src/wire/icmp/icmpv4.rs
index b1f1c7b..0c18da1 100644
--- a/bin/recovery_netstack/core/src/wire/icmp/icmpv4.rs
+++ b/bin/recovery_netstack/core/src/wire/icmp/icmpv4.rs
@@ -2,19 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-//! ICMP v4
+//! ICMPv4
use std::fmt;
-use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{BufferView, ParsablePacket, ParseMetadata};
use zerocopy::ByteSlice;
use crate::error::ParseError;
use crate::ip::{Ipv4, Ipv4Addr};
use super::common::{IcmpDestUnreachable, IcmpEchoReply, IcmpEchoRequest, IcmpTimeExceeded};
-use super::{peek_message_type, IcmpIpExt, IcmpPacket, IcmpUnusedCode, IdAndSeq, OriginalPacket};
+use super::{
+ peek_message_type, IcmpIpExt, IcmpPacket, IcmpParseArgs, IcmpUnusedCode, IdAndSeq,
+ OriginalPacket,
+};
/// An ICMPv4 packet with a dynamic message type.
///
@@ -24,7 +27,7 @@
/// knowing the message type ahead of time while still getting the benefits of a
/// statically-typed packet struct after parsing is complete.
#[allow(missing_docs)]
-pub enum Packet<B> {
+pub enum Icmpv4Packet<B> {
EchoReply(IcmpPacket<Ipv4, B, IcmpEchoReply>),
DestUnreachable(IcmpPacket<Ipv4, B, IcmpDestUnreachable>),
Redirect(IcmpPacket<Ipv4, B, Icmpv4Redirect>),
@@ -35,9 +38,9 @@
TimestampReply(IcmpPacket<Ipv4, B, Icmpv4TimestampReply>),
}
-impl<B: ByteSlice + fmt::Debug> fmt::Debug for Packet<B> {
+impl<B: ByteSlice + fmt::Debug> fmt::Debug for Icmpv4Packet<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use self::Packet::*;
+ use self::Icmpv4Packet::*;
match self {
DestUnreachable(ref p) => f.debug_tuple("DestUnreachable").field(p).finish(),
EchoReply(ref p) => f.debug_tuple("EchoReply").field(p).finish(),
@@ -51,44 +54,40 @@
}
}
-create_net_enum! {
- MessageType,
- EchoReply: ECHO_REPLY = 0,
- DestUnreachable: DEST_UNREACHABLE = 3,
- Redirect: REDIRECT = 5,
- EchoRequest: ECHO_REQUEST = 8,
- TimeExceeded: TIME_EXCEEDED = 11,
- ParameterProblem: PARAMETER_PROBLEM = 12,
- TimestampRequest: TIMESTAMP_REQUEST = 13,
- TimestampReply: TIMESTAMP_REPLY = 14,
-}
+impl<B: ByteSlice> ParsablePacket<B, IcmpParseArgs<Ipv4Addr>> for Icmpv4Packet<B> {
+ type Error = ParseError;
-impl<B: ByteSlice> Packet<B> {
- /// Parse an ICMP packet.
- ///
- /// `parse` parses `bytes` as an ICMP packet and validates the header fields
- /// and checksum. It returns the byte range corresponding to the message
- /// body within `bytes`. This can be useful when extracting the encapsulated
- /// body to send to another layer of the stack. If the message type has no
- /// body, then the range is meaningless and should be ignored.
- pub fn parse(
- bytes: B, src_ip: Ipv4Addr, dst_ip: Ipv4Addr,
- ) -> Result<(Packet<B>, Range<usize>), ParseError> {
+ fn parse_metadata(&self) -> ParseMetadata {
+ use self::Icmpv4Packet::*;
+ match self {
+ EchoReply(p) => p.parse_metadata(),
+ DestUnreachable(p) => p.parse_metadata(),
+ Redirect(p) => p.parse_metadata(),
+ EchoRequest(p) => p.parse_metadata(),
+ TimeExceeded(p) => p.parse_metadata(),
+ ParameterProblem(p) => p.parse_metadata(),
+ TimestampRequest(p) => p.parse_metadata(),
+ TimestampReply(p) => p.parse_metadata(),
+ }
+ }
+
+ fn parse<BV: BufferView<B>>(
+ mut buffer: BV, args: IcmpParseArgs<Ipv4Addr>,
+ ) -> Result<Self, ParseError> {
macro_rules! mtch {
- ($bytes:expr, $src_ip:expr, $dst_ip:expr, $($variant:ident => $type:ty,)*) => {
- match peek_message_type(&$bytes)? {
+ ($buffer:expr, $args:expr, $($variant:ident => $type:ty,)*) => {
+ match peek_message_type($buffer.as_ref())? {
$(MessageType::$variant => {
- let (packet, range) = IcmpPacket::<Ipv4, B, $type>::parse($bytes, $src_ip, $dst_ip)?;
- (Packet::$variant(packet), range)
+ let packet = <IcmpPacket<Ipv4, B, $type> as ParsablePacket<_, _>>::parse($buffer, $args)?;
+ Icmpv4Packet::$variant(packet)
})*
}
}
}
Ok(mtch!(
- bytes,
- src_ip,
- dst_ip,
+ buffer,
+ args,
EchoReply => IcmpEchoReply,
DestUnreachable => IcmpDestUnreachable,
Redirect => Icmpv4Redirect,
@@ -102,6 +101,18 @@
}
create_net_enum! {
+ MessageType,
+ EchoReply: ECHO_REPLY = 0,
+ DestUnreachable: DEST_UNREACHABLE = 3,
+ Redirect: REDIRECT = 5,
+ EchoRequest: ECHO_REQUEST = 8,
+ TimeExceeded: TIME_EXCEEDED = 11,
+ ParameterProblem: PARAMETER_PROBLEM = 12,
+ TimestampRequest: TIMESTAMP_REQUEST = 13,
+ TimestampReply: TIMESTAMP_REPLY = 14,
+}
+
+create_net_enum! {
Icmpv4DestUnreachableCode,
DestNetworkUnreachable: DEST_NETWORK_UNREACHABLE = 0,
DestHostUnreachable: DEST_HOST_UNREACHABLE = 1,
@@ -274,148 +285,119 @@
);
#[cfg(test)]
-mod test {
+mod tests {
+ use packet::{ParseBuffer, Serializer};
+
use super::*;
use crate::wire::icmp::{IcmpMessage, MessageBody};
- use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketSerializer};
- use crate::wire::util::{BufferAndRange, PacketSerializer, SerializationRequest};
+ use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketBuilder};
fn serialize_to_bytes<B: ByteSlice, M: IcmpMessage<Ipv4, B>>(
src_ip: Ipv4Addr, dst_ip: Ipv4Addr, icmp: &IcmpPacket<Ipv4, B, M>,
- serializer: Ipv4PacketSerializer,
+ builder: Ipv4PacketBuilder,
) -> Vec<u8> {
- let icmp_serializer = icmp.serializer(src_ip, dst_ip);
- let mut data = vec![0; icmp_serializer.max_header_bytes() + icmp.message_body.len()];
- let body_offset = data.len() - icmp.message_body.len();
- (&mut data[body_offset..]).copy_from_slice(icmp.message_body.bytes());
- BufferAndRange::new_from(&mut data[..], body_offset..)
- .encapsulate(icmp_serializer)
- .encapsulate(serializer)
+ icmp.message_body
+ .bytes()
+ .encapsulate(icmp.builder(src_ip, dst_ip))
+ .encapsulate(builder)
.serialize_outer()
.as_ref()
.to_vec()
}
+ fn test_parse_and_serialize<
+ M: for<'a> IcmpMessage<Ipv4, &'a [u8]>,
+ F: for<'a> FnOnce(&IcmpPacket<Ipv4, &'a [u8], M>),
+ >(
+ mut req: &[u8], check: F,
+ ) {
+ let orig_req = &req[..];
+
+ let ip = req.parse::<Ipv4Packet<_>>().unwrap();
+ let mut body = ip.body();
+ let icmp = body
+ .parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(ip.src_ip(), ip.dst_ip()))
+ .unwrap();
+ check(&icmp);
+
+ let data = serialize_to_bytes(ip.src_ip(), ip.dst_ip(), &icmp, ip.builder());
+ assert_eq!(&data[..], orig_req);
+ }
+
#[test]
fn test_parse_and_serialize_echo_request() {
use crate::wire::testdata::icmp_echo::*;
- let (ip, _) = Ipv4Packet::parse(REQUEST_IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, IcmpEchoRequest>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.original_packet().bytes(), ECHO_DATA);
- assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
- assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], REQUEST_IP_PACKET_BYTES);
+ test_parse_and_serialize::<IcmpEchoRequest, _>(REQUEST_IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.message_body.bytes(), ECHO_DATA);
+ assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
+ assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
+ });
}
#[test]
fn test_parse_and_serialize_echo_response() {
use crate::wire::testdata::icmp_echo::*;
- let (ip, _) = Ipv4Packet::parse(RESPONSE_IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, IcmpEchoReply>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.original_packet().bytes(), ECHO_DATA);
- assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
- assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], RESPONSE_IP_PACKET_BYTES);
+ test_parse_and_serialize::<IcmpEchoReply, _>(RESPONSE_IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.message_body.bytes(), ECHO_DATA);
+ assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
+ assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
+ });
}
#[test]
fn test_parse_and_serialize_timestamp_request() {
use crate::wire::testdata::icmp_timestamp::*;
- let (ip, _) = Ipv4Packet::parse(REQUEST_IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, Icmpv4TimestampRequest>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(
- icmp.message().0.timestamps.origin_timestamp(),
- ORIGIN_TIMESTAMP
- );
- assert_eq!(
- icmp.message().0.timestamps.recv_timestamp(),
- RX_TX_TIMESTAMP
- );
- assert_eq!(icmp.message().0.timestamps.tx_timestamp(), RX_TX_TIMESTAMP);
- assert_eq!(icmp.message().0.id_seq.id(), IDENTIFIER);
- assert_eq!(icmp.message().0.id_seq.seq(), SEQUENCE_NUM);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], REQUEST_IP_PACKET_BYTES);
+ test_parse_and_serialize::<Icmpv4TimestampRequest, _>(REQUEST_IP_PACKET_BYTES, |icmp| {
+ assert_eq!(
+ icmp.message().0.timestamps.origin_timestamp(),
+ ORIGIN_TIMESTAMP
+ );
+ assert_eq!(icmp.message().0.timestamps.tx_timestamp(), RX_TX_TIMESTAMP);
+ assert_eq!(icmp.message().0.id_seq.id(), IDENTIFIER);
+ assert_eq!(icmp.message().0.id_seq.seq(), SEQUENCE_NUM);
+ });
}
#[test]
fn test_parse_and_serialize_timestamp_reply() {
use crate::wire::testdata::icmp_timestamp::*;
- let (ip, _) = Ipv4Packet::parse(RESPONSE_IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, Icmpv4TimestampReply>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(
- icmp.message().0.timestamps.origin_timestamp(),
- ORIGIN_TIMESTAMP
- );
- // TODO: Assert other values here?
- // TODO: Check value of recv_timestamp and tx_timestamp
- assert_eq!(icmp.message().0.id_seq.id(), IDENTIFIER);
- assert_eq!(icmp.message().0.id_seq.seq(), SEQUENCE_NUM);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], RESPONSE_IP_PACKET_BYTES);
+ test_parse_and_serialize::<Icmpv4TimestampReply, _>(RESPONSE_IP_PACKET_BYTES, |icmp| {
+ assert_eq!(
+ icmp.message().0.timestamps.origin_timestamp(),
+ ORIGIN_TIMESTAMP
+ );
+ // TODO: Assert other values here?
+ // TODO: Check value of recv_timestamp and tx_timestamp
+ assert_eq!(icmp.message().0.id_seq.id(), IDENTIFIER);
+ assert_eq!(icmp.message().0.id_seq.seq(), SEQUENCE_NUM);
+ });
}
#[test]
fn test_parse_and_serialize_dest_unreachable() {
use crate::wire::testdata::icmp_dest_unreachable::*;
- let (ip, _) = Ipv4Packet::parse(IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<Ipv4, _, IcmpDestUnreachable>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.code(), Icmpv4DestUnreachableCode::DestHostUnreachable);
- assert_eq!(icmp.original_packet_body(), ORIGIN_DATA);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], IP_PACKET_BYTES);
+ test_parse_and_serialize::<IcmpDestUnreachable, _>(IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.code(), Icmpv4DestUnreachableCode::DestHostUnreachable);
+ assert_eq!(icmp.original_packet_body(), ORIGIN_DATA);
+ });
}
#[test]
fn test_parse_and_serialize_redirect() {
use crate::wire::testdata::icmp_redirect::*;
- let (ip, _) = Ipv4Packet::parse(IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, Icmpv4Redirect>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.code(), Icmpv4RedirectCode::RedirectForHost);
- assert_eq!(icmp.message().gateway, GATEWAY_ADDR);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], IP_PACKET_BYTES);
+ test_parse_and_serialize::<Icmpv4Redirect, _>(IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.code(), Icmpv4RedirectCode::RedirectForHost);
+ assert_eq!(icmp.message().gateway, GATEWAY_ADDR);
+ });
}
#[test]
fn test_parse_and_serialize_time_exceeded() {
use crate::wire::testdata::icmp_time_exceeded::*;
- let (ip, _) = Ipv4Packet::parse(IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, ttl) = (ip.src_ip(), ip.dst_ip(), ip.ttl());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, IcmpTimeExceeded>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.code(), Icmpv4TimeExceededCode::TTLExpired);
- assert_eq!(icmp.original_packet_body(), ORIGIN_DATA);
-
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], IP_PACKET_BYTES);
+ test_parse_and_serialize::<IcmpTimeExceeded, _>(IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.code(), Icmpv4TimeExceededCode::TTLExpired);
+ assert_eq!(icmp.original_packet_body(), ORIGIN_DATA);
+ });
}
}
diff --git a/bin/recovery_netstack/core/src/wire/icmp/icmpv6.rs b/bin/recovery_netstack/core/src/wire/icmp/icmpv6.rs
index 6902bf8..0705f6e 100644
--- a/bin/recovery_netstack/core/src/wire/icmp/icmpv6.rs
+++ b/bin/recovery_netstack/core/src/wire/icmp/icmpv6.rs
@@ -5,15 +5,17 @@
//! ICMPv6
use std::fmt;
-use std::ops::Range;
+use packet::{BufferView, ParsablePacket, ParseMetadata};
use zerocopy::ByteSlice;
use crate::error::ParseError;
use crate::ip::{Ipv6, Ipv6Addr};
use super::common::{IcmpDestUnreachable, IcmpEchoReply, IcmpEchoRequest, IcmpTimeExceeded};
-use super::{ndp, peek_message_type, IcmpIpExt, IcmpPacket, IcmpUnusedCode, OriginalPacket};
+use super::{
+ ndp, peek_message_type, IcmpIpExt, IcmpPacket, IcmpParseArgs, IcmpUnusedCode, OriginalPacket,
+};
/// An ICMPv6 packet with a dynamic message type.
///
@@ -23,7 +25,7 @@
/// knowing the message type ahead of time while still getting the benefits of a
/// statically-typed packet struct after parsing is complete.
#[allow(missing_docs)]
-pub enum Packet<B> {
+pub enum Icmpv6Packet<B> {
DestUnreachable(IcmpPacket<Ipv6, B, IcmpDestUnreachable>),
PacketTooBig(IcmpPacket<Ipv6, B, Icmpv6PacketTooBig>),
TimeExceeded(IcmpPacket<Ipv6, B, IcmpTimeExceeded>),
@@ -37,9 +39,9 @@
Redirect(IcmpPacket<Ipv6, B, ndp::Redirect>),
}
-impl<B: ByteSlice + fmt::Debug> fmt::Debug for Packet<B> {
+impl<B: ByteSlice + fmt::Debug> fmt::Debug for Icmpv6Packet<B> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use self::Packet::*;
+ use self::Icmpv6Packet::*;
match self {
DestUnreachable(ref p) => f.debug_tuple("DestUnreachable").field(p).finish(),
PacketTooBig(ref p) => f.debug_tuple("PacketTooBig").field(p).finish(),
@@ -56,32 +58,43 @@
}
}
-impl<B: ByteSlice> Packet<B> {
- /// Parse an ICMP packet.
- ///
- /// `parse` parses `bytes` as an ICMP packet and validates the header fields
- /// and checksum. It returns the byte range corresponding to the message
- /// body within `bytes`. This can be useful when extracting the encapsulated
- /// body to send to another layer of the stack. If the message type has no
- /// body, then the range is meaningless and should be ignored.
- pub fn parse(
- bytes: B, src_ip: Ipv6Addr, dst_ip: Ipv6Addr,
- ) -> Result<(Packet<B>, Range<usize>), ParseError> {
+impl<B: ByteSlice> ParsablePacket<B, IcmpParseArgs<Ipv6Addr>> for Icmpv6Packet<B> {
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ use self::Icmpv6Packet::*;
+ match self {
+ DestUnreachable(p) => p.parse_metadata(),
+ PacketTooBig(p) => p.parse_metadata(),
+ TimeExceeded(p) => p.parse_metadata(),
+ ParameterProblem(p) => p.parse_metadata(),
+ EchoRequest(p) => p.parse_metadata(),
+ EchoReply(p) => p.parse_metadata(),
+ RouterSolicitation(p) => p.parse_metadata(),
+ RouterAdvertisment(p) => p.parse_metadata(),
+ NeighborSolicitation(p) => p.parse_metadata(),
+ NeighborAdvertisment(p) => p.parse_metadata(),
+ Redirect(p) => p.parse_metadata(),
+ }
+ }
+
+ fn parse<BV: BufferView<B>>(
+ mut buffer: BV, args: IcmpParseArgs<Ipv6Addr>,
+ ) -> Result<Self, ParseError> {
macro_rules! mtch {
- ($bytes:expr, $src_ip:expr, $dst_ip:expr, $($variant:ident => $type:ty,)*) => {
- match peek_message_type(&$bytes)? {
+ ($buffer:expr, $args:expr, $($variant:ident => $type:ty,)*) => {
+ match peek_message_type($buffer.as_ref())? {
$(MessageType::$variant => {
- let (packet, range) = IcmpPacket::<Ipv6, B, $type>::parse($bytes, $src_ip, $dst_ip)?;
- (Packet::$variant(packet), range)
+ let packet = <IcmpPacket<Ipv6, B, $type> as ParsablePacket<_, _>>::parse($buffer, $args)?;
+ Icmpv6Packet::$variant(packet)
})*
}
}
}
Ok(mtch!(
- bytes,
- src_ip,
- dst_ip,
+ buffer,
+ args,
DestUnreachable => IcmpDestUnreachable,
PacketTooBig => Icmpv6PacketTooBig,
TimeExceeded => IcmpTimeExceeded,
@@ -204,41 +217,51 @@
#[cfg(test)]
mod tests {
+ use packet::{ParseBuffer, Serializer};
+
use super::*;
use crate::wire::icmp::{IcmpMessage, IcmpPacket, MessageBody};
- use crate::wire::ipv4::{Ipv4Packet, Ipv4PacketSerializer};
- use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketSerializer};
- use crate::wire::util::{BufferAndRange, PacketSerializer, SerializationRequest};
+ use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
fn serialize_to_bytes<B: ByteSlice, M: IcmpMessage<Ipv6, B>>(
src_ip: Ipv6Addr, dst_ip: Ipv6Addr, icmp: &IcmpPacket<Ipv6, B, M>,
- serializer: Ipv6PacketSerializer,
+ builder: Ipv6PacketBuilder,
) -> Vec<u8> {
- let icmp_serializer = icmp.serializer(src_ip, dst_ip);
- let mut data = vec![0; icmp_serializer.max_header_bytes() + icmp.message_body.len()];
- let body_offset = data.len() - icmp.message_body.len();
- (&mut data[body_offset..]).copy_from_slice(icmp.message_body.bytes());
- BufferAndRange::new_from(&mut data[..], body_offset..)
- .encapsulate(icmp_serializer)
- .encapsulate(serializer)
+ icmp.message_body
+ .bytes()
+ .encapsulate(icmp.builder(src_ip, dst_ip))
+ .encapsulate(builder)
.serialize_outer()
.as_ref()
.to_vec()
}
- #[test]
- fn test_parse_and_serialize_echo_request_ipv6() {
- use crate::wire::testdata::icmp_echo_v6::*;
- let (ip, _) = Ipv6Packet::parse(REQUEST_IP_PACKET_BYTES).unwrap();
- let (src_ip, dst_ip, hop_limit) = (ip.src_ip(), ip.dst_ip(), ip.hop_limit());
- // TODO: Check range
- let (icmp, _) =
- IcmpPacket::<_, _, IcmpEchoRequest>::parse(ip.body(), src_ip, dst_ip).unwrap();
- assert_eq!(icmp.original_packet().bytes(), ECHO_DATA);
- assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
- assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
+ fn test_parse_and_serialize<
+ M: for<'a> IcmpMessage<Ipv6, &'a [u8]>,
+ F: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
+ >(
+ mut req: &[u8], check: F,
+ ) {
+ let orig_req = &req[..];
- let data = serialize_to_bytes(src_ip, dst_ip, &icmp, ip.serializer());
- assert_eq!(&data[..], REQUEST_IP_PACKET_BYTES);
+ let ip = req.parse::<Ipv6Packet<_>>().unwrap();
+ let mut body = ip.body();
+ let icmp = body
+ .parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(ip.src_ip(), ip.dst_ip()))
+ .unwrap();
+ check(&icmp);
+
+ let data = serialize_to_bytes(ip.src_ip(), ip.dst_ip(), &icmp, ip.builder());
+ assert_eq!(&data[..], orig_req);
+ }
+
+ #[test]
+ fn test_parse_and_serialize_echo_request() {
+ use crate::wire::testdata::icmp_echo_v6::*;
+ test_parse_and_serialize::<IcmpEchoRequest, _>(REQUEST_IP_PACKET_BYTES, |icmp| {
+ assert_eq!(icmp.message_body.bytes(), ECHO_DATA);
+ assert_eq!(icmp.message().id_seq.id(), IDENTIFIER);
+ assert_eq!(icmp.message().id_seq.seq(), SEQUENCE_NUM);
+ });
}
}
diff --git a/bin/recovery_netstack/core/src/wire/icmp/mod.rs b/bin/recovery_netstack/core/src/wire/icmp/mod.rs
index 7ef2ea2..79d61ae 100644
--- a/bin/recovery_netstack/core/src/wire/icmp/mod.rs
+++ b/bin/recovery_netstack/core/src/wire/icmp/mod.rs
@@ -14,24 +14,25 @@
#[cfg(test)]
mod testdata;
-pub use self::icmpv4::Packet as Icmpv4Packet;
-pub use self::icmpv6::Packet as Icmpv6Packet;
+pub use self::common::*;
+pub use self::icmpv4::*;
+pub use self::icmpv6::*;
use std::cmp;
use std::convert::TryFrom;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
-use std::ops::{Deref, Range};
+use std::ops::Deref;
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{BufferView, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use crate::error::ParseError;
use crate::ip::{Ip, IpAddr, IpProto, Ipv4, Ipv6};
use crate::wire::ipv4;
-use crate::wire::util::fits_in_u32;
-use crate::wire::util::{BufferAndRange, Checksum, OptionImpl, Options, PacketSerializer};
+use crate::wire::util::{fits_in_u32, Checksum, OptionImpl, Options};
// Header has the same memory layout (thanks to repr(C, packed)) as an ICMP
// header. Thus, we can simply reinterpret the bytes of the ICMP header as a
@@ -166,6 +167,11 @@
}
}
+// TODO(joshlf): Once we have generic associated types, refactor this so that we
+// don't have to bind B ahead of time. Removing that requirement would make some
+// APIs (in particular, IcmpPacketBuilder) simpler by removing the B parameter
+// from them as well.
+
/// `MessageBody` represents the parsed body of the ICMP packet.
///
/// - For messages that expect no body, the `MessageBody` is of type `()`.
@@ -293,7 +299,7 @@
/// Parse a `Code` from the 8-bit "code" field in the ICMP header. Not all
/// values for this field are valid. If an invalid value is passed,
/// `code_from_u8` returns `None`.
- fn code_from_u8(_: u8) -> Option<Self::Code>;
+ fn code_from_u8(code: u8) -> Option<Self::Code>;
}
/// An ICMP packet.
@@ -323,32 +329,44 @@
}
}
-impl<I: IcmpIpExt, B: ByteSlice, M: IcmpMessage<I, B>> IcmpPacket<I, B, M> {
- /// Parse an ICMP packet.
- ///
- /// `parse` parses `bytes` as an ICMP packet and validates the header fields
- /// and checksum. It returns the byte range corresponding to the message
- /// body within `bytes`. This can be useful when extracting the encapsulated
- /// body to send to another layer of the stack. If the message type has no
- /// body, then the range is meaningless and should be ignored.
- ///
- /// If `bytes` are a valid ICMP packet, but do not match the message type
- /// `M`, `parse` will return `Err(ParseError::NotExpected)`. If multiple
- /// message types are valid in a given context, `peek_message_types` may be
- /// used to peek at the header and determine what type is present so that
- /// the correct type can then be used in a call to `parse`.
- pub fn parse(
- bytes: B, src_ip: I::Addr, dst_ip: I::Addr,
- ) -> Result<(IcmpPacket<I, B, M>, Range<usize>), ParseError> {
- let (header, rest) =
- LayoutVerified::<B, Header>::new_unaligned_from_prefix(bytes).ok_or_else(
- debug_err_fn!(ParseError::Format, "too few bytes for header"),
- )?;
- let (message, message_body) = LayoutVerified::<B, M>::new_unaligned_from_prefix(rest)
- .ok_or_else(debug_err_fn!(
- ParseError::Format,
- "too few bytes for packet"
- ))?;
+/// Arguments required to parse an ICMP packet.
+pub struct IcmpParseArgs<A: IpAddr> {
+ src_ip: A,
+ dst_ip: A,
+}
+
+impl<A: IpAddr> IcmpParseArgs<A> {
+ /// Construct a new `IcmpParseArgs`.
+ pub fn new(src_ip: A, dst_ip: A) -> IcmpParseArgs<A> {
+ IcmpParseArgs { src_ip, dst_ip }
+ }
+}
+
+impl<B: ByteSlice, I: IcmpIpExt, M: IcmpMessage<I, B>> ParsablePacket<B, IcmpParseArgs<I::Addr>>
+ for IcmpPacket<I, B, M>
+{
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ let header_len = self.header.bytes().len() + self.message.bytes().len();
+ ParseMetadata::from_packet(header_len, self.message_body.len(), 0)
+ }
+
+ fn parse<BV: BufferView<B>>(
+ mut buffer: BV, args: IcmpParseArgs<I::Addr>,
+ ) -> Result<Self, ParseError> {
+ let header = buffer.take_obj_front::<Header>().ok_or_else(debug_err_fn!(
+ ParseError::Format,
+ "too few bytes for header"
+ ))?;
+ let message = buffer.take_obj_front::<M>().ok_or_else(debug_err_fn!(
+ ParseError::Format,
+ "too few bytes for packet"
+ ))?;
+ let message_body = buffer.into_rest();
+ if !M::Body::EXPECTS_BODY && !message_body.is_empty() {
+ return debug_err!(Err(ParseError::Format), "unexpected message body");
+ }
if header.msg_type != M::TYPE.into() {
return debug_err!(Err(ParseError::NotExpected), "unexpected message type");
@@ -359,25 +377,28 @@
header.code
))?;
if header.checksum()
- != Self::compute_checksum(&header, message.bytes(), &message_body, src_ip, dst_ip)
- .ok_or_else(debug_err_fn!(ParseError::Format, "packet too large"))?
+ != Self::compute_checksum(
+ &header,
+ message.bytes(),
+ &message_body,
+ args.src_ip,
+ args.dst_ip,
+ )
+ .ok_or_else(debug_err_fn!(ParseError::Format, "packet too large"))?
{
return debug_err!(Err(ParseError::Checksum), "invalid checksum");
}
-
let message_body = M::Body::parse(message_body)?;
-
- let pre_body_len = header.bytes().len() + message.bytes().len();
- let total_len = pre_body_len + message_body.len();
- let packet = IcmpPacket {
+ Ok(IcmpPacket {
header,
message,
message_body,
_marker: PhantomData,
- };
- Ok((packet, pre_body_len..total_len))
+ })
}
+}
+impl<I: IcmpIpExt, B: ByteSlice, M: IcmpMessage<I, B>> IcmpPacket<I, B, M> {
/// Get the ICMP message.
pub fn message(&self) -> &M {
&self.message
@@ -392,9 +413,9 @@
M::code_from_u8(self.header.code).unwrap()
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketSerializer<I, B, M> {
- IcmpPacketSerializer {
+ /// Construct a builder with the same contents as this packet.
+ pub fn builder(&self, src_ip: I::Addr, dst_ip: I::Addr) -> IcmpPacketBuilder<I, B, M> {
+ IcmpPacketBuilder {
src_ip,
dst_ip,
code: self.code(),
@@ -459,20 +480,20 @@
}
}
-/// A serializer for ICMP packets.
-pub struct IcmpPacketSerializer<I: IcmpIpExt, B, M: IcmpMessage<I, B>> {
+/// A builder for ICMP packets.
+pub struct IcmpPacketBuilder<I: IcmpIpExt, B, M: IcmpMessage<I, B>> {
src_ip: I::Addr,
dst_ip: I::Addr,
code: M::Code,
msg: M,
}
-impl<I: IcmpIpExt, B, M: IcmpMessage<I, B>> IcmpPacketSerializer<I, B, M> {
- /// Construct a new `IcmpPacketSerializer`.
+impl<I: IcmpIpExt, B, M: IcmpMessage<I, B>> IcmpPacketBuilder<I, B, M> {
+ /// Construct a new `IcmpPacketBuilder`.
pub fn new(
src_ip: I::Addr, dst_ip: I::Addr, code: M::Code, msg: M,
- ) -> IcmpPacketSerializer<I, B, M> {
- IcmpPacketSerializer {
+ ) -> IcmpPacketBuilder<I, B, M> {
+ IcmpPacketBuilder {
src_ip,
dst_ip,
code,
@@ -482,54 +503,57 @@
}
// TODO(joshlf): Figure out a way to split body and non-body message types by
-// trait and implement PacketSerializer for some and InnerPacketSerializer for
-// others.
+// trait and implement PacketBuilder for some and InnerPacketBuilder for others.
-impl<I: IcmpIpExt, B, M: IcmpMessage<I, B>> PacketSerializer for IcmpPacketSerializer<I, B, M> {
- fn max_header_bytes(&self) -> usize {
+impl<I: IcmpIpExt, B, M: IcmpMessage<I, B>> PacketBuilder for IcmpPacketBuilder<I, B, M> {
+ fn header_len(&self) -> usize {
mem::size_of::<Header>() + mem::size_of::<M>()
}
- fn min_header_bytes(&self) -> usize {
- self.max_header_bytes()
+ fn min_body_len(&self) -> usize {
+ 0
}
- fn serialize<B2: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B2>) {
- let extend_backwards = {
- let (prefix, message_body, _) = buffer.parts_mut();
- assert!(
- M::Body::EXPECTS_BODY || message_body.len() == 0,
- "body provided for message that doesn't take a body"
- );
- // SECURITY: Use _zeroed constructors to ensure we zero memory to prevent
- // leaking information from packets previously stored in this buffer.
- let (prefix, mut message) =
- LayoutVerified::<_, M>::new_unaligned_from_suffix_zeroed(prefix)
- .expect("too few bytes for ICMP message");
- let (_, mut header) =
- LayoutVerified::<_, Header>::new_unaligned_from_suffix_zeroed(prefix)
- .expect("too few bytes for ICMP message");
- *message = self.msg;
- header.set_msg_type(M::TYPE);
- header.code = self.code.into();
- let checksum = IcmpPacket::<I, B, M>::compute_checksum(
- &header,
- message.bytes(),
- message_body,
- self.src_ip,
- self.dst_ip,
- )
- .unwrap_or_else(|| {
- panic!(
- "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
- header.bytes().len() + message.bytes().len() + message_body.len(),
- )
- });
- header.set_checksum(checksum);
- header.bytes().len() + message.bytes().len()
- };
+ fn footer_len(&self) -> usize {
+ 0
+ }
- buffer.extend_backwards(extend_backwards);
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
+ use packet::BufferViewMut;
+
+ let (mut prefix, message_body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut prefix = &mut prefix;
+
+ assert!(
+ M::Body::EXPECTS_BODY || message_body.len() == 0,
+ "body provided for message that doesn't take a body"
+ );
+ // SECURITY: Use _zero constructors to ensure we zero memory to prevent
+ // leaking information from packets previously stored in this buffer.
+ let mut header = prefix
+ .take_obj_front_zero::<Header>()
+ .expect("too few bytes for ICMP message");
+ let mut message = prefix
+ .take_obj_front_zero::<M>()
+ .expect("too few bytes for ICMP message");
+ *message = self.msg;
+ header.set_msg_type(M::TYPE);
+ header.code = self.code.into();
+ let checksum = IcmpPacket::<I, B, M>::compute_checksum(
+ &header,
+ message.bytes(),
+ message_body,
+ self.src_ip,
+ self.dst_ip,
+ )
+ .unwrap_or_else(|| {
+ panic!(
+ "total ICMP packet length of {} overflows 32-bit length field of pseudo-header",
+ header.bytes().len() + message.bytes().len() + message_body.len(),
+ )
+ });
+ header.set_checksum(checksum);
}
}
diff --git a/bin/recovery_netstack/core/src/wire/icmp/ndp.rs b/bin/recovery_netstack/core/src/wire/icmp/ndp.rs
index a176642..a214b45 100644
--- a/bin/recovery_netstack/core/src/wire/icmp/ndp.rs
+++ b/bin/recovery_netstack/core/src/wire/icmp/ndp.rs
@@ -119,7 +119,7 @@
use crate::ip::Ipv6Addr;
use crate::wire::util::{OptionImpl, OptionImplErr};
- create_net_enum!{
+ create_net_enum! {
NdpOptionType,
SourceLinkLayerAddress: SOURCE_LINK_LAYER_ADDRESS = 1,
TargetLinkLayerAddress: TARGET_LINK_LAYER_ADDRESS = 2,
@@ -214,19 +214,24 @@
}
#[cfg(test)]
-mod test {
- use super::*;
+mod tests {
+ use packet::{ParsablePacket, ParseBuffer};
- use crate::wire::icmp::{IcmpMessage, IcmpPacket};
- use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketSerializer};
+ use super::*;
+ use crate::wire::icmp::{IcmpMessage, IcmpPacket, IcmpParseArgs};
+ use crate::wire::ipv6::{Ipv6Packet, Ipv6PacketBuilder};
#[test]
fn parse_neighbor_solicitation() {
use crate::wire::icmp::testdata::ndp_neighbor::*;
- let (ip, _) = Ipv6Packet::parse(SOLICITATION_IP_PACKET_BYTES).unwrap();
+ let mut buf = &SOLICITATION_IP_PACKET_BYTES[..];
+ let ip = buf.parse::<Ipv6Packet<_>>().unwrap();
let (src_ip, dst_ip, hop_limit) = (ip.src_ip(), ip.dst_ip(), ip.hop_limit());
- let (icmp, _) =
- IcmpPacket::<_, _, NeighborSolicitation>::parse(ip.body(), src_ip, dst_ip).unwrap();
+ let icmp = buf
+ .parse_with::<_, IcmpPacket<_, _, NeighborSolicitation>>(IcmpParseArgs::new(
+ src_ip, dst_ip,
+ ))
+ .unwrap();
assert_eq!(icmp.message().target_address.ipv6_bytes(), TARGET_ADDRESS);
for option in icmp.ndp_options().iter() {
@@ -242,10 +247,14 @@
#[test]
fn parse_neighbor_advertisment() {
use crate::wire::icmp::testdata::ndp_neighbor::*;
- let (ip, _) = Ipv6Packet::parse(ADVERTISMENT_IP_PACKET_BYTES).unwrap();
+ let mut buf = &ADVERTISMENT_IP_PACKET_BYTES[..];
+ let ip = buf.parse::<Ipv6Packet<_>>().unwrap();
let (src_ip, dst_ip, hop_limit) = (ip.src_ip(), ip.dst_ip(), ip.hop_limit());
- let (icmp, _) =
- IcmpPacket::<_, _, NeighborAdvertisment>::parse(ip.body(), src_ip, dst_ip).unwrap();
+ let icmp = buf
+ .parse_with::<_, IcmpPacket<_, _, NeighborAdvertisment>>(IcmpParseArgs::new(
+ src_ip, dst_ip,
+ ))
+ .unwrap();
assert_eq!(icmp.message().target_address.ipv6_bytes(), TARGET_ADDRESS);
assert_eq!(icmp.ndp_options().iter().count(), 0);
}
@@ -253,10 +262,14 @@
#[test]
fn parse_router_advertisment() {
use crate::wire::icmp::testdata::ndp_router::*;
- let (ip, _) = Ipv6Packet::parse(ADVERTISMENT_IP_PACKET_BYTES).unwrap();
+ let mut buf = &ADVERTISMENT_IP_PACKET_BYTES[..];
+ let ip = buf.parse::<Ipv6Packet<_>>().unwrap();
let (src_ip, dst_ip) = (ip.src_ip(), ip.dst_ip());
- let (icmp, _) =
- IcmpPacket::<_, _, RouterAdvertisment>::parse(ip.body(), src_ip, dst_ip).unwrap();
+ let icmp = buf
+ .parse_with::<_, IcmpPacket<_, _, RouterAdvertisment>>(IcmpParseArgs::new(
+ src_ip, dst_ip,
+ ))
+ .unwrap();
assert_eq!(icmp.message().current_hop_limit, HOP_LIMIT);
assert_eq!(icmp.message().router_lifetime(), LIFETIME);
assert_eq!(icmp.message().reachable_time(), REACHABLE_TIME);
diff --git a/bin/recovery_netstack/core/src/wire/ipv4.rs b/bin/recovery_netstack/core/src/wire/ipv4.rs
index 3c99995..e661523 100644
--- a/bin/recovery_netstack/core/src/wire/ipv4.rs
+++ b/bin/recovery_netstack/core/src/wire/ipv4.rs
@@ -5,14 +5,16 @@
//! Parsing and serialization of IPv4 packets.
use std::fmt::{self, Debug, Formatter};
-use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{
+ BufferView, BufferViewMut, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer,
+};
use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified, Unaligned};
use crate::error::ParseError;
use crate::ip::{IpProto, Ipv4Addr, Ipv4Option};
-use crate::wire::util::{BufferAndRange, Checksum, Options, PacketSerializer};
+use crate::wire::util::{Checksum, Options};
use self::options::Ipv4OptionImpl;
@@ -83,7 +85,7 @@
/// necessary.
///
/// An `Ipv4Packet` - whether parsed using `parse` or created using
-/// `Ipv4PacketSerializer` - maintains the invariant that the checksum is always
+/// `Ipv4PacketBuilder` - maintains the invariant that the checksum is always
/// valid.
pub struct Ipv4Packet<B> {
hdr_prefix: LayoutVerified<B, HeaderPrefix>,
@@ -91,27 +93,31 @@
body: B,
}
-impl<B: ByteSlice> Ipv4Packet<B> {
- /// Parse an IPv4 packet.
- ///
- /// `parse` parses `bytes` as an IPv4 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.
- pub fn parse(bytes: B) -> Result<(Ipv4Packet<B>, Range<usize>), ParseError> {
+impl<B: ByteSlice> ParsablePacket<B, ()> for Ipv4Packet<B> {
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ let header_len = self.hdr_prefix.bytes().len() + self.options.bytes().len();
+ ParseMetadata::from_packet(header_len, self.body.len(), 0)
+ }
+
+ fn parse<BV: BufferView<B>>(mut buffer: BV, args: ()) -> Result<Self, ParseError> {
// See for details: https://en.wikipedia.org/wiki/IPv4#Header
- let total_len = bytes.len();
- let (hdr_prefix, rest) = LayoutVerified::<B, HeaderPrefix>::new_from_prefix(bytes)
+ let total_len = buffer.len();
+ let hdr_prefix = buffer
+ .take_obj_front::<HeaderPrefix>()
.ok_or_else(debug_err_fn!(
ParseError::Format,
"too few bytes for header"
))?;
let hdr_bytes = (hdr_prefix.ihl() * 4) as usize;
- if hdr_bytes > total_len || hdr_bytes < hdr_prefix.bytes().len() {
+ if hdr_bytes < HEADER_PREFIX_SIZE {
return debug_err!(Err(ParseError::Format), "invalid IHL: {}", hdr_prefix.ihl());
}
- let (options, body) = rest.split_at(hdr_bytes - HEADER_PREFIX_SIZE);
+ let options = buffer
+ .take_front(hdr_bytes - HEADER_PREFIX_SIZE)
+ .ok_or_else(debug_err_fn!(ParseError::Format, "IHL larger than buffer"))?;
let options = Options::parse(options).map_err(|_| ParseError::Format)?;
if hdr_prefix.version() != 4 {
return debug_err!(
@@ -121,10 +127,15 @@
);
}
let body = if (hdr_prefix.total_length() as usize) < total_len {
- let (body, _) = body.split_at(hdr_prefix.total_length() as usize - hdr_bytes);
+ // This unwrap is safe because of the check against total_len.
+ let body = buffer
+ .take_back(hdr_prefix.total_length() as usize - hdr_bytes)
+ .unwrap();
+ // Discard the padding left by the previous layer.
+ buffer.into_rest();
body
} else if hdr_prefix.total_length() as usize == total_len {
- body
+ buffer.into_rest()
} else {
// we don't yet support IPv4 fragmentation
return debug_err!(Err(ParseError::NotSupported), "fragmentation not supported");
@@ -138,10 +149,11 @@
if packet.compute_header_checksum() != packet.hdr_prefix.hdr_checksum() {
return debug_err!(Err(ParseError::Checksum), "invalid checksum");
}
- let hdr_len = packet.hdr_prefix.bytes().len() + packet.options.bytes().len();
- Ok((packet, hdr_len..total_len))
+ Ok(packet)
}
+}
+impl<B: ByteSlice> Ipv4Packet<B> {
/// Iterate over the IPv4 header options.
pub fn iter_options<'a>(&'a self) -> impl 'a + Iterator<Item = Ipv4Option> {
self.options.iter()
@@ -230,9 +242,9 @@
self.header_len() + self.body.len()
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer(&self) -> Ipv4PacketSerializer {
- let mut s = Ipv4PacketSerializer {
+ /// Construct a builder with the same contents as this packet.
+ pub fn builder(&self) -> Ipv4PacketBuilder {
+ let mut s = Ipv4PacketBuilder {
dscp: self.dscp(),
ecn: self.ecn(),
id: self.id(),
@@ -289,8 +301,8 @@
}
}
-/// A serializer for IPv4 packets.
-pub struct Ipv4PacketSerializer {
+/// A builder for IPv4 packets.
+pub struct Ipv4PacketBuilder {
dscp: u8,
ecn: u8,
id: u16,
@@ -302,12 +314,10 @@
dst_ip: Ipv4Addr,
}
-impl Ipv4PacketSerializer {
- /// Construct a new `Ipv4PacketSerializer`.
- pub fn new(
- src_ip: Ipv4Addr, dst_ip: Ipv4Addr, ttl: u8, proto: IpProto,
- ) -> Ipv4PacketSerializer {
- Ipv4PacketSerializer {
+impl Ipv4PacketBuilder {
+ /// Construct a new `Ipv4PacketBuilder`.
+ pub fn new(src_ip: Ipv4Addr, dst_ip: Ipv4Addr, ttl: u8, proto: IpProto) -> Ipv4PacketBuilder {
+ Ipv4PacketBuilder {
dscp: 0,
ecn: 0,
id: 0,
@@ -382,61 +392,61 @@
// used by wire::icmp
pub const MIN_HEADER_BYTES: usize = 20;
-impl PacketSerializer for Ipv4PacketSerializer {
- fn max_header_bytes(&self) -> usize {
- MAX_HEADER_BYTES
- }
-
- fn min_header_bytes(&self) -> usize {
+impl PacketBuilder for Ipv4PacketBuilder {
+ fn header_len(&self) -> usize {
MIN_HEADER_BYTES
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
- let extend_backwards = {
- let (header, body, _) = buffer.parts_mut();
- // create a 0-byte slice for the options since we don't support
- // serializing options yet (NET-955)
- let (options, body) = body.split_at_mut(0);
- // SECURITY: Use _zeroed constructor to ensure we zero memory to prevent
- // leaking information from packets previously stored in this buffer.
- let (_, hdr_prefix) =
- LayoutVerified::<_, HeaderPrefix>::new_unaligned_from_suffix_zeroed(header)
- .expect("too few bytes for IPv4 header");
- let options =
- Options::parse(options).expect("parsing an empty options slice should not fail");
- let mut packet = Ipv4Packet {
- hdr_prefix,
- options,
- body,
- };
+ fn min_body_len(&self) -> usize {
+ 0
+ }
- packet.hdr_prefix.version_ihl = (4u8 << 4) | 5;
- packet.hdr_prefix.dscp_ecn = (self.dscp << 2) | self.ecn;
- let total_len = packet.total_packet_len();
- if total_len >= 1 << 16 {
- panic!(
- "packet length of {} exceeds maximum of {}",
- total_len,
- 1 << 16 - 1,
- );
- }
- NetworkEndian::write_u16(&mut packet.hdr_prefix.total_len, total_len as u16);
- NetworkEndian::write_u16(&mut packet.hdr_prefix.id, self.id);
- NetworkEndian::write_u16(
- &mut packet.hdr_prefix.flags_frag_off,
- ((u16::from(self.flags)) << 13) | self.frag_off,
- );
- packet.hdr_prefix.ttl = self.ttl;
- packet.hdr_prefix.proto = self.proto;
- packet.hdr_prefix.src_ip = self.src_ip.ipv4_bytes();
- packet.hdr_prefix.dst_ip = self.dst_ip.ipv4_bytes();
- let checksum = packet.compute_header_checksum();
- NetworkEndian::write_u16(&mut packet.hdr_prefix.hdr_checksum, checksum);
+ fn footer_len(&self) -> usize {
+ 0
+ }
- packet.header_len()
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
+ let (mut header, body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut header = &mut header;
+
+ // SECURITY: Use _zero constructor to ensure we zero memory to prevent
+ // leaking information from packets previously stored in this buffer.
+ let hdr_prefix = header
+ .take_obj_front_zero::<HeaderPrefix>()
+ .expect("too few bytes for IPv4 header");
+ // create a 0-byte slice for the options since we don't support
+ // serializing options yet (NET-955)
+ let options =
+ Options::parse(&mut [][..]).expect("parsing an empty options slice should not fail");
+ let mut packet = Ipv4Packet {
+ hdr_prefix,
+ options,
+ body,
};
- buffer.extend_backwards(extend_backwards);
+ packet.hdr_prefix.version_ihl = (4u8 << 4) | 5;
+ packet.hdr_prefix.dscp_ecn = (self.dscp << 2) | self.ecn;
+ let total_len = packet.total_packet_len();
+ if total_len >= 1 << 16 {
+ panic!(
+ "packet length of {} exceeds maximum of {}",
+ total_len,
+ 1 << 16 - 1,
+ );
+ }
+ NetworkEndian::write_u16(&mut packet.hdr_prefix.total_len, total_len as u16);
+ NetworkEndian::write_u16(&mut packet.hdr_prefix.id, self.id);
+ NetworkEndian::write_u16(
+ &mut packet.hdr_prefix.flags_frag_off,
+ ((u16::from(self.flags)) << 13) | self.frag_off,
+ );
+ packet.hdr_prefix.ttl = self.ttl;
+ packet.hdr_prefix.proto = self.proto;
+ packet.hdr_prefix.src_ip = self.src_ip.ipv4_bytes();
+ packet.hdr_prefix.dst_ip = self.dst_ip.ipv4_bytes();
+ let checksum = packet.compute_header_checksum();
+ NetworkEndian::write_u16(&mut packet.hdr_prefix.hdr_checksum, checksum);
}
}
@@ -487,10 +497,11 @@
#[cfg(test)]
mod tests {
+ use packet::{Buf, BufferSerializer, ParseBuffer, Serializer};
+
use super::*;
use crate::device::ethernet::EtherType;
use crate::wire::ethernet::EthernetFrame;
- use crate::wire::util::SerializationRequest;
const DEFAULT_SRC_IP: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const DEFAULT_DST_IP: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
@@ -499,14 +510,14 @@
fn test_parse_serialize_full_tcp() {
use crate::wire::testdata::tls_client_hello::*;
- let (frame, body_range) = EthernetFrame::parse(ETHERNET_FRAME_BYTES).unwrap();
- assert_eq!(body_range, ETHERNET_BODY_RANGE);
+ let mut buf = ÐERNET_FRAME_BYTES[..];
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
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);
+ let mut body = frame.body();
+ let packet = body.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(packet.proto(), Ok(IpProto::Tcp));
assert_eq!(packet.dscp(), IP_DSCP);
assert_eq!(packet.ecn(), IP_ECN);
@@ -518,9 +529,10 @@
assert_eq!(packet.src_ip(), IP_SRC_IP);
assert_eq!(packet.dst_ip(), IP_DST_IP);
- let buffer = (&frame.body()[body_range])
- .encapsulate(packet.serializer())
- .encapsulate(frame.serializer())
+ let buffer = packet
+ .body()
+ .encapsulate(packet.builder())
+ .encapsulate(frame.builder())
.serialize_outer();
assert_eq!(buffer.as_ref(), ETHERNET_FRAME_BYTES);
}
@@ -529,14 +541,14 @@
fn test_parse_serialize_full_udp() {
use crate::wire::testdata::dns_request::*;
- let (frame, body_range) = EthernetFrame::parse(ETHERNET_FRAME_BYTES).unwrap();
- assert_eq!(body_range, ETHERNET_BODY_RANGE);
+ let mut buf = ÐERNET_FRAME_BYTES[..];
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
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);
+ let mut body = frame.body();
+ let packet = body.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(packet.proto(), Ok(IpProto::Udp));
assert_eq!(packet.dscp(), IP_DSCP);
assert_eq!(packet.ecn(), IP_ECN);
@@ -548,9 +560,10 @@
assert_eq!(packet.src_ip(), IP_SRC_IP);
assert_eq!(packet.dst_ip(), IP_DST_IP);
- let buffer = (&frame.body()[body_range])
- .encapsulate(packet.serializer())
- .encapsulate(frame.serializer())
+ let buffer = packet
+ .body()
+ .encapsulate(packet.builder())
+ .encapsulate(frame.builder())
.serialize_outer();
assert_eq!(buffer.as_ref(), ETHERNET_FRAME_BYTES);
}
@@ -581,9 +594,8 @@
#[test]
fn test_parse() {
- let bytes = hdr_prefix_to_bytes(new_hdr_prefix());
- let (packet, body_range) = Ipv4Packet::parse(&bytes[..]).unwrap();
- assert_eq!(body_range, 20..20);
+ let mut bytes = &hdr_prefix_to_bytes(new_hdr_prefix())[..];
+ let packet = bytes.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(packet.id(), 0x0102);
assert_eq!(packet.ttl(), 0x03);
assert_eq!(packet.proto(), Ok(IpProto::Tcp));
@@ -598,7 +610,9 @@
let mut hdr_prefix = new_hdr_prefix();
hdr_prefix.version_ihl = (5 << 4) | 5;
assert_eq!(
- Ipv4Packet::parse(&hdr_prefix_to_bytes(hdr_prefix)[..]).unwrap_err(),
+ (&hdr_prefix_to_bytes(hdr_prefix)[..])
+ .parse::<Ipv4Packet<_>>()
+ .unwrap_err(),
ParseError::Format
);
@@ -607,7 +621,9 @@
let mut hdr_prefix = new_hdr_prefix();
hdr_prefix.version_ihl = (4 << 4) | 4;
assert_eq!(
- Ipv4Packet::parse(&hdr_prefix_to_bytes(hdr_prefix)[..]).unwrap_err(),
+ (&hdr_prefix_to_bytes(hdr_prefix)[..])
+ .parse::<Ipv4Packet<_>>()
+ .unwrap_err(),
ParseError::Format
);
@@ -616,46 +632,39 @@
let mut hdr_prefix = new_hdr_prefix();
hdr_prefix.version_ihl = (4 << 4) | 6;
assert_eq!(
- Ipv4Packet::parse(&hdr_prefix_to_bytes(hdr_prefix)[..]).unwrap_err(),
+ (&hdr_prefix_to_bytes(hdr_prefix)[..])
+ .parse::<Ipv4Packet<_>>()
+ .unwrap_err(),
ParseError::Format
);
}
- // Return a stock Ipv4PacketSerializer with reasonable default values.
- fn new_serializer() -> Ipv4PacketSerializer {
- Ipv4PacketSerializer::new(DEFAULT_DST_IP, DEFAULT_DST_IP, 64, IpProto::Tcp)
+ // Return a stock Ipv4PacketBuilder with reasonable default values.
+ fn new_builder() -> Ipv4PacketBuilder {
+ Ipv4PacketBuilder::new(DEFAULT_DST_IP, DEFAULT_DST_IP, 64, IpProto::Tcp)
}
#[test]
fn test_serialize() {
- let mut buf = [0; 30];
- let mut serializer = new_serializer();
- serializer.dscp(0x12);
- serializer.ecn(3);
- serializer.id(0x0405);
- serializer.df_flag(true);
- serializer.mf_flag(true);
- serializer.fragment_offset(0x0607);
- {
- // set the body
- (&mut buf[20..]).copy_from_slice(&[0, 1, 2, 3, 3, 4, 5, 7, 8, 9]);
- let mut buffer = BufferAndRange::new_from(&mut buf[..], 20..);
- serializer.serialize(&mut buffer);
- assert_eq!(buffer.range(), 0..30);
- }
+ let mut builder = new_builder();
+ builder.dscp(0x12);
+ builder.ecn(3);
+ builder.id(0x0405);
+ builder.df_flag(true);
+ builder.mf_flag(true);
+ builder.fragment_offset(0x0607);
- // assert that we get the literal bytes we expected
+ let mut buf = (&[0, 1, 2, 3, 3, 4, 5, 7, 8, 9])
+ .encapsulate(builder)
+ .serialize_outer();
assert_eq!(
- buf,
+ buf.as_ref(),
[
69, 75, 0, 30, 4, 5, 102, 7, 64, 6, 248, 103, 5, 6, 7, 8, 5, 6, 7, 8, 0, 1, 2, 3,
3, 4, 5, 7, 8, 9
],
);
- let (packet, body_range) = Ipv4Packet::parse(&buf[..]).unwrap();
- // assert that when we parse those bytes, we get the values we set in
- // the serializer
- assert_eq!(body_range, 20..30);
+ let packet = buf.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(packet.dscp(), 0x12);
assert_eq!(packet.ecn(), 3);
assert_eq!(packet.id(), 0x0405);
@@ -666,12 +675,16 @@
#[test]
fn test_serialize_zeroes() {
- // Test that Ipv4PacketSerializer::serialize properly zeroes memory before
+ // Test that Ipv4PacketBuilder::serialize properly zeroes memory before
// serializing the header.
let mut buf_0 = [0; 20];
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut buf_0[..], 20..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_0[..], 20..))
+ .encapsulate(new_builder())
+ .serialize_outer();
let mut buf_1 = [0xFF; 20];
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut buf_1[..], 20..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_1[..], 20..))
+ .encapsulate(new_builder())
+ .serialize_outer();
assert_eq!(buf_0, buf_1);
}
@@ -679,14 +692,8 @@
#[should_panic]
fn test_serialize_panic_packet_length() {
// Test that a packet which is longer than 2^16 - 1 bytes is rejected.
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut [0; 1 << 16][..], 20..));
- }
-
- #[test]
- #[should_panic]
- fn test_serialize_panic_insufficient_header_space() {
- // Test that a body range which doesn't leave enough room for the header
- // is rejected.
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut [0; 20], ..));
+ BufferSerializer::new_vec(Buf::new(&mut [0; (1 << 16) - 20][..], ..))
+ .encapsulate(new_builder())
+ .serialize_outer();
}
}
diff --git a/bin/recovery_netstack/core/src/wire/ipv6.rs b/bin/recovery_netstack/core/src/wire/ipv6.rs
index e522246..61ab650 100644
--- a/bin/recovery_netstack/core/src/wire/ipv6.rs
+++ b/bin/recovery_netstack/core/src/wire/ipv6.rs
@@ -5,15 +5,16 @@
//! Parsing and serialization of IPv6 packets.
use std::fmt::{self, Debug, Formatter};
-use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
use log::debug;
+use packet::{
+ BufferView, BufferViewMut, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer,
+};
use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified, Unaligned};
use crate::error::ParseError;
use crate::ip::{IpProto, Ipv6Addr};
-use crate::wire::util::{BufferAndRange, PacketSerializer};
// FixedHeader has the same memory layout (thanks to repr(C, packed)) as an IPv6
// fixed header. Thus, we can simply reinterpret the bytes of the IPv6 fixed
@@ -82,16 +83,23 @@
body: B,
}
-impl<B: ByteSlice> Ipv6Packet<B> {
- /// Parse an IPv6 packet.
- ///
- /// `parse` parses `bytes` as an IPv6 packet. 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.
- pub fn parse(bytes: B) -> Result<(Ipv6Packet<B>, Range<usize>), ParseError> {
- let total_len = bytes.len();
- let (fixed_hdr, rest) = LayoutVerified::<B, FixedHeader>::new_from_prefix(bytes)
+impl<B: ByteSlice> ParsablePacket<B, ()> for Ipv6Packet<B> {
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ let header_len = self.fixed_hdr.bytes().len()
+ + self
+ .extension_hdrs
+ .as_ref()
+ .map(|hdrs| hdrs.len())
+ .unwrap_or(0);
+ ParseMetadata::from_packet(header_len, self.body.len(), 0)
+ }
+
+ fn parse<BV: BufferView<B>>(mut buffer: BV, args: ()) -> Result<Self, ParseError> {
+ let total_len = buffer.len();
+ let fixed_hdr = buffer
+ .take_obj_front::<FixedHeader>()
.ok_or_else(debug_err_fn!(
ParseError::Format,
"too few bytes for header"
@@ -100,7 +108,7 @@
let packet = Ipv6Packet {
fixed_hdr,
extension_hdrs: None,
- body: rest,
+ body: buffer.into_rest(),
};
if packet.fixed_hdr.version() != 6 {
return debug_err!(
@@ -115,11 +123,11 @@
"payload length does not match header"
);
}
- let extensions_len = packet.extension_hdrs.as_ref().map(|b| b.len()).unwrap_or(0);
- let hdr_len = packet.fixed_hdr.bytes().len() + extensions_len;
- Ok((packet, hdr_len..total_len))
+ Ok(packet)
}
+}
+impl<B: ByteSlice> Ipv6Packet<B> {
/// The packet body.
pub fn body(&self) -> &[u8] {
&self.body
@@ -170,9 +178,9 @@
self.extension_hdrs.as_ref().map(|b| b.len()).unwrap_or(0) + self.body.len()
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer(&self) -> Ipv6PacketSerializer {
- Ipv6PacketSerializer {
+ /// Construct a builder with the same contents as this packet.
+ pub fn builder(&self) -> Ipv6PacketBuilder {
+ Ipv6PacketBuilder {
ds: self.ds(),
ecn: self.ecn(),
flowlabel: self.flowlabel(),
@@ -207,8 +215,8 @@
}
}
-/// A serializer for IPv6 packets.
-pub struct Ipv6PacketSerializer {
+/// A builder for IPv6 packets.
+pub struct Ipv6PacketBuilder {
ds: u8,
ecn: u8,
flowlabel: u32,
@@ -218,12 +226,12 @@
dst_ip: Ipv6Addr,
}
-impl Ipv6PacketSerializer {
- /// Construct a new `Ipv6PacketSerializer`.
+impl Ipv6PacketBuilder {
+ /// Construct a new `Ipv6PacketBuilder`.
pub fn new(
src_ip: Ipv6Addr, dst_ip: Ipv6Addr, hop_limit: u8, proto: IpProto,
- ) -> Ipv6PacketSerializer {
- Ipv6PacketSerializer {
+ ) -> Ipv6PacketBuilder {
+ Ipv6PacketBuilder {
ds: 0,
ecn: 0,
flowlabel: 0,
@@ -267,60 +275,63 @@
const FIXED_HEADER_BYTES: usize = 40;
-impl PacketSerializer for Ipv6PacketSerializer {
- fn max_header_bytes(&self) -> usize {
+impl PacketBuilder for Ipv6PacketBuilder {
+ fn header_len(&self) -> usize {
// TODO(joshlf): Update when we support serializing extension headers
FIXED_HEADER_BYTES
}
- fn min_header_bytes(&self) -> usize {
- FIXED_HEADER_BYTES
+ fn min_body_len(&self) -> usize {
+ 0
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
- let extend_backwards = {
- let (header, body, _) = buffer.parts_mut();
- // TODO(tkilbourn): support extension headers
- let (_, fixed_hdr) =
- LayoutVerified::<_, FixedHeader>::new_unaligned_from_suffix_zeroed(header)
- .expect("too few bytes for IPv6 header");
- let extension_hdrs = None;
- let mut packet = Ipv6Packet {
- fixed_hdr,
- extension_hdrs,
- body,
- };
+ fn footer_len(&self) -> usize {
+ 0
+ }
- packet.fixed_hdr.version_tc_flowlabel = [
- (6u8 << 4) | self.ds >> 2,
- ((self.ds & 0b11) << 6) | (self.ecn << 4) | (self.flowlabel >> 16) as u8,
- ((self.flowlabel >> 8) & 0xFF) as u8,
- (self.flowlabel & 0xFF) as u8,
- ];
- let payload_len = packet.payload_len();
- debug!("serialize: payload_len={}", payload_len);
- if payload_len >= 1 << 16 {
- panic!(
- "packet length of {} exceeds maximum of {}",
- payload_len,
- 1 << 16 - 1,
- );
- }
- NetworkEndian::write_u16(&mut packet.fixed_hdr.payload_len, payload_len as u16);
- packet.fixed_hdr.next_hdr = self.proto;
- packet.fixed_hdr.hop_limit = self.hop_limit;
- packet.fixed_hdr.src_ip = self.src_ip.ipv6_bytes();
- packet.fixed_hdr.dst_ip = self.dst_ip.ipv6_bytes();
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
+ let (mut header, body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut header = &mut header;
- packet.header_len()
+ // TODO(tkilbourn): support extension headers
+ let fixed_hdr = header
+ .take_obj_front_zero::<FixedHeader>()
+ .expect("too few bytes for IPv6 header");
+ let extension_hdrs = None;
+ let mut packet = Ipv6Packet {
+ fixed_hdr,
+ extension_hdrs,
+ body,
};
- buffer.extend_backwards(extend_backwards);
+ packet.fixed_hdr.version_tc_flowlabel = [
+ (6u8 << 4) | self.ds >> 2,
+ ((self.ds & 0b11) << 6) | (self.ecn << 4) | (self.flowlabel >> 16) as u8,
+ ((self.flowlabel >> 8) & 0xFF) as u8,
+ (self.flowlabel & 0xFF) as u8,
+ ];
+ let payload_len = packet.payload_len();
+ debug!("serialize: payload_len={}", payload_len);
+ if payload_len >= 1 << 16 {
+ panic!(
+ "packet length of {} exceeds maximum of {}",
+ payload_len,
+ 1 << 16 - 1,
+ );
+ }
+ NetworkEndian::write_u16(&mut packet.fixed_hdr.payload_len, payload_len as u16);
+ packet.fixed_hdr.next_hdr = self.proto;
+ packet.fixed_hdr.hop_limit = self.hop_limit;
+ packet.fixed_hdr.src_ip = self.src_ip.ipv6_bytes();
+ packet.fixed_hdr.dst_ip = self.dst_ip.ipv6_bytes();
}
}
#[cfg(test)]
mod tests {
+ use packet::{Buf, BufferSerializer, ParseBuffer, Serializer};
+
use super::*;
const DEFAULT_SRC_IP: Ipv6Addr =
@@ -354,9 +365,8 @@
#[test]
fn test_parse() {
- let bytes = fixed_hdr_to_bytes(new_fixed_hdr());
- let (packet, body_range) = Ipv6Packet::parse(&bytes[..]).unwrap();
- assert_eq!(body_range, 40..40);
+ let mut buf = &fixed_hdr_to_bytes(new_fixed_hdr())[..];
+ let packet = buf.parse::<Ipv6Packet<_>>().unwrap();
assert_eq!(packet.ds(), 0);
assert_eq!(packet.ecn(), 2);
assert_eq!(packet.flowlabel(), 0x77);
@@ -373,7 +383,9 @@
let mut fixed_hdr = new_fixed_hdr();
fixed_hdr.version_tc_flowlabel[0] = 0x50;
assert_eq!(
- Ipv6Packet::parse(&fixed_hdr_to_bytes(fixed_hdr)[..]).unwrap_err(),
+ (&fixed_hdr_to_bytes(fixed_hdr)[..])
+ .parse::<Ipv6Packet<_>>()
+ .unwrap_err(),
ParseError::Format
);
@@ -381,34 +393,30 @@
let mut fixed_hdr = new_fixed_hdr();
NetworkEndian::write_u16(&mut fixed_hdr.payload_len[..], 2);
assert_eq!(
- Ipv6Packet::parse(&fixed_hdr_to_bytes(fixed_hdr)[..]).unwrap_err(),
+ (&fixed_hdr_to_bytes(fixed_hdr)[..])
+ .parse::<Ipv6Packet<_>>()
+ .unwrap_err(),
ParseError::Format
);
}
- // Return a stock Ipv6PacketSerializer with reasonable default values.
- fn new_serializer() -> Ipv6PacketSerializer {
- Ipv6PacketSerializer::new(DEFAULT_SRC_IP, DEFAULT_DST_IP, 64, IpProto::Tcp)
+ // Return a stock Ipv6PacketBuilder with reasonable default values.
+ fn new_builder() -> Ipv6PacketBuilder {
+ Ipv6PacketBuilder::new(DEFAULT_SRC_IP, DEFAULT_DST_IP, 64, IpProto::Tcp)
}
#[test]
fn test_serialize() {
- let mut buf = [0; 50];
- let mut serializer = new_serializer();
- serializer.ds(0x12);
- serializer.ecn(3);
- serializer.flowlabel(0x10405);
- {
- // set the body
- (&mut buf[40..]).copy_from_slice(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
- let mut buffer = BufferAndRange::new_from(&mut buf[..], 40..);
- serializer.serialize(&mut buffer);
- assert_eq!(buffer.range(), 0..50);
- }
-
+ let mut builder = new_builder();
+ builder.ds(0x12);
+ builder.ecn(3);
+ builder.flowlabel(0x10405);
+ let mut buf = (&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+ .encapsulate(builder)
+ .serialize_outer();
// assert that we get the literal bytes we expected
assert_eq!(
- &buf[..],
+ buf.as_ref(),
&[
100, 177, 4, 5, 0, 10, 6, 64, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 0, 1, 2, 3, 4,
@@ -416,10 +424,9 @@
][..],
);
- let (packet, body_range) = Ipv6Packet::parse(&buf[..]).unwrap();
+ let packet = buf.parse::<Ipv6Packet<_>>().unwrap();
// assert that when we parse those bytes, we get the values we set in
- // the serializer
- assert_eq!(body_range, 40..50);
+ // the builder
assert_eq!(packet.ds(), 0x12);
assert_eq!(packet.ecn(), 3);
assert_eq!(packet.flowlabel(), 0x10405);
@@ -427,30 +434,26 @@
#[test]
fn test_serialize_zeroes() {
- // Test that Ipv6PacketSerializer::serialize properly zeroes memory before
+ // Test that Ipv6PacketBuilder::serialize properly zeroes memory before
// serializing the header.
let mut buf_0 = [0; 40];
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut buf_0[..], 40..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_0[..], 40..))
+ .encapsulate(new_builder())
+ .serialize_outer();
let mut buf_1 = [0xFF; 40];
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut buf_1[..], 40..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_1[..], 40..))
+ .encapsulate(new_builder())
+ .serialize_outer();
assert_eq!(&buf_0[..], &buf_1[..]);
}
#[test]
#[should_panic]
fn test_serialize_panic_packet_length() {
- // Test that a packet which is longer than 2^16 - 1 bytes is rejected.
- new_serializer().serialize(&mut BufferAndRange::new_from(
- &mut [0; (1 << 16) + 40][..],
- 40..,
- ));
- }
-
- #[test]
- #[should_panic]
- fn test_serialize_panic_insufficient_header_space() {
- // Test that a body range which doesn't leave enough room for the header
- // is rejected.
- new_serializer().serialize(&mut BufferAndRange::new_from(&mut [0u8; 40][..], ..));
+ // Test that a packet whose payload is longer than 2^16 - 1 bytes is
+ // rejected.
+ BufferSerializer::new_vec(Buf::new(&mut [0; 1 << 16][..], ..))
+ .encapsulate(new_builder())
+ .serialize_outer();
}
}
diff --git a/bin/recovery_netstack/core/src/wire/mod.rs b/bin/recovery_netstack/core/src/wire/mod.rs
index 3ba9bf0..a2a100c 100644
--- a/bin/recovery_netstack/core/src/wire/mod.rs
+++ b/bin/recovery_netstack/core/src/wire/mod.rs
@@ -61,7 +61,3 @@
mod testdata;
pub mod udp;
mod util;
-
-pub use self::ethernet::*;
-pub use self::udp::*;
-pub use self::util::{BufferAndRange, InnerSerializationRequest, SerializationRequest};
diff --git a/bin/recovery_netstack/core/src/wire/tcp.rs b/bin/recovery_netstack/core/src/wire/tcp.rs
index 50485e0..47457d3 100644
--- a/bin/recovery_netstack/core/src/wire/tcp.rs
+++ b/bin/recovery_netstack/core/src/wire/tcp.rs
@@ -7,17 +7,17 @@
#[cfg(test)]
use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroU16;
-use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{
+ BufferView, BufferViewMut, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer,
+};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use crate::error::ParseError;
use crate::ip::{Ip, IpAddr, IpProto};
use crate::transport::tcp::TcpOption;
-use crate::wire::util::{
- fits_in_u16, fits_in_u32, BufferAndRange, Checksum, Options, PacketSerializer,
-};
+use crate::wire::util::{fits_in_u16, fits_in_u32, Checksum, Options};
use self::options::TcpOptionImpl;
@@ -78,7 +78,7 @@
/// necessary.
///
/// A `TcpSegment` - whether parsed using `parse` or created using
-/// `TcpSegmentSerializer` - maintains the invariant that the checksum is always
+/// `TcpSegmentBuilder` - maintains the invariant that the checksum is always
/// valid.
pub struct TcpSegment<B> {
hdr_prefix: LayoutVerified<B, HeaderPrefix>,
@@ -86,38 +86,55 @@
body: B,
}
-impl<B: ByteSlice> TcpSegment<B> {
- /// Parse a TCP segment.
- ///
- /// `parse` parses `bytes` as a TCP segment 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.
- pub fn parse<A: IpAddr>(
- bytes: B, src_ip: A, dst_ip: A,
- ) -> Result<(TcpSegment<B>, Range<usize>), ParseError> {
+/// Arguments required to parse a TCP segment.
+pub struct TcpParseArgs<A: IpAddr> {
+ src_ip: A,
+ dst_ip: A,
+}
+
+impl<A: IpAddr> TcpParseArgs<A> {
+ /// Construct a new `TcpParseArgs`.
+ pub fn new(src_ip: A, dst_ip: A) -> TcpParseArgs<A> {
+ TcpParseArgs { src_ip, dst_ip }
+ }
+}
+
+impl<B: ByteSlice, A: IpAddr> ParsablePacket<B, TcpParseArgs<A>> for TcpSegment<B> {
+ type Error = ParseError;
+
+ fn parse_metadata(&self) -> ParseMetadata {
+ let header_len = self.hdr_prefix.bytes().len() + self.options.bytes().len();
+ ParseMetadata::from_packet(header_len, self.body.len(), 0)
+ }
+
+ fn parse<BV: BufferView<B>>(mut buffer: BV, args: TcpParseArgs<A>) -> Result<Self, ParseError> {
// See for details: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_segment_structure
- let (hdr_prefix, rest) = LayoutVerified::<B, HeaderPrefix>::new_from_prefix(bytes)
+ let hdr_prefix = buffer
+ .take_obj_front::<HeaderPrefix>()
.ok_or_else(debug_err_fn!(
ParseError::Format,
"too few bytes for header"
))?;
let hdr_bytes = (hdr_prefix.data_offset() * 4) as usize;
- if hdr_bytes > hdr_prefix.bytes().len() + rest.len() || hdr_bytes < hdr_prefix.bytes().len()
- {
+ if hdr_bytes < hdr_prefix.bytes().len() {
return debug_err!(
Err(ParseError::Format),
"invalid data offset: {}",
hdr_prefix.data_offset()
);
}
- let (options, body) = rest.split_at(hdr_bytes - hdr_prefix.bytes().len());
+ let options = buffer
+ .take_front(hdr_bytes - hdr_prefix.bytes().len())
+ .ok_or_else(debug_err_fn!(
+ ParseError::Format,
+ "data offset larger than buffer"
+ ))?;
let options = Options::parse(options).map_err(|_| ParseError::Format)?;
let segment = TcpSegment {
hdr_prefix,
options,
- body,
+ body: buffer.into_rest(),
};
if segment.hdr_prefix.src_port() == 0 || segment.hdr_prefix.dst_port() == 0 {
@@ -126,18 +143,17 @@
let checksum = NetworkEndian::read_u16(&segment.hdr_prefix.checksum);
if segment
- .compute_checksum(src_ip, dst_ip)
+ .compute_checksum(args.src_ip, args.dst_ip)
.ok_or_else(debug_err_fn!(ParseError::Format, "segment too large"))?
!= checksum
{
return debug_err!(Err(ParseError::Checksum), "invalid checksum");
}
-
- let hdr_len = segment.hdr_prefix.bytes().len() + segment.options.bytes().len();
- let total_len = hdr_len + segment.body.len();
- Ok((segment, hdr_len..total_len))
+ Ok(segment)
}
+}
+impl<B: ByteSlice> TcpSegment<B> {
/// Iterate over the TCP header options.
pub fn iter_options<'a>(&'a self) -> impl 'a + Iterator<Item = TcpOption> {
self.options.iter()
@@ -244,9 +260,9 @@
self.header_len() + self.body.len()
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer<A: IpAddr>(&self, src_ip: A, dst_ip: A) -> TcpSegmentSerializer<A> {
- let mut s = TcpSegmentSerializer {
+ /// Construct a builder with the same contents as this packet.
+ pub fn builder<A: IpAddr>(&self, src_ip: A, dst_ip: A) -> TcpSegmentBuilder<A> {
+ let mut s = TcpSegmentBuilder {
src_ip,
dst_ip,
src_port: self.src_port().get(),
@@ -265,12 +281,12 @@
// NOTE(joshlf): In order to ensure that the checksum is always valid, we don't
// expose any setters for the fields of the TCP segment; the only way to set
-// them is via TcpSegmentSerializer. This, combined with checksum validation
+// them is via TcpSegmentBuilder. This, combined with checksum validation
// performed in TcpSegment::parse, provides the invariant that a UdpPacket
// always has a valid checksum.
-/// A serializer for TCP segments.
-pub struct TcpSegmentSerializer<A: IpAddr> {
+/// A builder for TCP segments.
+pub struct TcpSegmentBuilder<A: IpAddr> {
src_ip: A,
dst_ip: A,
src_port: u16,
@@ -281,16 +297,16 @@
window_size: u16,
}
-impl<A: IpAddr> TcpSegmentSerializer<A> {
- /// Construct a new `TcpSegmentSerializer`.
+impl<A: IpAddr> TcpSegmentBuilder<A> {
+ /// Construct a new `TcpSegmentBuilder`.
///
/// If `ack_num` is `Some`, then the ACK flag will be set.
pub fn new(
src_ip: A, dst_ip: A, src_port: NonZeroU16, dst_port: NonZeroU16, seq_num: u32,
ack_num: Option<u32>, window_size: u16,
- ) -> TcpSegmentSerializer<A> {
+ ) -> TcpSegmentBuilder<A> {
let flags = if ack_num.is_some() { 1 << 4 } else { 0 };
- TcpSegmentSerializer {
+ TcpSegmentBuilder {
src_ip,
dst_ip,
src_port: src_port.get(),
@@ -329,62 +345,62 @@
const MAX_HEADER_BYTES: usize = 60;
const MIN_HEADER_BYTES: usize = 20;
-impl<A: IpAddr> PacketSerializer for TcpSegmentSerializer<A> {
- fn max_header_bytes(&self) -> usize {
- MAX_HEADER_BYTES
- }
-
- fn min_header_bytes(&self) -> usize {
+impl<A: IpAddr> PacketBuilder for TcpSegmentBuilder<A> {
+ fn header_len(&self) -> usize {
MIN_HEADER_BYTES
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
- let extend_backwards = {
- let (header, body, _) = buffer.parts_mut();
- // create a 0-byte slice for the options since we don't support
- // serializing options yet (NET-955)
- let (options, body) = body.split_at_mut(0);
- // SECURITY: Use _zeroed constructor to ensure we zero memory to prevent
- // leaking information from packets previously stored in this buffer.
- let (_, hdr_prefix) =
- LayoutVerified::<_, HeaderPrefix>::new_unaligned_from_suffix_zeroed(header)
- .expect("too few bytes for TCP header");
- let options =
- Options::parse(options).expect("parsing an empty options slice should not fail");
- let mut segment = TcpSegment {
- hdr_prefix,
- options,
- body,
- };
+ fn min_body_len(&self) -> usize {
+ 0
+ }
- NetworkEndian::write_u16(&mut segment.hdr_prefix.src_port, self.src_port);
- NetworkEndian::write_u16(&mut segment.hdr_prefix.dst_port, self.dst_port);
- NetworkEndian::write_u32(&mut segment.hdr_prefix.seq_num, self.seq_num);
- NetworkEndian::write_u32(&mut segment.hdr_prefix.ack, self.ack_num);
- // Data Offset is hard-coded to 5 until we support serializing options
- NetworkEndian::write_u16(
- &mut segment.hdr_prefix.data_offset_reserved_flags,
- (5u16 << 12) | self.flags,
- );
- NetworkEndian::write_u16(&mut segment.hdr_prefix.window_size, self.window_size);
- // we don't support setting the Urgent Pointer
- NetworkEndian::write_u16(&mut segment.hdr_prefix.urg_ptr, 0);
+ fn footer_len(&self) -> usize {
+ 0
+ }
- let segment_len = segment.total_segment_len();
- // 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 checksum = segment
- .compute_checksum(self.src_ip, self.dst_ip)
- .expect(&format!(
- "total TCP segment length of {} bytes overflows length field of pseudo-header",
- segment_len
- ));
- NetworkEndian::write_u16(&mut segment.hdr_prefix.checksum, checksum);
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
+ let (mut header, body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut header = &mut header;
- segment.header_len()
+ // SECURITY: Use _zero constructor to ensure we zero memory to prevent
+ // leaking information from packets previously stored in this buffer.
+ let hdr_prefix = header
+ .take_obj_front_zero::<HeaderPrefix>()
+ .expect("too few bytes for TCP header");
+ // create a 0-byte slice for the options since we don't support
+ // serializing options yet (NET-955)
+ let options =
+ Options::parse(&mut [][..]).expect("parsing an empty options slice should not fail");
+ let mut segment = TcpSegment {
+ hdr_prefix,
+ options,
+ body,
};
- buffer.extend_backwards(extend_backwards);
+ NetworkEndian::write_u16(&mut segment.hdr_prefix.src_port, self.src_port);
+ NetworkEndian::write_u16(&mut segment.hdr_prefix.dst_port, self.dst_port);
+ NetworkEndian::write_u32(&mut segment.hdr_prefix.seq_num, self.seq_num);
+ NetworkEndian::write_u32(&mut segment.hdr_prefix.ack, self.ack_num);
+ // Data Offset is hard-coded to 5 until we support serializing options
+ NetworkEndian::write_u16(
+ &mut segment.hdr_prefix.data_offset_reserved_flags,
+ (5u16 << 12) | self.flags,
+ );
+ NetworkEndian::write_u16(&mut segment.hdr_prefix.window_size, self.window_size);
+ // we don't support setting the Urgent Pointer
+ NetworkEndian::write_u16(&mut segment.hdr_prefix.urg_ptr, 0);
+
+ let segment_len = segment.total_segment_len();
+ // 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 checksum = segment
+ .compute_checksum(self.src_ip, self.dst_ip)
+ .expect(&format!(
+ "total TCP segment length of {} bytes overflows length field of pseudo-header",
+ segment_len
+ ));
+ NetworkEndian::write_u16(&mut segment.hdr_prefix.checksum, checksum);
}
}
@@ -494,12 +510,13 @@
#[cfg(test)]
mod tests {
+ use packet::{Buf, BufferSerializer, ParseBuffer, Serializer};
+
use super::*;
use crate::device::ethernet::EtherType;
use crate::ip::{IpProto, Ipv4Addr, Ipv6Addr};
use crate::wire::ethernet::EthernetFrame;
use crate::wire::ipv4::Ipv4Packet;
- use crate::wire::util::SerializationRequest;
const TEST_SRC_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const TEST_DST_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
@@ -513,14 +530,14 @@
fn test_parse_serialize_full() {
use crate::wire::testdata::tls_client_hello::*;
- let (frame, body_range) = EthernetFrame::parse(ETHERNET_FRAME_BYTES).unwrap();
- assert_eq!(body_range, ETHERNET_BODY_RANGE);
+ let mut buf = ÐERNET_FRAME_BYTES[..];
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
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);
+ let mut body = frame.body();
+ let packet = body.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(packet.proto(), Ok(IpProto::Tcp));
assert_eq!(packet.dscp(), IP_DSCP);
assert_eq!(packet.ecn(), IP_ECN);
@@ -532,9 +549,10 @@
assert_eq!(packet.src_ip(), IP_SRC_IP);
assert_eq!(packet.dst_ip(), IP_DST_IP);
- let (segment, body_range) =
- TcpSegment::parse(packet.body(), packet.src_ip(), packet.dst_ip()).unwrap();
- assert_eq!(body_range, TCP_BODY_RANGE);
+ let mut body = packet.body();
+ let segment = body
+ .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(packet.src_ip(), packet.dst_ip()))
+ .unwrap();
assert_eq!(segment.src_port().get(), TCP_SRC_PORT);
assert_eq!(segment.dst_port().get(), TCP_DST_PORT);
assert_eq!(segment.ack_num().is_some(), TCP_ACK_FLAG);
@@ -548,9 +566,9 @@
// TODO(joshlf): Uncomment once we support serializing options
// let buffer = segment.body()
- // .encapsulate(segment.serializer(packet.src_ip(), packet.dst_ip()))
- // .encapsulate(packet.serializer())
- // .encapsulate(frame.serializer())
+ // .encapsulate(segment.builder(packet.src_ip(), packet.dst_ip()))
+ // .encapsulate(packet.builder())
+ // .encapsulate(frame.builder())
// .serialize_outer();
// assert_eq!(buffer.as_ref(), ETHERNET_FRAME_BYTES);
}
@@ -579,10 +597,10 @@
#[test]
fn test_parse() {
- let bytes = hdr_prefix_to_bytes(new_hdr_prefix());
- let (segment, body_range) =
- TcpSegment::parse(&bytes[..], TEST_SRC_IPV4, TEST_DST_IPV4).unwrap();
- assert_eq!(body_range, 20..20);
+ let mut buf = &hdr_prefix_to_bytes(new_hdr_prefix())[..];
+ let segment = buf
+ .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .unwrap();
assert_eq!(segment.src_port().get(), 1);
assert_eq!(segment.dst_port().get(), 2);
assert_eq!(segment.body(), []);
@@ -590,64 +608,42 @@
#[test]
fn test_parse_error() {
+ // Assert that parsing a particular header prefix results in an error.
+ fn assert_header_err(hdr_prefix: HeaderPrefix, err: ParseError) {
+ let mut buf = &mut hdr_prefix_to_bytes(hdr_prefix)[..];
+ assert_eq!(
+ buf.parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .unwrap_err(),
+ err
+ );
+ }
+
// Set the source port to 0, which is illegal.
let mut hdr_prefix = new_hdr_prefix();
hdr_prefix.src_port = [0, 0];
- assert_eq!(
- TcpSegment::parse(
- &hdr_prefix_to_bytes(hdr_prefix)[..],
- TEST_SRC_IPV4,
- TEST_DST_IPV4
- )
- .unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(hdr_prefix, ParseError::Format);
// Set the destination port to 0, which is illegal.
let mut hdr_prefix = new_hdr_prefix();
hdr_prefix.dst_port = [0, 0];
- assert_eq!(
- TcpSegment::parse(
- &hdr_prefix_to_bytes(hdr_prefix)[..],
- TEST_SRC_IPV4,
- TEST_DST_IPV4
- )
- .unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(hdr_prefix, ParseError::Format);
// Set the data offset to 4, implying a header length of 16. This is
// smaller than the minimum of 20.
let mut hdr_prefix = new_hdr_prefix();
NetworkEndian::write_u16(&mut hdr_prefix.data_offset_reserved_flags, 4u16 << 12);
- assert_eq!(
- TcpSegment::parse(
- &hdr_prefix_to_bytes(hdr_prefix)[..],
- TEST_SRC_IPV4,
- TEST_DST_IPV4
- )
- .unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(hdr_prefix, ParseError::Format);
// Set the data offset to 6, implying a header length of 24. This is
// larger than the actual segment length of 20.
let mut hdr_prefix = new_hdr_prefix();
NetworkEndian::write_u16(&mut hdr_prefix.data_offset_reserved_flags, 6u16 << 12);
- assert_eq!(
- TcpSegment::parse(
- &hdr_prefix_to_bytes(hdr_prefix)[..],
- TEST_SRC_IPV4,
- TEST_DST_IPV4
- )
- .unwrap_err(),
- ParseError::Format
- );
+ assert_header_err(hdr_prefix, ParseError::Format);
}
- // Return a stock TcpSegmentSerializer with reasonable default values.
- fn new_serializer<A: IpAddr>(src_ip: A, dst_ip: A) -> TcpSegmentSerializer<A> {
- TcpSegmentSerializer::new(
+ // Return a stock TcpSegmentBuilder with reasonable default values.
+ fn new_builder<A: IpAddr>(src_ip: A, dst_ip: A) -> TcpSegmentBuilder<A> {
+ TcpSegmentBuilder::new(
src_ip,
dst_ip,
NonZeroU16::new(1).unwrap(),
@@ -660,32 +656,27 @@
#[test]
fn test_serialize() {
- let mut buf = [0; 30];
- let mut serializer = new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4);
- serializer.fin(true);
- serializer.rst(true);
- serializer.syn(true);
- {
- // set the body
- (&mut buf[20..]).copy_from_slice(&[0, 1, 2, 3, 3, 4, 5, 7, 8, 9]);
- let mut buffer = BufferAndRange::new_from(&mut buf[..], 20..);
- serializer.serialize(&mut buffer);
- assert_eq!(buffer.range(), 0..30);
- }
+ let mut builder = new_builder(TEST_SRC_IPV4, TEST_DST_IPV4);
+ builder.fin(true);
+ builder.rst(true);
+ builder.syn(true);
+ let mut buf = (&[0, 1, 2, 3, 3, 4, 5, 7, 8, 9])
+ .encapsulate(builder)
+ .serialize_outer();
// assert that we get the literal bytes we expected
assert_eq!(
- buf,
+ buf.as_ref(),
[
0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 80, 23, 0, 5, 141, 137, 0, 0, 0, 1, 2, 3, 3, 4,
5, 7, 8, 9
]
);
- let (segment, body_range) =
- TcpSegment::parse(&buf[..], TEST_SRC_IPV4, TEST_DST_IPV4).unwrap();
+ let segment = buf
+ .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .unwrap();
// assert that when we parse those bytes, we get the values we set in
- // the serializer
- assert_eq!(body_range, 20..30);
+ // the builder
assert_eq!(segment.src_port().get(), 1);
assert_eq!(segment.dst_port().get(), 2);
assert_eq!(segment.seq_num(), 3);
@@ -696,33 +687,17 @@
#[test]
fn test_serialize_zeroes() {
- // Test that TcpSegmentSerializer::serialize properly zeroes memory before
+ // Test that TcpSegmentBuilder::serialize properly zeroes memory before
// serializing the header.
let mut buf_0 = [0; 20];
- new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4)
- .serialize(&mut BufferAndRange::new_from(&mut buf_0[..], 20..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_0[..], 20..))
+ .encapsulate(new_builder(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .serialize_outer();
let mut buf_1 = [0xFF; 20];
- new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4)
- .serialize(&mut BufferAndRange::new_from(&mut buf_1[..], 20..));
- assert_eq!(buf_0, buf_1);
- }
-
- #[test]
- #[should_panic]
- fn test_serialize_panic_body_range() {
- // Test that a body range which is out of bounds of the buffer is
- // rejected.
- new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4)
- .serialize(&mut BufferAndRange::new_from(&mut [0; 20][..], ..21));
- }
-
- #[test]
- #[should_panic]
- fn test_serialize_panic_insufficient_header_space() {
- // Test that a body range which doesn't leave enough room for the header
- // is rejected.
- new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4)
- .serialize(&mut BufferAndRange::new_from(&mut [0; 20][..], ..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_1[..], 20..))
+ .encapsulate(new_builder(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .serialize_outer();
+ assert_eq!(&buf_0[..], &buf_1[..]);
}
#[test]
@@ -730,8 +705,9 @@
fn test_serialize_panic_segment_too_long_ipv4() {
// Test that a segment length which overflows u16 is rejected because it
// can't fit in the length field in the IPv4 pseudo-header.
- new_serializer(TEST_SRC_IPV4, TEST_DST_IPV4)
- .serialize(&mut BufferAndRange::new_from(&mut [0; 1 << 16][..], 20..));
+ BufferSerializer::new_vec(Buf::new(&mut [0; (1 << 16) - 20][..], ..))
+ .encapsulate(new_builder(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .serialize_outer();
}
#[test]
@@ -741,7 +717,8 @@
fn test_serialize_panic_segment_too_long_ipv6() {
// Test that a segment length which overflows u32 is rejected because it
// can't fit in the length field in the IPv4 pseudo-header.
- new_serializer(TEST_SRC_IPV6, TEST_DST_IPV6)
- .serialize(&mut BufferAndRange::new_from(&mut [0; 1 << 32][..], 20..));
+ BufferSerializer::new_vec(Buf::new(&mut [0; (1 << 32) - 20][..], ..))
+ .encapsulate(new_builder(TEST_SRC_IPV6, TEST_DST_IPV6))
+ .serialize_outer();
}
}
diff --git a/bin/recovery_netstack/core/src/wire/udp.rs b/bin/recovery_netstack/core/src/wire/udp.rs
index 3148863..812f8f8 100644
--- a/bin/recovery_netstack/core/src/wire/udp.rs
+++ b/bin/recovery_netstack/core/src/wire/udp.rs
@@ -7,14 +7,16 @@
#[cfg(test)]
use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroU16;
-use std::ops::Range;
use byteorder::{ByteOrder, NetworkEndian};
+use packet::{
+ BufferView, BufferViewMut, PacketBuilder, ParsablePacket, ParseMetadata, SerializeBuffer,
+};
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};
use crate::error::ParseError;
use crate::ip::{Ip, IpAddr, IpProto};
-use crate::wire::util::{fits_in_u16, fits_in_u32, BufferAndRange, Checksum, PacketSerializer};
+use crate::wire::util::{fits_in_u16, fits_in_u32, 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
@@ -85,44 +87,48 @@
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> {
+/// Arguments required to parse a UDP packet.
+pub struct UdpParseArgs<A: IpAddr> {
+ src_ip: A,
+ dst_ip: A,
+}
+
+impl<A: IpAddr> UdpParseArgs<A> {
+ /// Construct a new `UdpParseArgs`.
+ pub fn new(src_ip: A, dst_ip: A) -> UdpParseArgs<A> {
+ UdpParseArgs { src_ip, dst_ip }
+ }
+}
+
+impl<B: ByteSlice, A: IpAddr> ParsablePacket<B, UdpParseArgs<A>> for UdpPacket<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: UdpParseArgs<A>) -> Result<Self, 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_else(
- debug_err_fn!(ParseError::Format, "too few bytes for header"),
- )?;
- let packet = UdpPacket { header, body };
+ let buf_len = buffer.len();
+ let header = buffer.take_obj_front::<Header>().ok_or_else(debug_err_fn!(
+ ParseError::Format,
+ "too few bytes for header"
+ ))?;
+ let packet = UdpPacket {
+ header,
+ body: buffer.into_rest(),
+ };
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
+ buf_len
} else {
packet.header.length() as usize
};
- if len != bytes_len {
+ if len != buf_len {
return debug_err!(
Err(ParseError::Format),
"length in header does not match packet length"
@@ -143,7 +149,7 @@
NetworkEndian::read_u16(&packet.header.checksum)
};
if packet
- .compute_checksum(src_ip, dst_ip)
+ .compute_checksum(args.src_ip, args.dst_ip)
.ok_or_else(debug_err_fn!(ParseError::Format, "segment too large"))?
!= target
{
@@ -152,10 +158,7 @@
} else if A::Version::VERSION.is_v6() {
return debug_err!(Err(ParseError::Format), "missing checksum");
}
-
- let hdr_len = packet.header.bytes().len();
- let total_len = hdr_len + packet.body.len();
- Ok((packet, hdr_len..total_len))
+ Ok(packet)
}
}
@@ -229,9 +232,9 @@
self.header_len() + self.body.len()
}
- /// Construct a serializer with the same contents as this packet.
- pub fn serializer<A: IpAddr>(&self, src_ip: A, dst_ip: A) -> UdpPacketSerializer<A> {
- UdpPacketSerializer {
+ /// Construct a builder with the same contents as this packet.
+ pub fn builder<A: IpAddr>(&self, src_ip: A, dst_ip: A) -> UdpPacketBuilder<A> {
+ UdpPacketBuilder {
src_ip,
dst_ip,
src_port: self.src_port(),
@@ -242,24 +245,24 @@
// 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 UdpPacketSerializer::serialize. This, combined with checksum validation
+// is via UdpPacketBuilder::serialize. This, combined with checksum validation
// performed in UdpPacket::parse, provides the invariant that a UdpPacket always
// has a valid checksum.
-/// A serializer for UDP packets.
-pub struct UdpPacketSerializer<A: IpAddr> {
+/// A builder for UDP packets.
+pub struct UdpPacketBuilder<A: IpAddr> {
src_ip: A,
dst_ip: A,
src_port: Option<NonZeroU16>,
dst_port: NonZeroU16,
}
-impl<A: IpAddr> UdpPacketSerializer<A> {
- /// Construct a new `UdpPacketSerializer`.
+impl<A: IpAddr> UdpPacketBuilder<A> {
+ /// Construct a new `UdpPacketBuilder`.
pub fn new(
src_ip: A, dst_ip: A, src_port: Option<NonZeroU16>, dst_port: NonZeroU16,
- ) -> UdpPacketSerializer<A> {
- UdpPacketSerializer {
+ ) -> UdpPacketBuilder<A> {
+ UdpPacketBuilder {
src_ip,
dst_ip,
src_port,
@@ -270,71 +273,71 @@
const HEADER_BYTES: usize = 8;
-impl<A: IpAddr> PacketSerializer for UdpPacketSerializer<A> {
- fn max_header_bytes(&self) -> usize {
+impl<A: IpAddr> PacketBuilder for UdpPacketBuilder<A> {
+ fn header_len(&self) -> usize {
HEADER_BYTES
}
- fn min_header_bytes(&self) -> usize {
- HEADER_BYTES
+ fn min_body_len(&self) -> usize {
+ 0
}
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>) {
+ fn footer_len(&self) -> usize {
+ 0
+ }
+
+ fn serialize<'a>(self, mut buffer: SerializeBuffer<'a>) {
// 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 };
+ let (mut header, body, _) = buffer.parts();
+ // implements BufferViewMut, giving us take_obj_xxx_zero methods
+ let mut header = &mut header;
- packet
- .header
- .set_src_port(self.src_port.map(|port| port.get()).unwrap_or(0));
- packet.header.set_dst_port(self.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);
+ // SECURITY: Use _zero constructor to ensure we zero memory to prevent
+ // leaking information from packets previously stored in this buffer.
+ let header = header
+ .take_obj_front_zero::<Header>()
+ .expect("too few bytes for UDP header");
+ let mut packet = UdpPacket { header, body };
- // 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(self.src_ip, self.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
+ .set_src_port(self.src_port.map(|port| port.get()).unwrap_or(0));
+ packet.header.set_dst_port(self.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
);
-
- packet.header_len()
};
+ NetworkEndian::write_u16(&mut packet.header.length, len_field);
- buffer.extend_backwards(extend_backwards);
+ // 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(self.src_ip, self.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
+ },
+ );
}
}
@@ -348,13 +351,13 @@
#[cfg(test)]
mod tests {
+ use packet::{Buf, BufferSerializer, ParseBuffer, Serializer};
+
+ use super::*;
use crate::device::ethernet::EtherType;
use crate::ip::{Ipv4Addr, Ipv6Addr};
use crate::wire::ethernet::EthernetFrame;
use crate::wire::ipv4::Ipv4Packet;
- use crate::wire::util::SerializationRequest;
-
- use super::*;
const TEST_SRC_IPV4: Ipv4Addr = Ipv4Addr::new([1, 2, 3, 4]);
const TEST_DST_IPV4: Ipv4Addr = Ipv4Addr::new([5, 6, 7, 8]);
@@ -368,14 +371,14 @@
fn test_parse_serialize_full() {
use crate::wire::testdata::dns_request::*;
- let (frame, body_range) = EthernetFrame::parse(ETHERNET_FRAME_BYTES).unwrap();
- assert_eq!(body_range, ETHERNET_BODY_RANGE);
+ let mut buf = ÐERNET_FRAME_BYTES[..];
+ let frame = buf.parse::<EthernetFrame<_>>().unwrap();
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 (ip_packet, body_range) = Ipv4Packet::parse(frame.body()).unwrap();
- assert_eq!(body_range, IP_BODY_RANGE);
+ let mut body = frame.body();
+ let ip_packet = body.parse::<Ipv4Packet<_>>().unwrap();
assert_eq!(ip_packet.proto(), Ok(IpProto::Udp));
assert_eq!(ip_packet.dscp(), IP_DSCP);
assert_eq!(ip_packet.ecn(), IP_ECN);
@@ -387,9 +390,13 @@
assert_eq!(ip_packet.src_ip(), IP_SRC_IP);
assert_eq!(ip_packet.dst_ip(), IP_DST_IP);
- let (udp_packet, body_range) =
- UdpPacket::parse(ip_packet.body(), ip_packet.src_ip(), ip_packet.dst_ip()).unwrap();
- assert_eq!(body_range, UDP_BODY_RANGE);
+ let mut body = ip_packet.body();
+ let udp_packet = body
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(
+ ip_packet.src_ip(),
+ ip_packet.dst_ip(),
+ ))
+ .unwrap();
assert_eq!(
udp_packet.src_port().map(|p| p.get()).unwrap_or(0),
UDP_SRC_PORT
@@ -399,9 +406,9 @@
let buffer = udp_packet
.body()
- .encapsulate(udp_packet.serializer(ip_packet.src_ip(), ip_packet.dst_ip()))
- .encapsulate(ip_packet.serializer())
- .encapsulate(frame.serializer())
+ .encapsulate(udp_packet.builder(ip_packet.src_ip(), ip_packet.dst_ip()))
+ .encapsulate(ip_packet.builder())
+ .encapsulate(frame.builder())
.serialize_outer();
assert_eq!(buffer.as_ref(), ETHERNET_FRAME_BYTES);
}
@@ -409,20 +416,20 @@
#[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);
+ let mut buf = &[0, 0, 1, 2, 0, 8, 0, 0][..];
+ let packet = buf
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .unwrap();
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);
+ let mut buf = &[0, 0, 1, 2, 0, 0, 0xFD, 0xD3][..];
+ let packet = buf
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(TEST_SRC_IPV6, TEST_DST_IPV6))
+ .unwrap();
assert!(packet.src_port().is_none());
assert_eq!(packet.dst_port().get(), NetworkEndian::read_u16(&[1, 2]));
assert!(packet.checksummed());
@@ -431,23 +438,20 @@
#[test]
fn test_serialize() {
- let mut buf = [0; 8];
- {
- let mut buffer = BufferAndRange::new_from(&mut buf, 8..);
- UdpPacketSerializer::new(
+ let mut buf = (&[])
+ .encapsulate(UdpPacketBuilder::new(
TEST_SRC_IPV4,
TEST_DST_IPV4,
NonZeroU16::new(1),
NonZeroU16::new(2).unwrap(),
- )
- .serialize(&mut buffer);
- 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();
+ ))
+ .serialize_outer();
+ assert_eq!(buf.as_ref(), [0, 1, 0, 2, 0, 8, 239, 199]);
+ let packet = buf
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(TEST_SRC_IPV4, TEST_DST_IPV4))
+ .unwrap();
// assert that when we parse those bytes, we get the values we set in
- // the serializer
+ // the builder
assert_eq!(packet.src_port().unwrap().get(), 1);
assert_eq!(packet.dst_port().get(), 2);
assert!(packet.checksummed());
@@ -458,27 +462,29 @@
// Test that UdpPacket::serialize properly zeroes memory before serializing
// the header.
let mut buf_0 = [0; 8];
- UdpPacketSerializer::new(
- TEST_SRC_IPV4,
- TEST_DST_IPV4,
- NonZeroU16::new(1),
- NonZeroU16::new(2).unwrap(),
- )
- .serialize(&mut BufferAndRange::new_from(&mut buf_0[..], 8..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_0[..], 8..))
+ .encapsulate(UdpPacketBuilder::new(
+ TEST_SRC_IPV4,
+ TEST_DST_IPV4,
+ NonZeroU16::new(1),
+ NonZeroU16::new(2).unwrap(),
+ ))
+ .serialize_outer();
let mut buf_1 = [0xFF; 8];
- UdpPacketSerializer::new(
- TEST_SRC_IPV4,
- TEST_DST_IPV4,
- NonZeroU16::new(1),
- NonZeroU16::new(2).unwrap(),
- )
- .serialize(&mut BufferAndRange::new_from(&mut buf_1[..], 8..));
+ BufferSerializer::new_vec(Buf::new(&mut buf_1[..], 8..))
+ .encapsulate(UdpPacketBuilder::new(
+ TEST_SRC_IPV4,
+ TEST_DST_IPV4,
+ NonZeroU16::new(1),
+ NonZeroU16::new(2).unwrap(),
+ ))
+ .serialize_outer();
assert_eq!(buf_0, buf_1);
}
#[test]
fn test_parse_fail() {
- // Test that while a given byte pattern optionally succeeds, zeroing out
+ // Test thact 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) {
@@ -486,12 +492,20 @@
// 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());
+ let mut buf = &buf[..];
+ assert!(buf
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(src, dst))
+ .is_ok());
}
for idx in zero {
buf[*idx] = 0;
}
- assert_eq!(UdpPacket::parse(&buf[..], src, dst).unwrap_err(), err);
+ let mut buf = &buf[..];
+ assert_eq!(
+ buf.parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(src, dst))
+ .unwrap_err(),
+ err
+ );
}
// destination port of 0 is disallowed
@@ -521,7 +535,9 @@
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(),
+ (&buf[..])
+ .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(TEST_SRC_IPV6, TEST_DST_IPV6))
+ .unwrap_err(),
ParseError::Format
);
}
@@ -530,43 +546,46 @@
#[test]
#[should_panic]
fn test_serialize_fail_header_too_short() {
- let mut buf = [0; 7];
- UdpPacketSerializer::new(
+ UdpPacketBuilder::new(
TEST_SRC_IPV4,
TEST_DST_IPV4,
None,
NonZeroU16::new(1).unwrap(),
)
- .serialize(&mut BufferAndRange::new_from(&mut buf[..], 7..));
+ .serialize(SerializeBuffer::new(&mut [0; 7][..], ..));
}
#[test]
#[should_panic]
fn test_serialize_fail_packet_too_long_ipv4() {
- let mut buf = [0; 1 << 16];
- UdpPacketSerializer::new(
- TEST_SRC_IPV4,
- TEST_DST_IPV4,
- None,
- NonZeroU16::new(1).unwrap(),
- )
- .serialize(&mut BufferAndRange::new_from(&mut buf[..], 8..));
+ (&[0; (1 << 16) - 8][..])
+ .encapsulate(UdpPacketBuilder::new(
+ TEST_SRC_IPV4,
+ TEST_DST_IPV4,
+ None,
+ NonZeroU16::new(1).unwrap(),
+ ))
+ .serialize_outer();
}
- // This test tries to allocate 4GB of memory. Run at your own risk.
- #[test]
- #[should_panic]
- #[ignore]
- #[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];
- UdpPacketSerializer::new(
- TEST_SRC_IPV4,
- TEST_DST_IPV4,
- None,
- NonZeroU16::new(1).unwrap(),
- )
- .serialize(&mut BufferAndRange::new_from(&mut buf[..], 8..));
- }
+ // TODO(joshlf): Figure out why compiling this test (yes, just compiling!)
+ // hangs the compiler.
+
+ // // This test tries to allocate 4GB of memory. Run at your own risk.
+ // #[test]
+ // #[should_panic]
+ // #[ignore]
+ // #[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];
+ // (&[0u8; (1 << 32) - 8])
+ // .encapsulate(UdpPacketBuilder::new(
+ // TEST_SRC_IPV4,
+ // TEST_DST_IPV4,
+ // None,
+ // NonZeroU16::new(1).unwrap(),
+ // ))
+ // .serialize_outer();
+ // }
}
diff --git a/bin/recovery_netstack/core/src/wire/util.rs b/bin/recovery_netstack/core/src/wire/util.rs
index 960409b..8360161 100644
--- a/bin/recovery_netstack/core/src/wire/util.rs
+++ b/bin/recovery_netstack/core/src/wire/util.rs
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-pub use self::buffer::*;
pub use self::checksum::*;
pub use self::options::*;
@@ -642,888 +641,3 @@
}
}
}
-
-mod buffer {
- use std::cmp;
- use std::convert::TryFrom;
- use std::ops::{Bound, Range, RangeBounds};
-
- // return the inclusive equivalent of the bound
- fn canonicalize_lower_bound(bound: Bound<&usize>) -> usize {
- match bound {
- Bound::Included(x) => *x,
- Bound::Excluded(x) => *x + 1,
- Bound::Unbounded => 0,
- }
- }
-
- // return the exclusive equivalent of the bound, verifying that it is in
- // range of len
- fn canonicalize_upper_bound(len: usize, bound: Bound<&usize>) -> Option<usize> {
- let bound = match bound {
- Bound::Included(x) => *x + 1,
- Bound::Excluded(x) => *x,
- Bound::Unbounded => len,
- };
- if bound > len {
- return None;
- }
- Some(bound)
- }
-
- // return the inclusive-exclusive equivalent of the bound, verifying that it
- // is in range of len, and panicking if it is not or if the range is
- // nonsensical
- fn canonicalize_range_infallible<R: RangeBounds<usize>>(len: usize, range: &R) -> Range<usize> {
- let lower = canonicalize_lower_bound(range.start_bound());
- let upper = canonicalize_upper_bound(len, range.end_bound()).expect("range out of bounds");
- assert!(lower <= upper, "invalid range");
- lower..upper
- }
-
- /// A serializer for non-encapsulating packets.
- ///
- /// `InnerPacketSerializer` is a serializer for packets which do not
- /// themselves encapsulate other packets.
- pub trait InnerPacketSerializer {
- /// The number of bytes required to serialize this packet.
- fn size(&self) -> usize;
-
- /// Serialize a packet in an existing buffer.
- ///
- /// `serialize` serializes a packet into `buffer` from the present
- /// configuration. It serializes starting at the beginning of
- /// `buffer.range()`, and expects the range to be empty.
- ///
- /// `serialize` updates the buffer's range to be equal to the bytes of
- /// the newly-serialized packet. This range can be used to indicate the
- /// range for encapsulation in another packet.
- ///
- /// # Panics
- ///
- /// `serialize` panics if `buffer.range()` is non-empty, or if the
- /// number of bytes following the range is less than `self.size()`.
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>);
- }
-
- // TODO(joshlf): Since {max,min}_{header,footer}_bytes are methods on a
- // PacketSerializer, perhaps we can just require that the PacketSerializer
- // know exactly how many bytes will be required, and collapse these from
- // four to two methods? Currently, the justification is that the difference
- // in bytes is small, and so performing the dynamic calculation may be more
- // expensive than allocating a few too many bytes, but that assumption may
- // be worth revisiting.
-
- /// A serializer for encapsulating packets.
- ///
- /// `PacketSerializer` is a serializer for packets which encapsulate other
- /// packets.
- pub trait PacketSerializer {
- /// The maximum number of pre-body bytes consumed by all headers.
- ///
- /// By providing at least `max_header_bytes` bytes preceding the body,
- /// the caller can ensure that a call to `serialize` will not panic.
- /// Note that the actual number of bytes consumed may be less than this.
- fn max_header_bytes(&self) -> usize {
- 0
- }
-
- /// The minimum number of pre-body bytes consumed by all headers.
- ///
- /// `min_header_bytes` returns the minimum number of bytes which are
- /// guaranteed to be consumed by all pre-body headers. Note that the
- /// actual number of bytes consumed may be more than this.
- fn min_header_bytes(&self) -> usize {
- 0
- }
-
- /// The minimum size of the body and padding bytes combined.
- ///
- /// Some packet formats have minimum length requirements. In order to
- /// satisfy these requirements, any bodies smaller than a certain
- /// minimum must be followed by padding bytes.
- /// `min_body_and_padding_bytes` returns the minimum number of bytes
- /// which must be consumed by the body and post-body padding.
- ///
- /// If a body of fewer bytes than this minimum is to be serialized using
- /// `serialize`, the caller must provide space for enough padding bytes
- /// to make up the difference.
- fn min_body_and_padding_bytes(&self) -> usize {
- 0
- }
-
- /// The maximum number of post-body, post-padding bytes consumed by all
- /// footers.
- ///
- /// `max_footer_bytes` returns the number of bytes which must be present
- /// after all body bytes and all post-body padding bytes in order to
- /// guarantee enough room to serialize footers. Note that the actual
- /// number of bytes consumed may be less than this.
- fn max_footer_bytes(&self) -> usize {
- 0
- }
-
- /// The minimum number of post-body, post-padding bytes consumed by all
- /// footers.
- ///
- /// `min_footer_bytes` returns the minimum number of bytes which are
- /// guaranteed to be consumed by all post-body, post-padding footers.
- /// Note that the actual number of bytes consumed may be more than this.
- fn min_footer_bytes(&self) -> usize {
- 0
- }
-
- /// Serialize a packet in an existing buffer.
- ///
- /// `serialize` serializes a packet into `buffer`, initializing any
- /// headers and footers from the present configuration. It treats
- /// `buffer.range()` as the packet body. It uses the last bytes before
- /// the body to store any headers. It leaves padding as necessary
- /// following the body, and serializes any footers immediately following
- /// the padding (if any).
- ///
- /// `serialize` updates the buffer's range to be equal to the bytes of
- /// the newly-serialized packet (including any headers, padding, and
- /// footers). This range can be used to indicate the range for
- /// encapsulation in another packet.
- ///
- /// # Panics
- ///
- /// `serialize` may panics
- /// - there are fewer than `max_header_bytes` bytes preceding the body
- /// - there are fewer than `max_footer_bytes` bytes following the body
- /// - the sum of the body bytes and post-body bytes is less than the sum
- /// of `min_body_and_padding_bytes` and `max_footer_bytes` (in other
- /// words, the minimum body and padding byte requirement is not met)
- fn serialize<B: AsRef<[u8]> + AsMut<[u8]>>(self, buffer: &mut BufferAndRange<B>);
- }
-
- // TODO(joshlf): Document common patterns with SerializationRequests,
- // especially using an existing BufferAndRange to forward a just-parsed
- // packet (this pattern is particularly subtle).
-
- /// A request to serialize a payload.
- ///
- /// A `SerializationRequest` is a request to serialize a packet.
- /// `SerializationRequest`s can be fulfilled either by serializing the
- /// packet, or by creating a new `SerializationRequest` which represents
- /// encapsulating the original packet in another packet, and then satisfying
- /// the resulting request. `SerializationRequest`s handle all of the logic
- /// of determining and satisfying header, footer, and padding requirements.
- ///
- /// `SerializationRequest` is implemented by the following types:
- /// - A `BufferAndRange` represents a request to serialize the buffer's
- /// range. If a `BufferAndRange` is encapsulated, its range will be used
- /// as the payload, and the rest of the buffer will be used for the
- /// headers, footers, and padding of the encapsulating packets.
- /// - An `InnerSerializationRequest` represents a request to serialize an
- /// innermost packet - one which doesn't encapsulate any other packets.
- /// - An `EncapsulatingSerializationRequest` represents a request to
- /// serialize a packet which itself encapsulates the packet requested by
- /// another, nested `SerializationRequest`.
- pub trait SerializationRequest: Sized {
- type Buffer: AsRef<[u8]> + AsMut<[u8]>;
-
- /// Serialize a packet, fulfilling this request.
- ///
- /// `serialize` serializes this request into a buffer, and returns the
- /// buffer so that encapsulating packets may serialize their headers,
- /// footers, and padding. The returned buffer's range represents the
- /// bytes that have been serialized and should be encapsulated by any
- /// lower layers. The returned buffer is guaranteed to satisfy the
- /// following requirements:
- /// - There are at least `header_bytes` bytes preceding the range
- /// - There are at least `footer_bytes` bytes following the range
- /// - The range and the bytes following it are at least
- /// `min_body_and_padding_bytes + footer_bytes` in length
- fn serialize(
- self, header_bytes: usize, min_body_and_padding_bytes: usize, footer_bytes: usize,
- ) -> BufferAndRange<Self::Buffer>;
-
- /// Serialize an outermost packet, fulfilling this request.
- ///
- /// `serialize_outer` is like `serialize`, except that the returned
- /// buffer doesn't make any guarantees about how many bytes precede or
- /// follow the range. It is intended to be called only when the returned
- /// packet is not going to be further encapsulated.
- fn serialize_outer(self) -> BufferAndRange<Self::Buffer> {
- self.serialize(0, 0, 0)
- }
-
- /// Construct a new request to encapsulate this packet in another one.
- ///
- /// `encapsulate` consumes this request, and returns a new request
- /// representing the encapsulation of this packet in another one.
- /// `serializer` is a `PacketSerializer` which will be used to serialize
- /// the encapsulating packet.
- fn encapsulate<S: PacketSerializer>(
- self, serializer: S,
- ) -> EncapsulatingSerializationRequest<S, Self> {
- EncapsulatingSerializationRequest {
- serializer,
- inner: self,
- }
- }
- }
-
- impl<I: InnerPacketSerializer> SerializationRequest for I {
- type Buffer = Vec<u8>;
-
- fn serialize(
- self, header_bytes: usize, min_body_and_padding_bytes: usize, footer_bytes: usize,
- ) -> BufferAndRange<Vec<u8>> {
- InnerSerializationRequest::new(self).serialize(
- header_bytes,
- min_body_and_padding_bytes,
- footer_bytes,
- )
- }
- }
-
- impl<'a> SerializationRequest for &'a [u8] {
- type Buffer = Vec<u8>;
-
- fn serialize(
- self, header_bytes: usize, min_body_and_padding_bytes: usize, footer_bytes: usize,
- ) -> BufferAndRange<Vec<u8>> {
- // First use BufferAndRange::ensure_prefix_suffix_padding to either
- // tell us that the current slice satisfies the constraints, or
- // allocate a new, satisfying Vec for us.
- let mut buffer = BufferAndRange::new_from(self, ..);
- buffer.ensure_prefix_suffix_padding(
- header_bytes,
- footer_bytes,
- min_body_and_padding_bytes,
- );
-
- // Next, either duplicate the slice (so we have something mutable)
- // or use the existing Vec.
- let range = buffer.range();
- let mut v = match buffer.buffer {
- RefOrOwned::Owned(v) => v,
- RefOrOwned::Ref(r) => r.to_vec(),
- };
-
- BufferAndRange::new_from(v, range).serialize(
- header_bytes,
- min_body_and_padding_bytes,
- footer_bytes,
- )
- }
- }
-
- /// A `SerializationRequest` for to serialize an inner packet.
- ///
- /// `InnerSerializationRequest` contains an `InnerPacketSerializer` and a
- /// `BufferAndRange` and implements `SerializationRequest` by serializing
- /// the serializer into the buffer.
- pub struct InnerSerializationRequest<S: InnerPacketSerializer, B> {
- serializer: S,
- buffer: BufferAndRange<B>,
- }
-
- impl<S: InnerPacketSerializer, B> InnerSerializationRequest<S, B>
- where
- B: AsRef<[u8]>,
- {
- /// Construct a new `InnerSerializationRequest` from a serializer and a
- /// buffer.
- pub fn new_with_buffer(serializer: S, buffer: B) -> InnerSerializationRequest<S, B> {
- InnerSerializationRequest {
- serializer,
- buffer: BufferAndRange::new_from(buffer, 0..0),
- }
- }
- }
-
- impl<S: InnerPacketSerializer> InnerSerializationRequest<S, Vec<u8>> {
- /// Construct a new `InnerSerializationRequest` from a serializer,
- /// allocating a new buffer.
- pub fn new(serializer: S) -> InnerSerializationRequest<S, Vec<u8>> {
- InnerSerializationRequest {
- serializer,
- buffer: BufferAndRange::new_from(vec![], ..),
- }
- }
- }
-
- impl<S: InnerPacketSerializer, B> SerializationRequest for InnerSerializationRequest<S, B>
- where
- B: AsRef<[u8]> + AsMut<[u8]>,
- {
- type Buffer = B;
-
- fn serialize(
- self, header_bytes: usize, min_body_and_padding_bytes: usize, footer_bytes: usize,
- ) -> BufferAndRange<B> {
- let InnerSerializationRequest {
- serializer,
- mut buffer,
- } = self;
- // Reset the buffer as required by InnerPacketSerializer::serialize.
- buffer.range = 0..0;
- // Ensure there's enough room for the packet itself.
- let min_body_and_padding_bytes =
- cmp::max(min_body_and_padding_bytes, serializer.size());
- buffer.ensure_prefix_suffix_padding(
- header_bytes,
- footer_bytes,
- min_body_and_padding_bytes,
- );
- serializer.serialize(&mut buffer);
- buffer
- }
- }
-
- /// A `SerializationRequest` to encapsulate a packet in another packet.
- ///
- /// `EncapsulatingSerializationRequest`s can be constructed from existing
- /// `SerializationRequest`s using the `encapsulate` method.
- ///
- /// # Padding
- ///
- /// If the `PacketSerializer` used to construct this request specifies a
- /// minimum body length requirement, and the encapsulated packet is not
- /// large enough to satisfy that requirement, then padding will
- /// automatically be added (and zeroed for security).
- pub struct EncapsulatingSerializationRequest<S: PacketSerializer, R: SerializationRequest> {
- serializer: S,
- inner: R,
- }
-
- impl<S: PacketSerializer, R: SerializationRequest> SerializationRequest
- for EncapsulatingSerializationRequest<S, R>
- {
- type Buffer = R::Buffer;
-
- fn serialize(
- self, mut header_bytes: usize, min_body_and_padding_bytes: usize,
- mut footer_bytes: usize,
- ) -> BufferAndRange<R::Buffer> {
- header_bytes += self.serializer.max_header_bytes();
- footer_bytes += self.serializer.max_footer_bytes();
-
- // The number required by this layer.
- let this_min_body = self.serializer.min_body_and_padding_bytes();
- // The number required by the next outer layer, taking into account
- // that at least min_header_bytes + min_footer_bytes will be
- // consumed by this layer.
- let next_min_body = min_body_and_padding_bytes
- .checked_sub(
- self.serializer.min_header_bytes() + self.serializer.min_footer_bytes(),
- )
- .unwrap_or(0);
-
- let EncapsulatingSerializationRequest { serializer, inner } = self;
- let mut buffer = inner.serialize(
- header_bytes,
- footer_bytes,
- cmp::max(this_min_body, next_min_body),
- );
-
- let body_len = buffer.range().len();
- if body_len < this_min_body {
- // The body itself isn't large enough to satisfy the minimum
- // body length requirement, so we add padding. This is only
- // valid if the length requirement comes from this layer - if it
- // comes from a lower layer, then there are other encapsulating
- // packets which need to be serialized before the padding is
- // added, and that layer's call to serialize will run this code
- // block instead.
-
- // This is guaranteed to succeed so long as inner.serialize
- // satisfies its contract.
- //
- // SECURITY: Use _zero to ensure we zero padding bytes to
- // prevent leaking information from packets previously stored in
- // this buffer.
- buffer.extend_forwards_zero(this_min_body - body_len);
- }
-
- serializer.serialize(&mut buffer);
- buffer
- }
- }
-
- /// A buffer and a range into that buffer.
- ///
- /// A `BufferAndRange` stores a pair of a buffer and a range which
- /// represents a subset of the buffer. It implements `AsRef<[u8]>` and
- /// `AsMut<[u8]>` for the range of the buffer.
- ///
- /// `BufferAndRange` is useful for passing nested payloads up the stack
- /// while still maintaining access to the entire buffer in case it is needed
- /// again in the future, such as to serialize new packets.
- pub struct BufferAndRange<B> {
- buffer: RefOrOwned<B>,
- range: Range<usize>,
- }
-
- impl<B> BufferAndRange<B>
- where
- B: AsRef<[u8]>,
- {
- /// Construct a new `BufferAndRange` from an existing buffer.
- ///
- /// # Panics
- ///
- /// `new_from` panics if `range` is out of bounds of `buffer` or is
- /// nonsensical (i.e., the upper bound precedes the lower bound).
- pub fn new_from<R: RangeBounds<usize>>(buffer: B, range: R) -> BufferAndRange<B> {
- let len = buffer.as_ref().len();
- BufferAndRange {
- buffer: RefOrOwned::Ref(buffer),
- range: canonicalize_range_infallible(len, &range),
- }
- }
-
- /// Extend the end of the range forwards towards the end of the buffer.
- ///
- /// `extend_forwards` adds `bytes` to the end index of the buffer's
- /// range, resulting in the range being `bytes` bytes closer to the end
- /// of the buffer than it was before.
- ///
- /// # Panics
- ///
- /// `extend_forwards` panics if there are fewer than `bytes` bytes
- /// following the existing range.
- pub fn extend_forwards(&mut self, bytes: usize) {
- assert!(
- bytes <= self.buffer.as_ref().len() - self.range.end,
- "cannot extend range with {} following bytes forwards by {} bytes",
- self.buffer.as_ref().len() - self.range.end,
- bytes
- );
- self.range.end += bytes;
- }
-
- /// Ensure that this `BufferAndRange` satisfies certain prefix, suffix,
- /// and padding size requirements.
- ///
- /// `ensure_prefix_suffix_padding` ensures that this `BufferAndRange`
- /// has at least `prefix` bytes preceding the range, at least `suffix`
- /// bytes following the range, and at least `range_plus_padding +
- /// suffix` bytes in the range plus any bytes following the range. If it
- /// already satisfies these constraints, then it is left unchanged.
- /// Otherwise, a new buffer is allocated, the original range bytes are
- /// copied into the new buffer, and the range is adjusted so that it
- /// matches the location of the bytes in the new buffer.
- ///
- /// The "range plus padding" construction is useful when a packet format
- /// requires a minimum body length, and the body which is being
- /// encapsulated does not meet that minimum. In that case, it is
- /// necessary to add extra padding bytes after the body in order to meet
- /// the minimum.
- fn ensure_prefix_suffix_padding(
- &mut self, prefix: usize, suffix: usize, range_plus_padding: usize,
- ) {
- let range_len = self.range.end - self.range.start;
- let post_range_len = self.buffer.as_ref().len() - self.range.end;
- // normalize to guarantee that range_plus_padding >= range_len
- let range_plus_padding = cmp::max(range_plus_padding, range_len);
- if prefix > self.range.start
- || suffix < post_range_len
- || range_len + post_range_len < range_plus_padding + suffix
- {
- // TODO(joshlf): Right now, we split the world into two cases -
- // either the constraints aren't satisfied and so we need to
- // reallocate, or they are, so we don't need to do anything. In
- // fact, there's a third case, in which the constraints aren't
- // satisfied, but the buffer is large enough to satisfy the
- // constraints. In that case, we can avoid reallocating by
- // simply moving the range within the existing buffer.
-
- // The constraints aren't satisfied, and the buffer isn't large
- // enough to satisfy the constraints, so we have to reallocate.
-
- let padding = range_plus_padding - range_len;
- let total_len = prefix + range_len + padding + suffix;
- let mut vec = vec![0; total_len];
- vec[prefix..prefix + range_len]
- .copy_from_slice(slice(self.buffer.as_ref(), &self.range));
- *self = BufferAndRange {
- buffer: RefOrOwned::Owned(vec),
- range: prefix..prefix + range_len,
- }
- }
- }
- }
-
- impl<B> BufferAndRange<B> {
- /// Shrink the buffer range.
- ///
- /// `slice` shrinks the buffer's range to be equal to the provided
- /// range. It interprets `range` as relative to the current range. For
- /// example, if, in a 10-byte buffer, the current range is `[2, 8)`, and
- /// the `range` argument is `[2, 6)`, then after `slice` returns, the
- /// beginning of the buffer's range will be `2 + 2 = 4`. Since `range`
- /// has a length of 4, the end of the buffer's range will be `4 + 4 =
- /// 8`.
- ///
- /// # Examples
- ///
- /// ```rust,ignore
- /// # // TODO(joshlf): Make this compile and remove the ignore
- /// let buf = [0; 10];
- /// let mut buf = BufferAndRange::new_from(&buf, 2..8);
- /// assert_eq!(buf.as_ref().len(), 6);
- /// buf.slice(2..6);
- /// assert_eq!(buf.as_ref().len(), 4);
- /// ```
- ///
- /// # Panics
- ///
- /// `slice` panics if `range` is out of bounds for the existing buffer
- /// range, or if it nonsensical (i.e., the upper bound precedes the
- /// lower bound).
- pub fn slice<R: RangeBounds<usize>>(&mut self, range: R) {
- let cur_range_len = self.range.end - self.range.start;
- let range = canonicalize_range_infallible(cur_range_len, &range);
- self.range = translate_range(&range, isize::try_from(self.range.start).unwrap());
- }
-
- /// Extend the beginning of the range backwards towards the beginning of
- /// the buffer.
- ///
- /// `extend_backwards` subtracts `bytes` from the beginning index of the
- /// buffer's range, resulting in the range being `bytes` bytes closer to
- /// the beginning of the buffer than it was before.
- ///
- /// # Panics
- ///
- /// `extend_backwards` panics if there are fewer than `bytes` bytes
- /// preceding the existing range.
- pub fn extend_backwards(&mut self, bytes: usize) {
- assert!(
- bytes <= self.range.start,
- "cannot extend range starting at {} backwards by {} bytes",
- self.range.start,
- bytes
- );
- self.range.start -= bytes;
- }
-
- /// Get the range.
- pub fn range(&self) -> Range<usize> {
- self.range.clone()
- }
- }
-
- impl<B> BufferAndRange<B>
- where
- B: AsMut<[u8]>,
- {
- /// Extract the prefix, range, and suffix from the buffer.
- ///
- /// `parts_mut` returns the region of the buffer preceding the range,
- /// the range itself, and the region of the buffer following the range.
- pub fn parts_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) {
- let (prefix, rest) = (&mut self.buffer.as_mut()[..]).split_at_mut(self.range.start);
- let (mid, suffix) = rest.split_at_mut(self.range.end - self.range.start);
- (prefix, mid, suffix)
- }
- }
-
- impl<B> BufferAndRange<B>
- where
- B: AsRef<[u8]> + AsMut<[u8]>,
- {
- /// Extend the end of the range forwards towards the end of the buffer,
- /// zeroing the newly-included bytes.
- ///
- /// `extend_forwards_zero` adds `bytes` to the end index of the buffer's
- /// range, resulting in the range being `bytes` bytes closer to the end
- /// of the buffer than it was before. These new bytes are set to zero,
- /// which can be useful when extending a body to include padding which
- /// has not yet been zeroed.
- ///
- /// # Panics
- ///
- /// `extend_forwards_zero` panics if there are fewer than `bytes` bytes
- /// following the existing range.
- fn extend_forwards_zero(&mut self, bytes: usize) {
- self.extend_forwards(bytes);
- let slice = self.as_mut();
- let len = slice.len();
- zero(&mut slice[len - bytes..]);
- }
- }
-
- impl<B: AsRef<[u8]> + AsMut<[u8]>> SerializationRequest for BufferAndRange<B> {
- type Buffer = B;
-
- /// Serialize a packet, fulfilling this request.
- ///
- /// `serialize` ensures that this buffer satisfies the header, padding,
- /// and footer requirements using `ensure_prefix_suffix_padding`, and
- /// then returns it. The buffer's range is left in tact, and thus will
- /// be treated as the payload to be encapsulated by any encapsulating
- /// packets.
- fn serialize(
- mut self, header_bytes: usize, min_body_and_padding_bytes: usize, footer_bytes: usize,
- ) -> BufferAndRange<B> {
- self.ensure_prefix_suffix_padding(
- header_bytes,
- footer_bytes,
- min_body_and_padding_bytes,
- );
- self
- }
- }
-
- impl<B> AsRef<[u8]> for BufferAndRange<B>
- where
- B: AsRef<[u8]>,
- {
- fn as_ref(&self) -> &[u8] {
- &self.buffer.as_ref()[self.range.clone()]
- }
- }
-
- impl<B> AsMut<[u8]> for BufferAndRange<B>
- where
- B: AsMut<[u8]>,
- {
- fn as_mut(&mut self) -> &mut [u8] {
- &mut self.buffer.as_mut()[self.range.clone()]
- }
- }
-
- /// Either a reference or an owned allocated buffer.
- enum RefOrOwned<B> {
- Ref(B),
- Owned(Vec<u8>),
- }
-
- impl<B: AsRef<[u8]>> AsRef<[u8]> for RefOrOwned<B> {
- fn as_ref(&self) -> &[u8] {
- match self {
- RefOrOwned::Ref(ref r) => r.as_ref(),
- RefOrOwned::Owned(ref v) => v.as_slice(),
- }
- }
- }
-
- impl<B: AsMut<[u8]>> AsMut<[u8]> for RefOrOwned<B> {
- fn as_mut(&mut self) -> &mut [u8] {
- match self {
- RefOrOwned::Ref(ref mut r) => r.as_mut(),
- RefOrOwned::Owned(ref mut v) => v.as_mut_slice(),
- }
- }
- }
-
- /// Zero a slice.
- ///
- /// Set every element of `slice` to 0.
- fn zero(slice: &mut [u8]) {
- for s in slice.iter_mut() {
- *s = 0;
- }
- }
-
- /// Translate a `Range<usize>` left or right.
- ///
- /// Translate a `Range<usize>` by a fixed offset. This function is
- /// equivalent to the following code, except with overflow explicitly
- /// checked:
- ///
- /// ```rust,ignore
- /// # // TODO(joshlf): Make this compile and remove the ignore
- /// Range {
- /// start: ((range.start as isize) + offset) as usize,
- /// end: ((range.end as isize) + offset) as usize,
- /// }
- /// ```
- ///
- /// # Panics
- ///
- /// `translate_range` panics if any addition overflows or any conversion
- /// between signed and unsigned types fails.
- fn translate_range(range: &Range<usize>, offset: isize) -> Range<usize> {
- let start = isize::try_from(range.start).unwrap();
- let end = isize::try_from(range.end).unwrap();
- Range {
- start: usize::try_from(start.checked_add(offset).unwrap()).unwrap(),
- end: usize::try_from(end.checked_add(offset).unwrap()).unwrap(),
- }
- }
-
- /// Get an immutable slice from a range.
- ///
- /// This is a temporary replacement for the syntax `&slc[range]` until this
- /// [issue] is fixed.
- ///
- /// [issue]: https://github.com/rust-lang/rust/issues/35729#issuecomment-394200339
- fn slice<'a, T, R: RangeBounds<usize>>(slc: &'a [T], range: &R) -> &'a [T] {
- let len = slc.len();
- &slc[canonicalize_range_infallible(len, range)]
- }
-
- #[cfg(test)]
- mod tests {
- use super::*;
-
- #[test]
- fn test_buffer_and_range_slice() {
- let mut buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- let mut buf = BufferAndRange::new_from(&mut buf, ..);
- assert_eq!(buf.range(), 0..10);
- assert_eq!(buf.as_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [][..],
- &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..],
- &mut [][..]
- )
- );
-
- buf.slice(..);
- assert_eq!(buf.range(), 0..10);
- assert_eq!(buf.as_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [][..],
- &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..],
- &mut [][..]
- )
- );
-
- buf.slice(2..);
- assert_eq!(buf.range(), 2..10);
- assert_eq!(buf.as_ref(), [2, 3, 4, 5, 6, 7, 8, 9]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [0, 1][..],
- &mut [2, 3, 4, 5, 6, 7, 8, 9][..],
- &mut [][..]
- )
- );
-
- buf.slice(..8);
- assert_eq!(buf.range(), 2..10);
- assert_eq!(buf.as_ref(), [2, 3, 4, 5, 6, 7, 8, 9]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [0, 1][..],
- &mut [2, 3, 4, 5, 6, 7, 8, 9][..],
- &mut [][..]
- )
- );
-
- buf.slice(..6);
- assert_eq!(buf.range(), 2..8);
- assert_eq!(buf.as_ref(), [2, 3, 4, 5, 6, 7]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [0, 1][..],
- &mut [2, 3, 4, 5, 6, 7][..],
- &mut [8, 9][..]
- )
- );
-
- buf.slice(2..4);
- assert_eq!(buf.range(), 4..6);
- assert_eq!(buf.as_ref(), [4, 5]);
- assert_eq!(
- buf.parts_mut(),
- (
- &mut [0, 1, 2, 3][..],
- &mut [4, 5][..],
- &mut [6, 7, 8, 9][..]
- )
- );
- }
-
- #[test]
- fn test_buffer_and_range_extend_backwards() {
- let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- let mut buf = BufferAndRange::new_from(&buf, 2..8);
- assert_eq!(buf.range(), 2..8);
- assert_eq!(buf.as_ref(), [2, 3, 4, 5, 6, 7]);
- buf.extend_backwards(1);
- assert_eq!(buf.range(), 1..8);
- assert_eq!(buf.as_ref(), [1, 2, 3, 4, 5, 6, 7]);
- buf.extend_backwards(1);
- assert_eq!(buf.range(), 0..8);
- assert_eq!(buf.as_ref(), [0, 1, 2, 3, 4, 5, 6, 7]);
- }
-
- #[test]
- #[should_panic]
- fn test_buffer_and_range_extend_backwards_panics() {
- let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- let mut buf = BufferAndRange::new_from(&buf, 2..8);
- assert_eq!(buf.as_ref(), [2, 3, 4, 5, 6, 7]);
- buf.extend_backwards(1);
- assert_eq!(buf.as_ref(), [1, 2, 3, 4, 5, 6, 7]);
- buf.extend_backwards(2);
- }
-
- #[test]
- fn test_ensure_prefix_suffix_padding() {
- fn verify<B: AsRef<[u8]> + AsMut<[u8]>>(
- mut buffer: BufferAndRange<B>, prefix: usize, suffix: usize,
- range_plus_padding: usize,
- ) {
- let range_len_old = {
- let range = buffer.range();
- range.end - range.start
- };
- let mut range_old = Vec::with_capacity(range_len_old);
- range_old.extend_from_slice(buffer.as_ref());
-
- buffer.ensure_prefix_suffix_padding(prefix, suffix, range_plus_padding);
- let range_len_new = {
- let range = buffer.range();
- range.end - range.start
- };
- assert_eq!(range_len_old, range_len_new);
- let (pfx, range, sfx) = buffer.parts_mut();
- assert!(pfx.len() >= prefix);
- assert_eq!(range.len(), range_len_new);
- assert!(sfx.len() >= suffix);
- assert_eq!(range_old.as_slice(), range);
- assert!(range.len() + sfx.len() >= (range_plus_padding + suffix));
- }
-
- // Test for every valid combination of buf_len, range_start,
- // range_end, prefix, suffix, and range_plus_padding within [0, 8).
- for buf_len in 0..8 {
- for range_start in 0..buf_len {
- for range_end in range_start..buf_len {
- for prefix in 0..8 {
- for suffix in 0..8 {
- for range_plus_padding in 0..8 {
- let mut vec = Vec::with_capacity(buf_len);
- vec.resize(buf_len, 0);
- // Initialize the vector with values 0, 1, 2,
- // ... so that we can check to make sure that
- // the range bytes have been properly copied if
- // the buffer is reallocated.
- for i in 0..vec.len() {
- vec[i] = i as u8;
- }
- verify(
- BufferAndRange::new_from(
- vec.as_mut_slice(),
- range_start..range_end,
- ),
- prefix,
- suffix,
- range_plus_padding,
- );
- }
- }
- }
- }
- }
- }
- }
- }
-}