support hardware based checksum settings in during packet send/recv

- makes sure the checksum is zeroed when not emitted by software
  (This is required by some implementations such as STM32 to work properly)
v0.7.x
Steffen Butzer 2017-10-02 12:47:51 +02:00 committed by whitequark
parent f9093d15aa
commit 61cb64406d
10 changed files with 189 additions and 72 deletions

View File

@ -10,6 +10,7 @@ mod utils;
use std::str::FromStr;
use std::time::Instant;
use std::os::unix::io::AsRawFd;
use smoltcp::phy::Device;
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress,
Ipv4Address, Ipv4Packet, Ipv4Repr,
@ -56,6 +57,7 @@ fn main() {
raw_rx_buffer, raw_tx_buffer);
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let caps = device.capabilities();
let mut iface = EthernetInterface::new(
Box::new(device), Box::new(arp_cache) as Box<ArpCache>,
hardware_addr, [IpAddress::from(local_addr)]);
@ -98,9 +100,9 @@ fn main() {
.unwrap();
let mut ipv4_packet = Ipv4Packet::new(raw_payload);
ipv4_repr.emit(&mut ipv4_packet);
ipv4_repr.emit(&mut ipv4_packet, &caps.checksum);
let mut icmp_packet = Icmpv4Packet::new(ipv4_packet.payload_mut());
icmp_repr.emit(&mut icmp_packet);
icmp_repr.emit(&mut icmp_packet, &caps.checksum);
waiting_queue.insert(seq_no, timestamp);
seq_no += 1;
@ -110,11 +112,11 @@ fn main() {
if socket.can_recv() {
let payload = socket.recv().unwrap();
let ipv4_packet = Ipv4Packet::new(payload);
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet).unwrap();
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &caps.checksum).unwrap();
if ipv4_repr.src_addr == remote_addr && ipv4_repr.dst_addr == local_addr {
let icmp_packet = Icmpv4Packet::new(ipv4_packet.payload());
let icmp_repr = Icmpv4Repr::parse(&icmp_packet);
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &caps.checksum);
if let Ok(Icmpv4Repr::EchoReply { seq_no, data, .. }) = icmp_repr {
if let Some(_) = waiting_queue.get(&seq_no) {

View File

@ -181,7 +181,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
socket.dispatch(|response| {
device_result = self.dispatch(timestamp, Packet::Raw(response));
device_result
}),
}, &caps.checksum),
#[cfg(feature = "socket-udp")]
&mut Socket::Udp(ref mut socket) =>
socket.dispatch(|response| {
@ -190,7 +190,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}),
#[cfg(feature = "socket-tcp")]
&mut Socket::Tcp(ref mut socket) =>
socket.dispatch(timestamp, &limits, |response| {
socket.dispatch(timestamp, &caps, |response| {
device_result = self.dispatch(timestamp, Packet::Tcp(response));
device_result
}),
@ -278,7 +278,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
eth_frame: &EthernetFrame<&'frame T>) ->
Result<Packet<'frame>> {
let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?;
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet)?;
let checksum_caps = self.device.capabilities().checksum;
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?;
if !ipv4_repr.src_addr.is_unicast() {
// Discard packets with non-unicast source addresses.
@ -304,7 +305,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
<Socket as AsSocket<RawSocket>>::try_as_socket) {
if !raw_socket.accepts(&ip_repr) { continue }
match raw_socket.process(&ip_repr, ip_payload) {
match raw_socket.process(&ip_repr, ip_payload, &checksum_caps) {
// The packet is valid and handled by socket.
Ok(()) => handled_by_raw_socket = true,
// The socket buffer is full.
@ -321,15 +322,15 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
match ipv4_repr.protocol {
IpProtocol::Icmp =>
Self::process_icmpv4(ipv4_repr, ip_payload),
self.process_icmpv4(ipv4_repr, ip_payload),
#[cfg(feature = "socket-udp")]
IpProtocol::Udp =>
Self::process_udp(sockets, ip_repr, ip_payload),
self.process_udp(sockets, ip_repr, ip_payload),
#[cfg(feature = "socket-tcp")]
IpProtocol::Tcp =>
Self::process_tcp(sockets, _timestamp, ip_repr, ip_payload),
self.process_tcp(sockets, _timestamp, ip_repr, ip_payload),
#[cfg(feature = "socket-raw")]
_ if handled_by_raw_socket =>
@ -352,10 +353,11 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
}
fn process_icmpv4<'frame>(ipv4_repr: Ipv4Repr, ip_payload: &'frame [u8]) ->
Result<Packet<'frame>> {
fn process_icmpv4<'frame>(&self, ipv4_repr: Ipv4Repr,
ip_payload: &'frame [u8]) -> Result<Packet<'frame>> {
let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?;
let icmp_repr = Icmpv4Repr::parse(&icmp_packet)?;
let checksum_caps = self.device.capabilities().checksum;
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &checksum_caps)?;
match icmp_repr {
// Respond to echo requests.
@ -383,12 +385,13 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
#[cfg(feature = "socket-udp")]
fn process_udp<'frame>(sockets: &mut SocketSet,
fn process_udp<'frame>(&self, sockets: &mut SocketSet,
ip_repr: IpRepr, ip_payload: &'frame [u8]) ->
Result<Packet<'frame>> {
let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
let udp_packet = UdpPacket::new_checked(ip_payload)?;
let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr)?;
let checksum_caps = self.device.capabilities().checksum;
let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &checksum_caps)?;
for udp_socket in sockets.iter_mut().filter_map(
<Socket as AsSocket<UdpSocket>>::try_as_socket) {
@ -425,12 +428,13 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
#[cfg(feature = "socket-tcp")]
fn process_tcp<'frame>(sockets: &mut SocketSet, timestamp: u64,
fn process_tcp<'frame>(&self, sockets: &mut SocketSet, timestamp: u64,
ip_repr: IpRepr, ip_payload: &'frame [u8]) ->
Result<Packet<'frame>> {
let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
let tcp_packet = TcpPacket::new_checked(ip_payload)?;
let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr)?;
let checksum_caps = self.device.capabilities().checksum;
let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &checksum_caps)?;
for tcp_socket in sockets.iter_mut().filter_map(
<Socket as AsSocket<TcpSocket>>::try_as_socket) {
@ -455,6 +459,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
fn dispatch(&mut self, timestamp: u64, packet: Packet) -> Result<()> {
let checksum_caps = self.device.capabilities().checksum;
match packet {
Packet::Arp(arp_repr) => {
let dst_hardware_addr =
@ -473,7 +478,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
},
Packet::Icmpv4(ipv4_repr, icmpv4_repr) => {
self.dispatch_ip(timestamp, IpRepr::Ipv4(ipv4_repr), |_ip_repr, payload| {
icmpv4_repr.emit(&mut Icmpv4Packet::new(payload));
icmpv4_repr.emit(&mut Icmpv4Packet::new(payload), &checksum_caps);
})
}
#[cfg(feature = "socket-raw")]
@ -486,7 +491,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Packet::Udp((ip_repr, udp_repr)) => {
self.dispatch_ip(timestamp, ip_repr, |ip_repr, payload| {
udp_repr.emit(&mut UdpPacket::new(payload),
&ip_repr.src_addr(), &ip_repr.dst_addr());
&ip_repr.src_addr(), &ip_repr.dst_addr(),
&checksum_caps);
})
}
#[cfg(feature = "socket-tcp")]
@ -513,7 +519,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
tcp_repr.emit(&mut TcpPacket::new(payload),
&ip_repr.src_addr(), &ip_repr.dst_addr());
&ip_repr.src_addr(), &ip_repr.dst_addr(),
&checksum_caps);
})
}
Packet::None => Ok(())
@ -574,6 +581,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
fn dispatch_ip<F>(&mut self, timestamp: u64, ip_repr: IpRepr, f: F) -> Result<()>
where F: FnOnce(IpRepr, &mut [u8]) {
let ip_repr = ip_repr.lower(&self.protocol_addrs)?;
let checksum_caps = self.device.capabilities().checksum;
let dst_hardware_addr =
self.lookup_hardware_addr(timestamp, &ip_repr.src_addr(), &ip_repr.dst_addr())?;
@ -585,7 +593,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
_ => unreachable!()
}
ip_repr.emit(frame.payload_mut());
ip_repr.emit(frame.payload_mut(), &checksum_caps);
let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..];
f(ip_repr, payload)

View File

@ -135,6 +135,52 @@ pub use self::tap_interface::TapInterface;
/// A tracer device for Ethernet frames.
pub type EthernetTracer<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;
/// The checksum configuration for a device
#[derive(Debug, Clone, Copy)]
pub enum Checksum {
/// Validate checksum when receiving and supply checksum when sending
Both,
/// Validate checksum when receiving
Rx,
/// Supply checksum before sending
Tx,
/// Ignore checksum
None,
}
impl Default for Checksum {
fn default() -> Checksum {
Checksum::Both
}
}
impl Checksum {
pub(crate) fn rx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Rx => true,
_ => false
}
}
pub(crate) fn tx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Tx => true,
_ => false
}
}
}
/// Configuration of checksum capabilities for each applicable protocol
#[derive(Debug, Clone, Default)]
pub struct ChecksumCapabilities {
pub ipv4: Checksum,
pub udpv4: Checksum,
pub udpv6: Checksum,
pub tcpv4: Checksum,
pub icmpv4: Checksum,
dummy: (),
}
/// A description of device capabilities.
///
/// Higher-level protocols may achieve higher throughput or lower latency if they consider
@ -158,6 +204,9 @@ pub struct DeviceCapabilities {
/// dynamically allocated.
pub max_burst_size: Option<usize>,
/// Checksum capabilities for the current device
pub checksum: ChecksumCapabilities,
/// Only present to prevent people from trying to initialize every field of DeviceLimits,
/// which would not let us add new fields in the future.
dummy: ()

View File

@ -2,6 +2,7 @@ use core::cmp::min;
use managed::Managed;
use {Error, Result};
use phy::ChecksumCapabilities;
use wire::{IpVersion, IpProtocol, Ipv4Repr, Ipv4Packet};
use socket::{IpRepr, Socket};
use storage::{Resettable, RingBuffer};
@ -173,13 +174,14 @@ impl<'a, 'b> RawSocket<'a, 'b> {
true
}
pub(crate) fn process(&mut self, ip_repr: &IpRepr, payload: &[u8]) -> Result<()> {
pub(crate) fn process(&mut self, ip_repr: &IpRepr, payload: &[u8],
checksum_caps: &ChecksumCapabilities) -> Result<()> {
debug_assert!(self.accepts(ip_repr));
let header_len = ip_repr.buffer_len();
let total_len = header_len + payload.len();
let packet_buf = self.rx_buffer.enqueue_one_with(|buf| buf.resize(total_len))?;
ip_repr.emit(&mut packet_buf.as_mut()[..header_len]);
ip_repr.emit(&mut packet_buf.as_mut()[..header_len], &checksum_caps);
packet_buf.as_mut()[header_len..].copy_from_slice(payload);
net_trace!("[{}]:{}:{}: receiving {} octets",
self.debug_id, self.ip_version, self.ip_protocol,
@ -187,17 +189,23 @@ impl<'a, 'b> RawSocket<'a, 'b> {
Ok(())
}
pub(crate) fn dispatch<F>(&mut self, emit: F) -> Result<()>
pub(crate) fn dispatch<F>(&mut self, emit: F, checksum_caps: &ChecksumCapabilities) -> Result<()>
where F: FnOnce((IpRepr, &[u8])) -> Result<()> {
fn prepare(protocol: IpProtocol, buffer: &mut [u8]) -> Result<(IpRepr, &[u8])> {
fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
match IpVersion::of_packet(buffer.as_ref())? {
IpVersion::Ipv4 => {
let mut packet = Ipv4Packet::new_checked(buffer.as_mut())?;
if packet.protocol() != protocol { return Err(Error::Unaddressable) }
packet.fill_checksum();
if checksum_caps.ipv4.tx() {
packet.fill_checksum();
} else {
// make sure we get a consistently zeroed checksum, since implementations might rely on it
packet.set_checksum(0);
}
let packet = Ipv4Packet::new(&*packet.into_inner());
let ipv4_repr = Ipv4Repr::parse(&packet)?;
let ipv4_repr = Ipv4Repr::parse(&packet, checksum_caps)?;
Ok((IpRepr::Ipv4(ipv4_repr), packet.payload()))
}
IpVersion::Unspecified => unreachable!(),
@ -209,7 +217,7 @@ impl<'a, 'b> RawSocket<'a, 'b> {
let ip_protocol = self.ip_protocol;
let ip_version = self.ip_version;
self.tx_buffer.dequeue_one_with(|packet_buf| {
match prepare(ip_protocol, packet_buf.as_mut()) {
match prepare(ip_protocol, packet_buf.as_mut(), &checksum_caps) {
Ok((ip_repr, raw_packet)) => {
net_trace!("[{}]:{}:{}: sending {} octets",
debug_id, ip_version, ip_protocol,
@ -289,7 +297,7 @@ mod test {
let mut socket = socket(buffer(0), buffer(1));
assert!(socket.can_send());
assert_eq!(socket.dispatch(|_| unreachable!()),
assert_eq!(socket.dispatch(|_| unreachable!(), &ChecksumCapabilities::default()),
Err(Error::Exhausted));
assert_eq!(socket.send_slice(&PACKET_BYTES[..]), Ok(()));
@ -300,14 +308,14 @@ mod test {
assert_eq!(ip_repr, HEADER_REPR);
assert_eq!(ip_payload, &PACKET_PAYLOAD);
Err(Error::Unaddressable)
}), Err(Error::Unaddressable));
}, &ChecksumCapabilities::default()), Err(Error::Unaddressable));
assert!(!socket.can_send());
assert_eq!(socket.dispatch(|(ip_repr, ip_payload)| {
assert_eq!(ip_repr, HEADER_REPR);
assert_eq!(ip_payload, &PACKET_PAYLOAD);
Ok(())
}), Ok(()));
}, &ChecksumCapabilities::default()), Ok(()));
assert!(socket.can_send());
}
@ -319,14 +327,14 @@ mod test {
Ipv4Packet::new(&mut wrong_version).set_version(5);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(|_| unreachable!()),
assert_eq!(socket.dispatch(|_| unreachable!(), &ChecksumCapabilities::default()),
Ok(()));
let mut wrong_protocol = PACKET_BYTES.clone();
Ipv4Packet::new(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(|_| unreachable!()),
assert_eq!(socket.dispatch(|_| unreachable!(), &ChecksumCapabilities::default()),
Ok(()));
}
@ -340,12 +348,12 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
assert!(!socket.can_recv());
@ -356,7 +364,7 @@ mod test {
let mut socket = socket(buffer(1), buffer(0));
assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD),
assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
Ok(()));
let mut slice = [0; 4];
@ -372,7 +380,7 @@ mod test {
buffer[..PACKET_BYTES.len()].copy_from_slice(&PACKET_BYTES[..]);
assert!(socket.accepts(&HEADER_REPR));
assert_eq!(socket.process(&HEADER_REPR, &buffer),
assert_eq!(socket.process(&HEADER_REPR, &buffer, &ChecksumCapabilities::default()),
Err(Error::Truncated));
}

View File

@ -2,6 +2,7 @@ use core::{cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use phy::ChecksumCapabilities;
use super::ip::checksum;
use super::{Ipv4Packet, Ipv4Repr};
@ -384,7 +385,11 @@ pub enum Repr<'a> {
impl<'a> Repr<'a> {
/// Parse an Internet Control Message Protocol version 4 packet and return
/// a high-level representation.
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&'a T>) -> Result<Repr<'a>> {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>> {
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
return Err(Error::Checksum)
}
match (packet.msg_type(), packet.msg_code()) {
(Message::EchoRequest, 0) => {
Ok(Repr::EchoRequest {
@ -441,7 +446,9 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into an Internet Control Message Protocol version 4
/// packet.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, packet: &mut Packet<&mut T>) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self,
packet: &mut Packet<&mut T>,
checksum_caps: &ChecksumCapabilities) {
packet.set_msg_code(0);
match self {
&Repr::EchoRequest { ident, seq_no, data } => {
@ -467,20 +474,26 @@ impl<'a> Repr<'a> {
packet.set_msg_code(reason.into());
let mut ip_packet = Ipv4Packet::new(packet.data_mut());
header.emit(&mut ip_packet);
header.emit(&mut ip_packet, checksum_caps);
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
payload.copy_from_slice(&data[..])
}
&Repr::__Nonexhaustive => unreachable!()
}
packet.fill_checksum()
if checksum_caps.icmpv4.tx() {
packet.fill_checksum()
} else {
// make sure we get a consistently zeroed checksum, since implementations might rely on it
packet.set_checksum(0);
}
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
match Repr::parse(self, &ChecksumCapabilities::default()) {
Ok(repr) => write!(f, "{}", repr),
Err(err) => {
write!(f, "ICMPv4 ({})", err)?;
@ -580,7 +593,7 @@ mod test {
#[test]
fn test_echo_parse() {
let packet = Packet::new(&ECHO_PACKET_BYTES[..]);
let repr = Repr::parse(&packet).unwrap();
let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, echo_packet_repr());
}
@ -589,7 +602,7 @@ mod test {
let repr = echo_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new(&mut bytes);
repr.emit(&mut packet);
repr.emit(&mut packet, &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
}
}

View File

@ -1,6 +1,7 @@
use core::fmt;
use {Error, Result};
use phy::ChecksumCapabilities;
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr};
/// Internet protocol version.
@ -338,12 +339,12 @@ impl IpRepr {
///
/// # Panics
/// This function panics if invoked on an unspecified representation.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T, checksum_caps: &ChecksumCapabilities) {
match self {
&IpRepr::Unspecified { .. } =>
panic!("unspecified IP representation"),
&IpRepr::Ipv4(repr) =>
repr.emit(&mut Ipv4Packet::new(buffer)),
repr.emit(&mut Ipv4Packet::new(buffer), &checksum_caps),
&IpRepr::__Nonexhaustive =>
unreachable!()
}

View File

@ -2,6 +2,7 @@ use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use phy::ChecksumCapabilities;
use super::ip::checksum;
use super::IpAddress;
@ -401,11 +402,14 @@ pub struct Repr {
impl Repr {
/// Parse an Internet Protocol version 4 packet and return a high-level representation.
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities) -> Result<Repr> {
// Version 4 is expected.
if packet.version() != 4 { return Err(Error::Malformed) }
// Valid checksum is expected.
if !packet.verify_checksum() { return Err(Error::Checksum) }
if checksum_caps.ipv4.rx() {
if !packet.verify_checksum() { return Err(Error::Checksum) }
}
// We do not support fragmentation.
if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error::Fragmented) }
// Total length may not be less than header length.
@ -432,7 +436,7 @@ impl Repr {
}
/// Emit a high-level representation into an Internet Protocol version 4 packet.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>, checksum_caps: &ChecksumCapabilities) {
packet.set_version(4);
packet.set_header_len(field::DST_ADDR.end as u8);
packet.set_dscp(0);
@ -448,13 +452,19 @@ impl Repr {
packet.set_protocol(self.protocol);
packet.set_src_addr(self.src_addr);
packet.set_dst_addr(self.dst_addr);
packet.fill_checksum();
if checksum_caps.ipv4.tx() {
packet.fill_checksum();
} else {
// make sure we get a consistently zeroed checksum, since implementations might rely on it
packet.set_checksum(0);
}
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
match Repr::parse(self, &ChecksumCapabilities::default()) {
Ok(repr) => write!(f, "{}", repr),
Err(err) => {
write!(f, "IPv4 ({})", err)?;
@ -507,7 +517,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
Err(err) => return write!(f, "{}({})\n", indent, err),
Ok(ip_packet) => {
write!(f, "{}{}\n", indent, ip_packet)?;
match Repr::parse(&ip_packet) {
match Repr::parse(&ip_packet, &ChecksumCapabilities::default()) {
Err(_) => return Ok(()),
Ok(ip_repr) => (ip_repr, &ip_packet.payload()[..ip_repr.payload_len])
}
@ -524,7 +534,8 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
Ok(udp_packet) => {
match super::UdpRepr::parse(&udp_packet,
&IpAddress::from(ip_repr.src_addr),
&IpAddress::from(ip_repr.dst_addr)) {
&IpAddress::from(ip_repr.dst_addr),
&ChecksumCapabilities::default()) {
Err(err) => write!(f, "{}{} ({})\n", indent, udp_packet, err),
Ok(udp_repr) => write!(f, "{}{}\n", indent, udp_repr)
}
@ -537,7 +548,8 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
Ok(tcp_packet) => {
match super::TcpRepr::parse(&tcp_packet,
&IpAddress::from(ip_repr.src_addr),
&IpAddress::from(ip_repr.dst_addr)) {
&IpAddress::from(ip_repr.dst_addr),
&ChecksumCapabilities::default()) {
Err(err) => write!(f, "{}{} ({})\n", indent, tcp_packet, err),
Ok(tcp_repr) => write!(f, "{}{}\n", indent, tcp_repr)
}
@ -647,7 +659,7 @@ mod test {
#[test]
fn test_parse() {
let packet = Packet::new(&REPR_PACKET_BYTES[..]);
let repr = Repr::parse(&packet).unwrap();
let repr = Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
@ -659,7 +671,7 @@ mod test {
packet.set_total_len(10);
packet.fill_checksum();
let packet = Packet::new(&*packet.into_inner());
assert_eq!(Repr::parse(&packet), Err(Error::Malformed));
assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
}
#[test]
@ -667,7 +679,7 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len() + REPR_PAYLOAD_BYTES.len()];
let mut packet = Packet::new(&mut bytes);
repr.emit(&mut packet);
repr.emit(&mut packet, &ChecksumCapabilities::default());
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES);
assert_eq!(&packet.into_inner()[..], &REPR_PACKET_BYTES[..]);
}

View File

@ -46,6 +46,7 @@
//!
/*!
```rust
use smoltcp::phy::ChecksumCapabilities;
use smoltcp::wire::*;
let repr = Ipv4Repr {
src_addr: Ipv4Address::new(10, 0, 0, 1),
@ -56,12 +57,12 @@ let repr = Ipv4Repr {
let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
{ // emission
let mut packet = Ipv4Packet::new(&mut buffer);
repr.emit(&mut packet);
repr.emit(&mut packet, &ChecksumCapabilities::default());
}
{ // parsing
let packet = Ipv4Packet::new_checked(&buffer)
.expect("truncated packet");
let parsed = Ipv4Repr::parse(&packet)
let parsed = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default())
.expect("malformed packet");
assert_eq!(repr, parsed);
}

View File

@ -2,6 +2,7 @@ use core::{i32, ops, cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use phy::ChecksumCapabilities;
use super::{IpProtocol, IpAddress};
use super::ip::checksum;
@ -644,13 +645,17 @@ impl<'a> Repr<'a> {
/// Parse a Transmission Control Protocol packet and return a high-level representation.
pub fn parse<T: ?Sized>(packet: &Packet<&'a T>,
src_addr: &IpAddress,
dst_addr: &IpAddress) -> Result<Repr<'a>>
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
where T: AsRef<[u8]> {
// Source and destination ports must be present.
if packet.src_port() == 0 { return Err(Error::Malformed) }
if packet.dst_port() == 0 { return Err(Error::Malformed) }
// Valid checksum is expected...
if !packet.verify_checksum(src_addr, dst_addr) { return Err(Error::Checksum) }
if checksum_caps.tcpv4.rx() && !packet.verify_checksum(src_addr, dst_addr) {
return Err(Error::Checksum)
}
let control =
match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
@ -713,7 +718,9 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into a Transmission Control Protocol packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>,
src_addr: &IpAddress, dst_addr: &IpAddress)
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
@ -741,7 +748,13 @@ impl<'a> Repr<'a> {
}
packet.set_urgent_at(0);
packet.payload_mut().copy_from_slice(self.payload);
packet.fill_checksum(src_addr, dst_addr)
if checksum_caps.tcpv4.tx() {
packet.fill_checksum(src_addr, dst_addr)
} else {
// make sure we get a consistently zeroed checksum, since implementations might rely on it
packet.set_checksum(0);
}
}
/// Return the length of the segment, in terms of sequence space.
@ -948,7 +961,7 @@ mod test {
#[test]
fn test_parse() {
let packet = Packet::new(&SYN_PACKET_BYTES[..]);
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into()).unwrap();
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
@ -957,7 +970,7 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
}

View File

@ -2,6 +2,7 @@ use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use {Error, Result};
use phy::ChecksumCapabilities;
use super::{IpProtocol, IpAddress};
use super::ip::checksum;
@ -203,12 +204,14 @@ impl<'a> Repr<'a> {
/// Parse an User Datagram Protocol packet and return a high-level representation.
pub fn parse<T: ?Sized>(packet: &Packet<&'a T>,
src_addr: &IpAddress,
dst_addr: &IpAddress) -> Result<Repr<'a>>
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
where T: AsRef<[u8]> {
// Destination port cannot be omitted (but source port can be).
if packet.dst_port() == 0 { return Err(Error::Malformed) }
// Valid checksum is expected...
if !packet.verify_checksum(src_addr, dst_addr) {
if checksum_caps.udpv4.rx() && !packet.verify_checksum(src_addr, dst_addr) {
match (src_addr, dst_addr) {
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_))
if packet.checksum() != 0 => {
@ -236,13 +239,20 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into an User Datagram Protocol packet.
pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress)
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> {
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
packet.set_len((field::CHECKSUM.end + self.payload.len()) as u16);
packet.payload_mut().copy_from_slice(self.payload);
packet.fill_checksum(src_addr, dst_addr)
if checksum_caps.udpv4.tx() {
packet.fill_checksum(src_addr, dst_addr)
} else {
// make sure we get a consistently zeroed checksum, since implementations might rely on it
packet.set_checksum(0);
}
}
}
@ -343,7 +353,7 @@ mod test {
#[test]
fn test_parse() {
let packet = Packet::new(&PACKET_BYTES[..]);
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into()).unwrap();
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
@ -352,7 +362,7 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
}
}