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: d16e0bd21293
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: d88ef3c8d3c8
Choose a head ref
  • 3 commits
  • 11 files changed
  • 2 contributors

Commits on Oct 3, 2017

  1. Implement wire::{IpCidr/Ipv4Cidr}.

    batonius authored and whitequark committed Oct 3, 2017
    Copy the full SHA
    fbfe985 View commit details
  2. Copy the full SHA
    331dc10 View commit details
  3. Drop the pretense that anyone cares about non-IP over Ethernet.

    To be precise, I'm talking about IPX, AppleTalk and DECnet here,
    not things like PPPoE, ATAoE, FCoE, or PTP, which make sense
    to implement on top of EthernetInterface but do not work on
    the same level on top of it as IP.
    whitequark committed Oct 3, 2017
    Copy the full SHA
    d88ef3c View commit details
Showing with 359 additions and 86 deletions.
  1. +7 −0 README.md
  2. +7 −6 examples/client.rs
  3. +3 −4 examples/loopback.rs
  4. +10 −8 examples/ping.rs
  5. +5 −5 examples/server.rs
  6. +78 −49 src/iface/ethernet.rs
  7. +56 −1 src/parsers.rs
  8. +2 −2 src/socket/tcp.rs
  9. +91 −11 src/wire/ip.rs
  10. +98 −0 src/wire/ipv4.rs
  11. +2 −0 src/wire/mod.rs
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -150,6 +150,13 @@ sudo ip link set tap0 up
sudo ip addr add 192.168.69.100/24 dev tap0
```

It's possible to let _smoltcp_ access Internet by enabling routing for the tap interface:

```sh
sudo iptables -t nat -A POSTROUTING -s 192.168.69.0/24 -j MASQUERADE
sudo sysctl net.ipv4.ip_forward=1
```

### Fault injection

In order to demonstrate the response of _smoltcp_ to adverse network conditions, all examples
13 changes: 7 additions & 6 deletions examples/client.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use std::str::{self, FromStr};
use std::time::Instant;
use std::os::unix::io::AsRawFd;
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress};
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
use smoltcp::socket::{AsSocket, SocketSet};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
@@ -39,18 +39,19 @@ fn main() {
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);

let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let protocol_addr = IpAddress::v4(192, 168, 69, 2);
let mut iface = EthernetInterface::new(
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)];
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let mut iface = EthernetInterface::new(
Box::new(device), Box::new(arp_cache) as Box<ArpCache>,
hardware_addr, [protocol_addr]);
ethernet_addr, ip_addrs, Some(default_v4_gw));

let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);

{
let socket: &mut TcpSocket = sockets.get_mut(tcp_handle).as_socket();
socket.connect((address, port), (protocol_addr, 49500)).unwrap();
socket.connect((address, port), 49500).unwrap();
}

let mut tcp_active = false;
7 changes: 3 additions & 4 deletions examples/loopback.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ mod utils;

use core::str;
use smoltcp::phy::Loopback;
use smoltcp::wire::{EthernetAddress, IpAddress};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
use smoltcp::socket::{AsSocket, SocketSet};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
@@ -89,11 +89,10 @@ fn main() {
let mut arp_cache_entries: [_; 8] = Default::default();
let mut arp_cache = SliceArpCache::new(&mut arp_cache_entries[..]);

let hardware_addr = EthernetAddress::default();
let mut protocol_addrs = [IpAddress::v4(127, 0, 0, 1)];
let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
let mut iface = EthernetInterface::new(
&mut device, &mut arp_cache as &mut ArpCache,
hardware_addr, &mut protocol_addrs[..]);
EthernetAddress::default(), &mut ip_addrs[..], None);

let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but
18 changes: 10 additions & 8 deletions examples/ping.rs
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ 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,
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr,
Ipv4Address, Ipv4Packet, Ipv4Repr,
Icmpv4Repr, Icmpv4Packet};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
@@ -39,6 +39,7 @@ fn main() {
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device_caps = device.capabilities();
let address = Ipv4Address::from_str(&matches.free[0]).expect("invalid address format");
let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4);
let interval = matches.opt_str("interval").map(|s| u64::from_str(&s).unwrap()).unwrap_or(1);
@@ -56,11 +57,12 @@ fn main() {
let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Icmp,
raw_rx_buffer, raw_tx_buffer);

let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let caps = device.capabilities();
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let mut iface = EthernetInterface::new(
Box::new(device), Box::new(arp_cache) as Box<ArpCache>,
hardware_addr, [IpAddress::from(local_addr)]);
ethernet_addr, [ip_addr], Some(default_v4_gw));

let mut sockets = SocketSet::new(vec![]);
let raw_handle = sockets.add(raw_socket);
@@ -100,9 +102,9 @@ fn main() {
.unwrap();

let mut ipv4_packet = Ipv4Packet::new(raw_payload);
ipv4_repr.emit(&mut ipv4_packet, &caps.checksum);
ipv4_repr.emit(&mut ipv4_packet, &device_caps.checksum);
let mut icmp_packet = Icmpv4Packet::new(ipv4_packet.payload_mut());
icmp_repr.emit(&mut icmp_packet, &caps.checksum);
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);

waiting_queue.insert(seq_no, timestamp);
seq_no += 1;
@@ -112,11 +114,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, &caps.checksum).unwrap();
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &device_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, &caps.checksum);
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum);

if let Ok(Icmpv4Repr::EchoReply { seq_no, data, .. }) = icmp_repr {
if let Some(_) = waiting_queue.get(&seq_no) {
10 changes: 5 additions & 5 deletions examples/server.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ use std::fmt::Write;
use std::time::Instant;
use std::os::unix::io::AsRawFd;
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
use smoltcp::socket::{AsSocket, SocketSet};
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketBuffer};
@@ -53,11 +53,11 @@ fn main() {
let tcp4_tx_buffer = TcpSocketBuffer::new(vec![0; 65535]);
let tcp4_socket = TcpSocket::new(tcp4_rx_buffer, tcp4_tx_buffer);

let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
let mut iface = EthernetInterface::new(
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let mut iface = EthernetInterface::new(
Box::new(device), Box::new(arp_cache) as Box<ArpCache>,
hardware_addr, protocol_addrs);
ethernet_addr, ip_addrs, None);

let mut sockets = SocketSet::new(vec![]);
let udp_handle = sockets.add(udp_socket);
127 changes: 78 additions & 49 deletions src/iface/ethernet.rs
Original file line number Diff line number Diff line change
@@ -6,10 +6,11 @@ use managed::{Managed, ManagedSlice};
use {Error, Result};
use phy::Device;
use wire::{EthernetAddress, EthernetProtocol, EthernetFrame};
use wire::{Ipv4Address};
use wire::{IpAddress, IpProtocol, IpRepr, IpCidr};
use wire::{ArpPacket, ArpRepr, ArpOperation};
use wire::{Ipv4Packet, Ipv4Repr};
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
use wire::{IpAddress, IpProtocol, IpRepr};
#[cfg(feature = "socket-udp")] use wire::{UdpPacket, UdpRepr};
#[cfg(feature = "socket-tcp")] use wire::{TcpPacket, TcpRepr, TcpControl};
use socket::{Socket, SocketSet, AsSocket};
@@ -26,8 +27,9 @@ use super::ArpCache;
pub struct Interface<'a, 'b, 'c, DeviceT: Device + 'a> {
device: Managed<'a, DeviceT>,
arp_cache: Managed<'b, ArpCache>,
hardware_addr: EthernetAddress,
protocol_addrs: ManagedSlice<'c, IpAddress>,
ethernet_addr: EthernetAddress,
ip_addrs: ManagedSlice<'c, IpCidr>,
ipv4_gateway: Option<Ipv4Address>,
}

enum Packet<'a> {
@@ -48,73 +50,83 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
/// # Panics
/// See the restrictions on [set_hardware_addr](#method.set_hardware_addr)
/// and [set_protocol_addrs](#method.set_protocol_addrs) functions.
pub fn new<DeviceMT, ArpCacheMT, ProtocolAddrsMT>
pub fn new<DeviceMT, ArpCacheMT, ProtocolAddrsMT, Ipv4GatewayAddrT>
(device: DeviceMT, arp_cache: ArpCacheMT,
hardware_addr: EthernetAddress, protocol_addrs: ProtocolAddrsMT) ->
ethernet_addr: EthernetAddress,
ip_addrs: ProtocolAddrsMT,
ipv4_gateway: Ipv4GatewayAddrT) ->
Interface<'a, 'b, 'c, DeviceT>
where DeviceMT: Into<Managed<'a, DeviceT>>,
ArpCacheMT: Into<Managed<'b, ArpCache>>,
ProtocolAddrsMT: Into<ManagedSlice<'c, IpAddress>>, {
ProtocolAddrsMT: Into<ManagedSlice<'c, IpCidr>>,
Ipv4GatewayAddrT: Into<Option<Ipv4Address>>, {
let device = device.into();
let arp_cache = arp_cache.into();
let protocol_addrs = protocol_addrs.into();

Self::check_hardware_addr(&hardware_addr);
Self::check_protocol_addrs(&protocol_addrs);
Interface {
device: device,
arp_cache: arp_cache,
hardware_addr: hardware_addr,
protocol_addrs: protocol_addrs,
}
let ip_addrs = ip_addrs.into();
let ipv4_gateway = ipv4_gateway.into();

Self::check_ethernet_addr(&ethernet_addr);
Self::check_ip_addrs(&ip_addrs);
Interface { device, arp_cache, ethernet_addr, ip_addrs, ipv4_gateway }
}

fn check_hardware_addr(addr: &EthernetAddress) {
fn check_ethernet_addr(addr: &EthernetAddress) {
if addr.is_multicast() {
panic!("hardware address {} is not unicast", addr)
panic!("Ethernet address {} is not unicast", addr)
}
}

/// Get the hardware address of the interface.
pub fn hardware_addr(&self) -> EthernetAddress {
self.hardware_addr
/// Get the Ethernet address of the interface.
pub fn ethernet_addr(&self) -> EthernetAddress {
self.ethernet_addr
}

/// Set the hardware address of the interface.
/// Set the Ethernet address of the interface.
///
/// # Panics
/// This function panics if the address is not unicast.
pub fn set_hardware_addr(&mut self, addr: EthernetAddress) {
self.hardware_addr = addr;
Self::check_hardware_addr(&self.hardware_addr);
pub fn set_ethernet_addr(&mut self, addr: EthernetAddress) {
self.ethernet_addr = addr;
Self::check_ethernet_addr(&self.ethernet_addr);
}

fn check_protocol_addrs(addrs: &[IpAddress]) {
for addr in addrs {
if !addr.is_unicast() {
panic!("protocol address {} is not unicast", addr)
fn check_ip_addrs(addrs: &[IpCidr]) {
for cidr in addrs {
if !cidr.address().is_unicast() {
panic!("IP address {} is not unicast", cidr.address())
}
}
}

/// Get the protocol addresses of the interface.
pub fn protocol_addrs(&self) -> &[IpAddress] {
self.protocol_addrs.as_ref()
/// Get the IP addresses of the interface.
pub fn ip_addrs(&self) -> &[IpCidr] {
self.ip_addrs.as_ref()
}

/// Update the protocol addresses of the interface.
/// Update the IP addresses of the interface.
///
/// # Panics
/// This function panics if any of the addresses is not unicast.
pub fn update_protocol_addrs<F: FnOnce(&mut ManagedSlice<'c, IpAddress>)>(&mut self, f: F) {
f(&mut self.protocol_addrs);
Self::check_protocol_addrs(&self.protocol_addrs)
pub fn update_ip_addrs<F: FnOnce(&mut ManagedSlice<'c, IpCidr>)>(&mut self, f: F) {
f(&mut self.ip_addrs);
Self::check_ip_addrs(&self.ip_addrs)
}

/// Check whether the interface has the given protocol address assigned.
pub fn has_protocol_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
/// Check whether the interface has the given IP address assigned.
pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
let addr = addr.into();
self.protocol_addrs.iter().any(|&probe| probe == addr)
self.ip_addrs.iter().any(|probe| probe.address() == addr)
}

/// Get the IPv4 gateway of the interface.
pub fn ipv4_gateway(&self) -> Option<Ipv4Address> {
self.ipv4_gateway
}

/// Set the IPv4 gateway of the interface.
pub fn set_ipv4_gateway<GatewayAddrT>(&mut self, gateway: GatewayAddrT)
where GatewayAddrT: Into<Option<Ipv4Address>> {
self.ipv4_gateway = gateway.into();
}

/// Transmit packets queued in the given sockets, and receive packets queued
@@ -219,7 +231,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {

// Ignore any packets not directed to our hardware address.
if !eth_frame.dst_addr().is_broadcast() &&
eth_frame.dst_addr() != self.hardware_addr {
eth_frame.dst_addr() != self.ethernet_addr {
return Ok(Packet::None)
}

@@ -255,11 +267,10 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
return Err(Error::Malformed)
}

if operation == ArpOperation::Request &&
self.has_protocol_addr(target_protocol_addr) {
if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) {
Ok(Packet::Arp(ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: self.hardware_addr,
source_hardware_addr: self.ethernet_addr,
source_protocol_addr: target_protocol_addr,
target_hardware_addr: source_hardware_addr,
target_protocol_addr: source_protocol_addr
@@ -315,7 +326,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
}

if !self.has_protocol_addr(ipv4_repr.dst_addr) {
if !self.has_ip_addr(ipv4_repr.dst_addr) {
// Ignore IP packets not directed at us.
return Ok(Packet::None)
}
@@ -534,17 +545,35 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
debug_assert!(tx_buffer.as_ref().len() == tx_len);

let mut frame = EthernetFrame::new(tx_buffer.as_mut());
frame.set_src_addr(self.hardware_addr);
frame.set_src_addr(self.ethernet_addr);

f(frame);

Ok(())
}

fn route(&self, addr: &IpAddress) -> Result<IpAddress> {
self.ip_addrs
.iter()
.find(|cidr| cidr.contains_addr(&addr))
.map(|_cidr| Ok(addr.clone())) // route directly
.unwrap_or_else(|| {
match (addr, self.ipv4_gateway) {
// route via a gateway
(&IpAddress::Ipv4(_), Some(gateway)) =>
Ok(gateway.into()),
// unroutable
_ => Err(Error::Unaddressable)
}
})
}

fn lookup_hardware_addr(&mut self, timestamp: u64,
src_addr: &IpAddress, dst_addr: &IpAddress) ->
Result<EthernetAddress> {
if let Some(hardware_addr) = self.arp_cache.lookup(dst_addr) {
let dst_addr = self.route(dst_addr)?;

if let Some(hardware_addr) = self.arp_cache.lookup(&dst_addr) {
return Ok(hardware_addr)
}

@@ -553,13 +582,13 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}

match (src_addr, dst_addr) {
(&IpAddress::Ipv4(src_addr), &IpAddress::Ipv4(dst_addr)) => {
(&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
net_debug!("address {} not in ARP cache, sending request",
dst_addr);

let arp_repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: self.hardware_addr,
source_hardware_addr: self.ethernet_addr,
source_protocol_addr: src_addr,
target_hardware_addr: EthernetAddress::BROADCAST,
target_protocol_addr: dst_addr,
@@ -580,7 +609,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 ip_repr = ip_repr.lower(&self.ip_addrs)?;
let checksum_caps = self.device.capabilities().checksum;

let dst_hardware_addr =
Loading