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: 5cb2dcd1aad1
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: ab0eccd21350
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Aug 28, 2017

  1. Use FnOnce, not FnMut, in Socket::dispatch() functions.

    There was never any reason to use FnMut and this significantly
    simplifies the job of the borrow checker.
    whitequark committed Aug 28, 2017
    Copy the full SHA
    917f89e View commit details
  2. Copy the full SHA
    ad9fa28 View commit details
  3. Get rid of IpPayload and indirection in Socket::dispatch.

    This was just completely pointless, and only served to obfuscate
    the data path and make testing harder.
    whitequark committed Aug 28, 2017
    Copy the full SHA
    ab0eccd View commit details
Showing with 133 additions and 232 deletions.
  1. +83 −95 src/iface/ethernet.rs
  2. +0 −18 src/socket/mod.rs
  3. +23 −47 src/socket/raw.rs
  4. +13 −36 src/socket/tcp.rs
  5. +14 −36 src/socket/udp.rs
178 changes: 83 additions & 95 deletions src/iface/ethernet.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ use wire::{ArpPacket, ArpRepr, ArpOperation};
use wire::{Ipv4Packet, Ipv4Repr};
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
use wire::{IpAddress, IpProtocol, IpRepr};
use wire::{TcpPacket, TcpRepr, TcpControl};
use wire::{UdpPacket, UdpRepr, TcpPacket, TcpRepr, TcpControl};
use socket::{Socket, SocketSet, RawSocket, TcpSocket, UdpSocket, AsSocket};
use super::ArpCache;

@@ -27,6 +27,8 @@ enum Response<'a> {
Nop,
Arp(ArpRepr),
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>),
Raw((IpRepr, &'a [u8])),
Udp((IpRepr, UdpRepr<'a>)),
Tcp((IpRepr, TcpRepr<'a>))
}

@@ -112,7 +114,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
pub fn poll(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<()> {
// First, transmit any outgoing packets.
loop {
if self.emit(sockets, timestamp)? { break }
if self.dispatch(sockets, timestamp)? { break }
}

// Now, receive any incoming packets.
@@ -134,7 +136,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
_ => return Err(Error::Unrecognized),
};

self.send_response(timestamp, response)
self.dispatch_response(timestamp, response)
}

// Snoop all ARP traffic, and respond to ARP packets directed at us.
@@ -334,7 +336,39 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
}
}

fn send_response(&mut self, timestamp: u64, response: Response) -> Result<()> {
fn dispatch(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<bool> {
let mut limits = self.device.limits();
limits.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();

let mut nothing_to_transmit = true;
for socket in sockets.iter_mut() {
let result = match socket {
&mut Socket::Raw(ref mut socket) =>
socket.dispatch(timestamp, &limits, |response|
self.dispatch_response(timestamp, Response::Raw(response))),
&mut Socket::Udp(ref mut socket) =>
socket.dispatch(timestamp, &limits, |response|
self.dispatch_response(timestamp, Response::Udp(response))),
&mut Socket::Tcp(ref mut socket) =>
socket.dispatch(timestamp, &limits, |response|
self.dispatch_response(timestamp, Response::Tcp(response))),
&mut Socket::__Nonexhaustive => unreachable!()
};

match result {
Ok(()) => {
nothing_to_transmit = false;
break
}
Err(Error::Exhausted) => continue,
Err(e) => return Err(e)
}
}

Ok(nothing_to_transmit)
}

fn dispatch_response(&mut self, timestamp: u64, response: Response) -> Result<()> {
macro_rules! emit_packet {
(Ethernet, $buffer_len:expr, |$frame:ident| $code:stmt) => ({
let tx_len = EthernetFrame::<&[u8]>::buffer_len($buffer_len);
@@ -349,21 +383,45 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
Ok(())
});

(Ip, $ip_repr:expr, |$payload:ident| $code:stmt) => ({
let ip_repr = $ip_repr.lower(&self.protocol_addrs)?;
match self.arp_cache.lookup(&ip_repr.dst_addr()) {
None => Err(Error::Unaddressable),
(Ip, $ip_repr_unspec:expr, |$ip_repr:ident, $payload:ident| $code:stmt) => ({
let $ip_repr = $ip_repr_unspec.lower(&self.protocol_addrs)?;

match self.arp_cache.lookup(&$ip_repr.dst_addr()) {
None => {
match ($ip_repr.src_addr(), $ip_repr.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_protocol_addr: src_addr,
target_hardware_addr: EthernetAddress([0xff; 6]),
target_protocol_addr: dst_addr,
};

emit_packet!(Ethernet, arp_repr.buffer_len(), |frame| {
frame.set_dst_addr(EthernetAddress([0xff; 6]));
frame.set_ethertype(EthernetProtocol::Arp);

arp_repr.emit(&mut ArpPacket::new(frame.payload_mut()));
})
}
_ => unreachable!()
}
},
Some(dst_hardware_addr) => {
emit_packet!(Ethernet, ip_repr.total_len(), |frame| {
emit_packet!(Ethernet, $ip_repr.total_len(), |frame| {
frame.set_dst_addr(dst_hardware_addr);
match ip_repr {
match $ip_repr {
IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4),
_ => unreachable!()
}

ip_repr.emit(frame.payload_mut());
$ip_repr.emit(frame.payload_mut());

let $payload = &mut frame.payload_mut()[ip_repr.buffer_len()..];
let $payload = &mut frame.payload_mut()[$ip_repr.buffer_len()..];
$code
})
}
@@ -388,98 +446,28 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
})
},
Response::Icmpv4(ipv4_repr, icmpv4_repr) => {
emit_packet!(Ip, IpRepr::Ipv4(ipv4_repr), |payload| {
emit_packet!(Ip, IpRepr::Ipv4(ipv4_repr), |ip_repr, payload| {
icmpv4_repr.emit(&mut Icmpv4Packet::new(payload));
})
}
Response::Raw((ip_repr, raw_packet)) => {
emit_packet!(Ip, ip_repr, |ip_repr, payload| {
payload.copy_from_slice(raw_packet);
})
}
Response::Udp((ip_repr, udp_repr)) => {
emit_packet!(Ip, ip_repr, |ip_repr, payload| {
udp_repr.emit(&mut UdpPacket::new(payload),
&ip_repr.src_addr(), &ip_repr.dst_addr());
})
}
Response::Tcp((ip_repr, tcp_repr)) => {
emit_packet!(Ip, ip_repr, |payload| {
emit_packet!(Ip, ip_repr, |ip_repr, payload| {
tcp_repr.emit(&mut TcpPacket::new(payload),
&ip_repr.src_addr(), &ip_repr.dst_addr());
})
}
Response::Nop => Ok(())
}
}

fn emit(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<bool> {
// Borrow checker is being overly careful around closures, so we have
// to hack around that.
let src_hardware_addr = self.hardware_addr;
let src_protocol_addrs = self.protocol_addrs.as_ref();
let arp_cache = &mut self.arp_cache;
let device = &mut self.device;

let mut limits = device.limits();
limits.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len();

let mut nothing_to_transmit = true;
for socket in sockets.iter_mut() {
let result = socket.dispatch(timestamp, &limits, &mut |repr, payload| {
let repr = repr.lower(src_protocol_addrs)?;

match arp_cache.lookup(&repr.dst_addr()) {
Some(dst_hardware_addr) => {
let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len() +
payload.buffer_len());
let mut tx_buffer = device.transmit(timestamp, tx_len)?;
debug_assert!(tx_buffer.as_ref().len() == tx_len);

let mut frame = EthernetFrame::new(&mut tx_buffer);
frame.set_src_addr(src_hardware_addr);
frame.set_dst_addr(dst_hardware_addr);
frame.set_ethertype(EthernetProtocol::Ipv4);

repr.emit(frame.payload_mut());

let mut ip_packet = Ipv4Packet::new(frame.payload_mut());
payload.emit(&repr, ip_packet.payload_mut());
}

None => {
let (src_addr, dst_addr) =
match (repr.src_addr(), repr.dst_addr()) {
(IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) =>
(src_addr, dst_addr),
// We've lowered all addresses to a concrete form.
_ => unreachable!()
};

let payload = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: src_hardware_addr,
source_protocol_addr: src_addr,
target_hardware_addr: EthernetAddress::default(),
target_protocol_addr: dst_addr,
};

let tx_len = EthernetFrame::<&[u8]>::buffer_len(payload.buffer_len());
let mut tx_buffer = device.transmit(timestamp, tx_len)?;
debug_assert!(tx_buffer.as_ref().len() == tx_len);

let mut frame = EthernetFrame::new(&mut tx_buffer);
frame.set_src_addr(src_hardware_addr);
frame.set_dst_addr(EthernetAddress([0xff; 6]));
frame.set_ethertype(EthernetProtocol::Arp);

let mut arp_packet = ArpPacket::new(frame.payload_mut());
payload.emit(&mut arp_packet);
}
}

Ok(())
});

match result {
Ok(()) => {
nothing_to_transmit = false;
break
}
Err(Error::Exhausted) => continue,
Err(e) => return Err(e)
}
}

Ok(nothing_to_transmit)
}
}
18 changes: 0 additions & 18 deletions src/socket/mod.rs
Original file line number Diff line number Diff line change
@@ -80,24 +80,6 @@ impl<'a, 'b> Socket<'a, 'b> {
pub fn set_debug_id(&mut self, id: usize) {
dispatch_socket!(self, |socket [mut]| socket.set_debug_id(id))
}

pub(crate) fn dispatch<F, R>(&mut self, timestamp: u64, limits: &DeviceLimits,
emit: &mut F) -> Result<R>
where F: FnMut(&IpRepr, &IpPayload) -> Result<R> {
dispatch_socket!(self, |socket [mut]| socket.dispatch(timestamp, limits, emit))
}
}

/// An IP-encapsulated packet representation.
///
/// This trait abstracts the various types of packets layered under the IP protocol,
/// and serves as an accessory to [trait Socket](trait.Socket.html).
pub trait IpPayload {
/// Return the length of the buffer required to serialize this high-level representation.
fn buffer_len(&self) -> usize;

/// Emit this high-level representation into a sequence of octets.
fn emit(&self, ip_repr: &IpRepr, payload: &mut [u8]);
}

/// A conversion trait for network sockets.
Loading