Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a7cdcd49f4ed
Choose a base ref
...
head repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d5147efb822e
Choose a head ref
  • 2 commits
  • 17 files changed
  • 1 contributor

Commits on Oct 2, 2017

  1. phy: introduce hardware based checksum settings, rename DeviceLimits

    this contains a rename of occurrences of
    DeviceLimits -> DeviceCapabilities.
    steffengy authored and whitequark committed Oct 2, 2017
    Copy the full SHA
    9b1b0b4 View commit details
  2. 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)
    steffengy authored and whitequark committed Oct 2, 2017
    Copy the full SHA
    d5147ef View commit details
Showing with 232 additions and 115 deletions.
  1. +6 −4 examples/ping.rs
  2. +31 −23 src/iface/ethernet.rs
  3. +6 −6 src/phy/fault_injector.rs
  4. +4 −4 src/phy/loopback.rs
  5. +59 −10 src/phy/mod.rs
  6. +2 −2 src/phy/pcap_writer.rs
  7. +4 −4 src/phy/raw_socket.rs
  8. +4 −4 src/phy/tap_interface.rs
  9. +2 −2 src/phy/tracer.rs
  10. +24 −16 src/socket/raw.rs
  11. +6 −6 src/socket/tcp.rs
  12. +20 −7 src/wire/icmpv4.rs
  13. +3 −2 src/wire/ip.rs
  14. +23 −11 src/wire/ipv4.rs
  15. +3 −2 src/wire/mod.rs
  16. +19 −6 src/wire/tcp.rs
  17. +16 −6 src/wire/udp.rs
10 changes: 6 additions & 4 deletions examples/ping.rs
Original file line number Diff line number Diff line change
@@ -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) {
54 changes: 31 additions & 23 deletions src/iface/ethernet.rs
Original file line number Diff line number Diff line change
@@ -169,8 +169,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}

fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<()> {
let mut limits = self.device.limits();
limits.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();
let mut caps = self.device.capabilities();
caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();

for socket in sockets.iter_mut() {
let mut device_result = Ok(());
@@ -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,12 +491,13 @@ 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")]
Packet::Tcp((ip_repr, mut tcp_repr)) => {
let limits = self.device.limits();
let caps = self.device.capabilities();
self.dispatch_ip(timestamp, ip_repr, |ip_repr, payload| {
// This is a terrible hack to make TCP performance more acceptable on systems
// where the TCP buffers are significantly larger than network buffers,
@@ -500,8 +506,8 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
// this would result in our peer pushing our window and sever packet loss.
//
// I'm really not happy about this "solution" but I don't know what else to do.
if let Some(max_burst_size) = limits.max_burst_size {
let mut max_segment_size = limits.max_transmission_unit;
if let Some(max_burst_size) = caps.max_burst_size {
let mut max_segment_size = caps.max_transmission_unit;
max_segment_size -= EthernetFrame::<&[u8]>::header_len();
max_segment_size -= ip_repr.buffer_len();
max_segment_size -= tcp_repr.header_len();
@@ -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)
12 changes: 6 additions & 6 deletions src/phy/fault_injector.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {Error, Result};
use super::{DeviceLimits, Device};
use super::{DeviceCapabilities, Device};

// We use our own RNG to stay compatible with #![no_std].
// The use of the RNG below has a slight bias, but it doesn't matter.
@@ -188,12 +188,12 @@ impl<D: Device> Device for FaultInjector<D>
type RxBuffer = D::RxBuffer;
type TxBuffer = TxBuffer<D::TxBuffer>;

fn limits(&self) -> DeviceLimits {
let mut limits = self.inner.limits();
if limits.max_transmission_unit > MTU {
limits.max_transmission_unit = MTU;
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = self.inner.capabilities();
if caps.max_transmission_unit > MTU {
caps.max_transmission_unit = MTU;
}
limits
caps
}

fn receive(&mut self, timestamp: u64) -> Result<Self::RxBuffer> {
8 changes: 4 additions & 4 deletions src/phy/loopback.rs
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ use std::collections::VecDeque;
use alloc::{Vec, VecDeque};

use {Error, Result};
use super::{Device, DeviceLimits};
use super::{Device, DeviceCapabilities};

/// A loopback device.
#[derive(Debug)]
@@ -32,10 +32,10 @@ impl Device for Loopback {
type RxBuffer = Vec<u8>;
type TxBuffer = TxBuffer;

fn limits(&self) -> DeviceLimits {
DeviceLimits {
fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities {
max_transmission_unit: 65535,
..DeviceLimits::default()
..DeviceCapabilities::default()
}
}

69 changes: 59 additions & 10 deletions src/phy/mod.rs
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
```rust
use std::slice;
use smoltcp::{Error, Result};
use smoltcp::phy::{DeviceLimits, Device};
use smoltcp::phy::{DeviceCapabilities, Device};
const TX_BUFFERS: [*mut u8; 2] = [0x10000000 as *mut u8, 0x10001000 as *mut u8];
const RX_BUFFERS: [*mut u8; 2] = [0x10002000 as *mut u8, 0x10003000 as *mut u8];
@@ -54,11 +54,11 @@ impl Device for EthernetDevice {
type RxBuffer = &'static [u8];
type TxBuffer = EthernetTxBuffer;
fn limits(&self) -> DeviceLimits {
let mut limits = DeviceLimits::default();
limits.max_transmission_unit = 1536;
limits.max_burst_size = Some(2);
limits
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(2);
caps
}
fn receive(&mut self, _timestamp: u64) -> Result<Self::RxBuffer> {
@@ -135,12 +135,58 @@ pub use self::tap_interface::TapInterface;
/// A tracer device for Ethernet frames.
pub type EthernetTracer<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;

/// A description of device limitations.
/// 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
/// the bandwidth or packet size limitations.
#[derive(Debug, Clone, Default)]
pub struct DeviceLimits {
pub struct DeviceCapabilities {
/// Maximum transmission unit.
///
/// The network device is unable to send or receive frames larger than the value returned
@@ -158,6 +204,9 @@ pub struct DeviceLimits {
/// 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: ()
@@ -172,8 +221,8 @@ pub trait Device {
type RxBuffer: AsRef<[u8]>;
type TxBuffer: AsRef<[u8]> + AsMut<[u8]>;

/// Get a description of device limitations.
fn limits(&self) -> DeviceLimits;
/// Get a description of device capabilities.
fn capabilities(&self) -> DeviceCapabilities;

/// Receive a frame.
///
Loading