Merge branch 'master' into dhcpv4-use-requested_ip

This commit is contained in:
whitequark 2020-10-22 09:53:38 +00:00 committed by GitHub
commit 3e22502f61
29 changed files with 730 additions and 141 deletions

View File

@ -12,37 +12,42 @@ matrix:
env: FEATURES='default' MODE='test'
### Test select feature permutations, chosen to be as orthogonal as possible
- rust: nightly
env: FEATURES='std phy-raw_socket proto-ipv6 socket-udp' MODE='test'
env: FEATURES='std ethernet phy-raw_socket proto-ipv6 socket-udp' MODE='test'
- rust: nightly
env: FEATURES='std phy-tap_interface proto-ipv6 socket-udp' MODE='test'
env: FEATURES='std ethernet phy-tap_interface proto-ipv6 socket-udp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv4 proto-igmp socket-raw' MODE='test'
env: FEATURES='std ethernet proto-ipv4 proto-igmp socket-raw' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv4 socket-udp socket-tcp' MODE='test'
env: FEATURES='std ethernet proto-ipv4 socket-udp socket-tcp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv4 proto-dhcpv4 socket-udp' MODE='test'
env: FEATURES='std ethernet proto-ipv4 proto-dhcpv4 socket-udp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv6 socket-udp' MODE='test'
env: FEATURES='std ethernet proto-ipv6 socket-udp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv6 socket-tcp' MODE='test'
env: FEATURES='std ethernet proto-ipv6 socket-tcp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv4 socket-icmp socket-tcp' MODE='test'
env: FEATURES='std ethernet proto-ipv4 socket-icmp socket-tcp' MODE='test'
- rust: nightly
env: FEATURES='std proto-ipv6 socket-icmp socket-tcp' MODE='test'
env: FEATURES='std ethernet proto-ipv6 socket-icmp socket-tcp' MODE='test'
### Test select feature permutations, chosen to be as aggressive as possible
- rust: nightly
env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp std'
env: FEATURES='ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp std'
MODE='test'
- rust: nightly
env: FEATURES='ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc'
MODE='test'
- rust: nightly
env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc'
MODE='test'
- rust: nightly
env: FEATURES='proto-ipv4 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp'
env: FEATURES='ethernet proto-ipv4 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp'
MODE='build'
- rust: nightly
env: MODE='fuzz run' ARGS='packet_parser -- -max_len=1536 -max_total_time=30'
- rust: nightly
env: FEATURES='default' MODE='clippy'
- rust: nightly
env: FEATURES='default' MODE='check --bench bench'
- os: osx
rust: nightly
env: FEATURES='default' MODE='build'
@ -54,6 +59,9 @@ matrix:
# Clippy sometimes fails to compile and this shouldn't gate merges
- rust: nightly
env: FEATURES='default' MODE='clippy'
# See if the bench actually breaks
- rust: nightly
env: FEATURES='default' MODE='check --bench bench'
before_script:
- if [ "$MODE" == "fuzz run" ]; then cargo install cargo-fuzz; fi
- if [ "$MODE" == "clippy" ]; then cargo install clippy; fi

View File

@ -1,6 +1,6 @@
[package]
name = "smoltcp"
version = "0.5.0"
version = "0.6.0"
authors = ["whitequark <whitequark@whitequark.org>"]
description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap."
documentation = "https://docs.rs/smoltcp/"
@ -31,6 +31,7 @@ url = "1.0"
std = ["managed/std"]
alloc = ["managed/alloc"]
verbose = []
ethernet = []
"phy-raw_socket" = ["std", "libc"]
"phy-tap_interface" = ["std", "libc"]
"proto-ipv4" = []
@ -43,6 +44,7 @@ verbose = []
"socket-icmp" = []
default = [
"std", "log", # needed for `cargo test --no-default-features --features default` :/
"ethernet",
"phy-raw_socket", "phy-tap_interface",
"proto-ipv4", "proto-igmp", "proto-ipv6",
"socket-raw", "socket-icmp", "socket-udp", "socket-tcp"

View File

@ -125,7 +125,7 @@ To use the _smoltcp_ library in your project, add the following to `Cargo.toml`:
```toml
[dependencies]
smoltcp = "0.4"
smoltcp = "0.5"
```
The default configuration assumes a hosted environment, for ease of evaluation.
@ -133,7 +133,7 @@ You probably want to disable default features and configure them one by one:
```toml
[dependencies]
smoltcp = { version = "0.4", default-features = false, features = ["log"] }
smoltcp = { version = "0.5", default-features = false, features = ["log"] }
```
### Feature `std`

View File

@ -10,7 +10,7 @@ mod utils;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT};
use std::sync::atomic::{Ordering, AtomicBool};
use std::thread;
use std::io::{Read, Write};
use std::net::TcpStream;
@ -59,7 +59,7 @@ fn client(kind: Client) {
CLIENT_DONE.store(true, Ordering::SeqCst);
}
static CLIENT_DONE: AtomicBool = ATOMIC_BOOL_INIT;
static CLIENT_DONE: AtomicBool = AtomicBool::new(false);
fn main() {
#[cfg(feature = "log")]

View File

@ -135,6 +135,7 @@ fn main() {
if socket.may_recv() {
let data = socket.recv(|buffer| {
let recvd_len = buffer.len();
let mut data = buffer.to_owned();
if data.len() > 0 {
debug!("tcp:6970 recv data: {:?}",
@ -143,7 +144,7 @@ fn main() {
data.reverse();
data.extend(b"\n");
}
(data.len(), data)
(recvd_len, data)
}).unwrap();
if socket.can_send() && data.len() > 0 {
debug!("tcp:6970 send data: {:?}",

View File

@ -294,6 +294,7 @@ impl Client {
client_identifier: Some(mac),
server_identifier: None,
parameter_request_list: None,
max_size: Some(raw_socket.payload_recv_capacity() as u16),
dns_servers: None,
};
let mut send_packet = |iface, endpoint, dhcp_repr| {
@ -305,7 +306,6 @@ impl Client {
match self.state {
ClientState::Discovering => {
self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT);
let endpoint = IpEndpoint {
addr: Ipv4Address::BROADCAST.into(),
port: UDP_SERVER_PORT,

View File

@ -2,4 +2,4 @@ pub const UDP_SERVER_PORT: u16 = 67;
pub const UDP_CLIENT_PORT: u16 = 68;
mod clientv4;
pub use self::clientv4::Client as Dhcpv4Client;
pub use self::clientv4::{Client as Dhcpv4Client, Config as Dhcpv4Config};

View File

@ -578,7 +578,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT>
let mut emitted_any = false;
for mut socket in sockets.iter_mut() {
if !socket.meta_mut().egress_permitted(|ip_addr|
if !socket.meta_mut().egress_permitted(timestamp, |ip_addr|
self.inner.has_neighbor(&ip_addr, timestamp)) {
continue
}
@ -850,8 +850,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
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.
Err(Error::Exhausted) => (),
// The socket buffer is full or the packet was truncated
Err(Error::Exhausted) | Err(Error::Truncated) => (),
// Raw sockets don't validate the packets in any way.
Err(_) => unreachable!(),
}
@ -878,7 +878,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
// Fill the neighbor cache from IP header of unicast frames.
let ip_addr = IpAddress::Ipv6(ipv6_repr.src_addr);
if self.in_same_network(&ip_addr) &&
self.neighbor_cache.lookup_pure(&ip_addr, timestamp).is_none() {
!self.neighbor_cache.lookup(&ip_addr, timestamp).found() {
self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp);
}
}
@ -1143,7 +1143,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
if flags.contains(NdiscNeighborFlags::OVERRIDE) {
self.neighbor_cache.fill(ip_addr, lladdr, timestamp)
} else {
if self.neighbor_cache.lookup_pure(&ip_addr, timestamp).is_none() {
if !self.neighbor_cache.lookup(&ip_addr, timestamp).found() {
self.neighbor_cache.fill(ip_addr, lladdr, timestamp)
}
}
@ -1543,8 +1543,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
match self.route(addr, timestamp) {
Ok(routed_addr) => {
self.neighbor_cache
.lookup_pure(&routed_addr, timestamp)
.is_some()
.lookup(&routed_addr, timestamp)
.found()
}
Err(_) => false
}
@ -1618,8 +1618,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
})?;
Err(Error::Unaddressable)
}
#[cfg(feature = "proto-ipv6")]
@ -1646,12 +1644,13 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
solicit.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(payload), &checksum_caps);
})?;
Err(Error::Unaddressable)
}
_ => Err(Error::Unaddressable)
_ => ()
}
// The request got dispatched, limit the rate on the cache.
self.neighbor_cache.limit_rate(timestamp);
Err(Error::Unaddressable)
}
fn dispatch_ip<Tx, F>(&mut self, tx_token: Tx, timestamp: Instant,
@ -2667,6 +2666,67 @@ mod test {
Ok(Packet::None));
}
#[test]
#[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))]
fn test_raw_socket_truncated_packet() {
use socket::{RawSocket, RawSocketBuffer, RawPacketMetadata};
use wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr};
let (mut iface, mut socket_set) = create_loopback();
let packets = 1;
let rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]);
let tx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]);
let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer);
socket_set.add(raw_socket);
let src_addr = Ipv4Address([127, 0, 0, 2]);
let dst_addr = Ipv4Address([127, 0, 0, 1]);
let udp_repr = UdpRepr {
src_port: 67,
dst_port: 68,
payload: &[0x2a; 49] // 49 > 48, hence packet will be truncated
};
let mut bytes = vec![0xff; udp_repr.buffer_len()];
let mut packet = UdpPacket::new_unchecked(&mut bytes[..]);
udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default());
let ipv4_repr = Ipv4Repr {
src_addr: src_addr,
dst_addr: dst_addr,
protocol: IpProtocol::Udp,
hop_limit: 64,
payload_len: udp_repr.buffer_len()
};
// Emit to ethernet frame
let mut eth_bytes = vec![0u8;
EthernetFrame::<&[u8]>::header_len() +
ipv4_repr.buffer_len() + udp_repr.buffer_len()
];
let frame = {
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
ipv4_repr.emit(
&mut Ipv4Packet::new_unchecked(frame.payload_mut()),
&ChecksumCapabilities::default());
udp_repr.emit(
&mut UdpPacket::new_unchecked(
&mut frame.payload_mut()[ipv4_repr.buffer_len()..]),
&src_addr.into(),
&dst_addr.into(),
&ChecksumCapabilities::default());
EthernetFrame::new_unchecked(&*frame.into_inner())
};
let frame = iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame);
// because the packet could not be handled we should send an Icmp message
assert!(match frame {
Ok(Packet::Icmpv4(_)) => true,
_ => false,
});
}
#[test]
#[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))]
fn test_raw_socket_with_udp_socket() {

View File

@ -4,13 +4,19 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
provides lookup and caching of hardware addresses, and handles management packets.
*/
#[cfg(feature = "ethernet")]
mod neighbor;
mod route;
#[cfg(feature = "ethernet")]
mod ethernet;
#[cfg(feature = "ethernet")]
pub use self::neighbor::Neighbor as Neighbor;
#[cfg(feature = "ethernet")]
pub(crate) use self::neighbor::Answer as NeighborAnswer;
#[cfg(feature = "ethernet")]
pub use self::neighbor::Cache as NeighborCache;
pub use self::route::{Route, Routes};
#[cfg(feature = "ethernet")]
pub use self::ethernet::{Interface as EthernetInterface,
InterfaceBuilder as EthernetInterfaceBuilder};

View File

@ -6,9 +6,6 @@ use managed::ManagedMap;
use wire::{EthernetAddress, IpAddress};
use time::{Duration, Instant};
#[cfg(any(feature = "std", feature = "alloc"))]
use core::mem;
/// A cached neighbor.
///
/// A neighbor mapping translates from a protocol address to a hardware address,
@ -31,6 +28,16 @@ pub(crate) enum Answer {
RateLimited
}
impl Answer {
/// Returns whether a valid address was found.
pub(crate) fn found(&self) -> bool {
match self {
Answer::Found(_) => true,
_ => false,
}
}
}
/// A neighbor cache backed by a map.
///
/// # Examples
@ -104,7 +111,7 @@ impl<'a> Cache<'a> {
.filter(|(_, v)| timestamp < v.expires_at)
.collect();
mem::replace(map, new_btree_map);
*map = new_btree_map;
}
}
};
@ -157,35 +164,29 @@ impl<'a> Cache<'a> {
}
}
pub(crate) fn lookup_pure(&self, protocol_addr: &IpAddress, timestamp: Instant) ->
Option<EthernetAddress> {
pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
if protocol_addr.is_broadcast() {
return Some(EthernetAddress::BROADCAST)
return Answer::Found(EthernetAddress::BROADCAST);
}
match self.storage.get(protocol_addr) {
Some(&Neighbor { expires_at, hardware_addr }) => {
if timestamp < expires_at {
return Some(hardware_addr)
return Answer::Found(hardware_addr)
}
}
None => ()
}
None
if timestamp < self.silent_until {
Answer::RateLimited
} else {
Answer::NotFound
}
}
pub(crate) fn lookup(&mut self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
match self.lookup_pure(protocol_addr, timestamp) {
Some(hardware_addr) =>
Answer::Found(hardware_addr),
None if timestamp < self.silent_until =>
Answer::RateLimited,
None => {
self.silent_until = timestamp + Self::SILENT_TIME;
Answer::NotFound
}
}
pub(crate) fn limit_rate(&mut self, timestamp: Instant) {
self.silent_until = timestamp + Self::SILENT_TIME;
}
}
@ -206,17 +207,17 @@ mod test {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), None);
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2),
None);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
}
#[test]
@ -225,9 +226,9 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2),
None);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
}
#[test]
@ -236,9 +237,9 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_B));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B));
}
#[test]
@ -251,7 +252,7 @@ mod test {
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2);
assert_eq!(cache.storage.len(), 1);
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Some(HADDR_C));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C));
}
#[test]
@ -262,12 +263,12 @@ mod test {
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Some(HADDR_B));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), None);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false);
cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), None);
assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Some(HADDR_D));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D));
}
#[test]
@ -276,6 +277,8 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound);
cache.limit_rate(Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound);
}

View File

@ -117,7 +117,7 @@ impl<'a> Routes<'a> {
_ => unimplemented!()
};
for (prefix, route) in self.storage.range((Bound::Unbounded, Bound::Included(cidr))).rev() {
for (prefix, route) in self.storage.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr))).rev() {
// TODO: do something with route.preferred_until
if let Some(expires_at) = route.expires_at {
if timestamp > expires_at {

View File

@ -1,7 +1,7 @@
#![cfg_attr(feature = "alloc", feature(alloc))]
#![no_std]
#![deny(unsafe_code)]
#![cfg_attr(any(feature = "proto-ipv4", feature = "proto-ipv6"), deny(unused))]
#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "ethernet"), deny(unused))]
//! The _smoltcp_ library is built in a layered structure, with the layers corresponding
//! to the levels of API abstraction. Only the highest layers would be used by a typical
@ -66,17 +66,17 @@
//! of a packet, it is still logged correctly and in full.
//!
//! ## Packet and representation layer support
//! | Protocol | Packet | Representation |
//! |----------|--------|----------------|
//! | Ethernet | Yes | Yes |
//! | ARP | Yes | Yes |
//! | IPv4 | Yes | Yes |
//! | ICMPv4 | Yes | Yes |
//! | IGMPv1/2 | Yes | Yes |
//! | IPv6 | Yes | Yes |
//! | ICMPv6 | Yes | Yes |
//! | TCP | Yes | Yes |
//! | UDP | Yes | Yes |
//! | Protocol | Packet | Representation |
//! |----------|--------|----------------|
//! | Ethernet | Yes | Yes |
//! | ARP | Yes | Yes |
//! | IPv4 | Yes | Yes |
//! | ICMPv4 | Yes | Yes |
//! | IGMPv1/2 | Yes | Yes |
//! | IPv6 | Yes | Yes |
//! | ICMPv6 | Yes | Yes |
//! | TCP | Yes | Yes |
//! | UDP | Yes | Yes |
//!
//! [wire]: wire/index.html
//! [osi]: https://en.wikipedia.org/wiki/OSI_model
@ -91,7 +91,7 @@ compile_error!("at least one socket needs to be enabled"); */
// FIXME(dlrobertson): clippy fails with this lint
#![cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))]
#[cfg(feature = "proto-ipv6")]
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
#[macro_use]
extern crate bitflags;
extern crate byteorder;
@ -134,6 +134,11 @@ pub enum Error {
/// or a TCP connection attempt was made to an unspecified endpoint.
Unaddressable,
/// The operation is finished.
/// E.g. when reading from a TCP socket, there's no more data to read because the remote
/// has closed the connection.
Finished,
/// An incoming packet could not be parsed because some of its fields were out of bounds
/// of the received data.
Truncated,
@ -165,6 +170,7 @@ impl fmt::Display for Error {
&Error::Exhausted => write!(f, "buffer space exhausted"),
&Error::Illegal => write!(f, "illegal operation"),
&Error::Unaddressable => write!(f, "unaddressable destination"),
&Error::Finished => write!(f, "operation finished"),
&Error::Truncated => write!(f, "truncated packet"),
&Error::Checksum => write!(f, "checksum error"),
&Error::Unrecognized => write!(f, "unrecognized packet"),

View File

@ -3,7 +3,9 @@
use core::str::FromStr;
use core::result;
use wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "ethernet")]
use wire::EthernetAddress;
use wire::{IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "proto-ipv4")]
use wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
@ -116,6 +118,7 @@ impl<'a> Parser<'a> {
}
}
#[cfg(feature = "ethernet")]
fn accept_mac_joined_with(&mut self, separator: u8) -> Result<EthernetAddress> {
let mut octets = [0u8; 6];
for n in 0..6 {
@ -127,6 +130,7 @@ impl<'a> Parser<'a> {
Ok(EthernetAddress(octets))
}
#[cfg(feature = "ethernet")]
fn accept_mac(&mut self) -> Result<EthernetAddress> {
if let Some(mac) = self.try(|p| p.accept_mac_joined_with(b'-')) {
return Ok(mac)
@ -344,6 +348,7 @@ impl<'a> Parser<'a> {
}
}
#[cfg(feature = "ethernet")]
impl FromStr for EthernetAddress {
type Err = ();
@ -462,7 +467,7 @@ mod test {
}
#[test]
#[cfg(feature = "proto-ipv4")]
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
fn test_mac() {
assert_eq!(EthernetAddress::from_str(""), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),

View File

@ -115,6 +115,7 @@ pub use self::raw_socket::RawSocket;
#[cfg(all(feature = "phy-tap_interface", target_os = "linux"))]
pub use self::tap_interface::TapInterface;
#[cfg(feature = "ethernet")]
/// A tracer device for Ethernet frames.
pub type EthernetTracer<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;
@ -208,7 +209,10 @@ pub struct DeviceCapabilities {
/// dynamically allocated.
pub max_burst_size: Option<usize>,
/// The set of protocols for which checksum can be computed in hardware.
/// Checksum behavior.
///
/// If the network device is capable of verifying or computing checksums for some protocols,
/// it can request that the stack not do so in software to improve performance.
pub checksum: ChecksumCapabilities,
/// Only present to prevent people from trying to initialize every field of DeviceLimits,

View File

@ -26,15 +26,24 @@ pub use self::tap_interface::TapInterfaceDesc;
/// Wait until given file descriptor becomes readable, but no longer than given timeout.
pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
unsafe {
let mut readfds = mem::uninitialized::<libc::fd_set>();
libc::FD_ZERO(&mut readfds);
libc::FD_SET(fd, &mut readfds);
let mut readfds = {
let mut readfds = mem::MaybeUninit::<libc::fd_set>::uninit();
libc::FD_ZERO(readfds.as_mut_ptr());
libc::FD_SET(fd, readfds.as_mut_ptr());
readfds.assume_init()
};
let mut writefds = mem::uninitialized::<libc::fd_set>();
libc::FD_ZERO(&mut writefds);
let mut writefds = {
let mut writefds = mem::MaybeUninit::<libc::fd_set>::uninit();
libc::FD_ZERO(writefds.as_mut_ptr());
writefds.assume_init()
};
let mut exceptfds = mem::uninitialized::<libc::fd_set>();
libc::FD_ZERO(&mut exceptfds);
let mut exceptfds = {
let mut exceptfds = mem::MaybeUninit::<libc::fd_set>::uninit();
libc::FD_ZERO(exceptfds.as_mut_ptr());
exceptfds.assume_init()
};
let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 };
let timeout_ptr =
@ -75,7 +84,7 @@ fn ifreq_for(name: &str) -> ifreq {
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
cmd: libc::c_ulong) -> io::Result<libc::c_int> {
unsafe {
let res = libc::ioctl(lower, cmd, ifreq as *mut ifreq);
let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);
if res == -1 { return Err(io::Error::last_os_error()) }
}

View File

@ -189,6 +189,30 @@ impl<'a, 'b> IcmpSocket<'a, 'b> {
!self.rx_buffer.is_empty()
}
/// Return the maximum number packets the socket can receive.
#[inline]
pub fn packet_recv_capacity(&self) -> usize {
self.rx_buffer.packet_capacity()
}
/// Return the maximum number packets the socket can transmit.
#[inline]
pub fn packet_send_capacity(&self) -> usize {
self.tx_buffer.packet_capacity()
}
/// Return the maximum number of bytes inside the recv buffer.
#[inline]
pub fn payload_recv_capacity(&self) -> usize {
self.rx_buffer.payload_capacity()
}
/// Return the maximum number of bytes inside the transmit buffer.
#[inline]
pub fn payload_send_capacity(&self) -> usize {
self.tx_buffer.payload_capacity()
}
/// Check whether the socket is open.
#[inline]
pub fn is_open(&self) -> bool {

View File

@ -58,18 +58,21 @@ impl Meta {
}
}
pub(crate) fn egress_permitted<F>(&mut self, has_neighbor: F) -> bool
pub(crate) fn egress_permitted<F>(&mut self, timestamp: Instant, has_neighbor: F) -> bool
where F: Fn(IpAddress) -> bool
{
match self.neighbor_state {
NeighborState::Active =>
true,
NeighborState::Waiting { neighbor, .. } => {
NeighborState::Waiting { neighbor, silent_until } => {
if has_neighbor(neighbor) {
net_trace!("{}: neighbor {} discovered, unsilencing",
self.handle, neighbor);
self.neighbor_state = NeighborState::Active;
true
} else if timestamp >= silent_until {
net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor);
true
} else {
false
}

View File

@ -74,6 +74,30 @@ impl<'a, 'b> RawSocket<'a, 'b> {
!self.rx_buffer.is_empty()
}
/// Return the maximum number packets the socket can receive.
#[inline]
pub fn packet_recv_capacity(&self) -> usize {
self.rx_buffer.packet_capacity()
}
/// Return the maximum number packets the socket can transmit.
#[inline]
pub fn packet_send_capacity(&self) -> usize {
self.tx_buffer.packet_capacity()
}
/// Return the maximum number of bytes inside the recv buffer.
#[inline]
pub fn payload_recv_capacity(&self) -> usize {
self.rx_buffer.payload_capacity()
}
/// Return the maximum number of bytes inside the transmit buffer.
#[inline]
pub fn payload_send_capacity(&self) -> usize {
self.tx_buffer.payload_capacity()
}
/// Enqueue a packet to send, and return a pointer to its payload.
///
/// This function returns `Err(Error::Exhausted)` if the transmit buffer is full,

View File

@ -16,7 +16,7 @@ pub struct Item<'a, 'b: 'a> {
}
/// A handle, identifying a socket in a set.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct Handle(usize);
impl fmt::Display for Handle {

View File

@ -192,6 +192,7 @@ pub struct TcpSocket<'a> {
timer: Timer,
assembler: Assembler,
rx_buffer: SocketBuffer<'a>,
rx_fin_received: bool,
tx_buffer: SocketBuffer<'a>,
/// Interval after which, if no inbound packets are received, the connection is aborted.
timeout: Option<Duration>,
@ -276,6 +277,7 @@ impl<'a> TcpSocket<'a> {
assembler: Assembler::new(rx_buffer.capacity()),
tx_buffer: tx_buffer,
rx_buffer: rx_buffer,
rx_fin_received: false,
timeout: None,
keep_alive: None,
hop_limit: None,
@ -419,6 +421,7 @@ impl<'a> TcpSocket<'a> {
self.assembler = Assembler::new(self.rx_buffer.capacity());
self.tx_buffer.clear();
self.rx_buffer.clear();
self.rx_fin_received = false;
self.keep_alive = None;
self.timeout = None;
self.hop_limit = None;
@ -483,9 +486,9 @@ impl<'a> TcpSocket<'a> {
// If local address is not provided, use an unspecified address but a specified protocol.
// This lets us lower IpRepr later to determine IP header size and calculate MSS,
// but without committing to a specific address right away.
let local_addr = match remote_endpoint.addr {
IpAddress::Unspecified => return Err(Error::Unaddressable),
_ => remote_endpoint.addr.to_unspecified(),
let local_addr = match local_endpoint.addr {
IpAddress::Unspecified => remote_endpoint.addr.to_unspecified(),
ip => ip,
};
let local_endpoint = IpEndpoint { addr: local_addr, ..local_endpoint };
@ -640,6 +643,18 @@ impl<'a> TcpSocket<'a> {
!self.tx_buffer.is_full()
}
/// Return the maximum number of bytes inside the recv buffer.
#[inline]
pub fn recv_capacity(&self) -> usize {
self.rx_buffer.capacity()
}
/// Return the maximum number of bytes inside the transmit buffer.
#[inline]
pub fn send_capacity(&self) -> usize {
self.tx_buffer.capacity()
}
/// Check whether the receive half of the full-duplex connection buffer is open
/// (see [may_recv](#method.may_recv), and the receive buffer is not empty.
#[inline]
@ -694,12 +709,23 @@ impl<'a> TcpSocket<'a> {
})
}
fn recv_impl<'b, F, R>(&'b mut self, f: F) -> Result<R>
where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R) {
fn recv_error_check(&mut self) -> Result<()> {
// We may have received some data inside the initial SYN, but until the connection
// is fully open we must not dequeue any data, as it may be overwritten by e.g.
// another (stale) SYN. (We do not support TCP Fast Open.)
if !self.may_recv() { return Err(Error::Illegal) }
if !self.may_recv() {
if self.rx_fin_received {
return Err(Error::Finished)
}
return Err(Error::Illegal)
}
Ok(())
}
fn recv_impl<'b, F, R>(&'b mut self, f: F) -> Result<R>
where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R) {
self.recv_error_check()?;
let _old_length = self.rx_buffer.len();
let (size, result) = f(&mut self.rx_buffer);
@ -716,8 +742,13 @@ impl<'a> TcpSocket<'a> {
/// Call `f` with the largest contiguous slice of octets in the receive buffer,
/// and dequeue the amount of elements returned by `f`.
///
/// This function returns `Err(Error::Illegal)` if the receive half of
/// the connection is not open; see [may_recv](#method.may_recv).
/// This function errors if the receive half of the connection is not open.
///
/// If the receive half has been gracefully closed (with a FIN packet), `Err(Error::Finished)`
/// is returned. In this case, the previously received data is guaranteed to be complete.
///
/// In all other cases, `Err(Error::Illegal)` is returned and previously received data (if any)
/// may be incomplete (truncated).
pub fn recv<'b, F, R>(&'b mut self, f: F) -> Result<R>
where F: FnOnce(&'b mut [u8]) -> (usize, R) {
self.recv_impl(|rx_buffer| {
@ -743,8 +774,7 @@ impl<'a> TcpSocket<'a> {
///
/// This function otherwise behaves identically to [recv](#method.recv).
pub fn peek(&mut self, size: usize) -> Result<&[u8]> {
// See recv() above.
if !self.may_recv() { return Err(Error::Illegal) }
self.recv_error_check()?;
let buffer = self.rx_buffer.get_allocated(0, size);
if buffer.len() > 0 {
@ -846,7 +876,8 @@ impl<'a> TcpSocket<'a> {
// and an acknowledgment indicating the next sequence number expected
// to be received.
reply_repr.seq_number = self.remote_last_seq;
reply_repr.ack_number = self.remote_last_ack;
reply_repr.ack_number = Some(self.remote_seq_no + self.rx_buffer.len());
self.remote_last_ack = reply_repr.ack_number;
// From RFC 1323:
// The window field [...] of every outgoing segment, with the exception of SYN
@ -964,6 +995,15 @@ impl<'a> TcpSocket<'a> {
self.meta.handle, self.local_endpoint, self.remote_endpoint);
return Err(Error::Dropped)
}
// Any ACK in the SYN-SENT state must have the SYN flag set.
(State::SynSent, &TcpRepr {
control: TcpControl::None, ack_number: Some(_), ..
}) => {
net_debug!("{}:{}:{}: expecting a SYN|ACK",
self.meta.handle, self.local_endpoint, self.remote_endpoint);
self.abort();
return Err(Error::Dropped)
}
// Every acknowledgement must be for transmitted but unacknowledged data.
(_, &TcpRepr { ack_number: Some(ack_number), .. }) => {
let unacknowledged = self.tx_buffer.len() + control_len;
@ -1127,6 +1167,7 @@ impl<'a> TcpSocket<'a> {
// 7th and 8th steps in the "SEGMENT ARRIVES" event describe this behavior.
(State::SynReceived, TcpControl::Fin) => {
self.remote_seq_no += 1;
self.rx_fin_received = true;
self.set_state(State::CloseWait);
self.timer.set_for_idle(timestamp, self.keep_alive);
}
@ -1157,6 +1198,7 @@ impl<'a> TcpSocket<'a> {
// FIN packets in ESTABLISHED state indicate the remote side has closed.
(State::Established, TcpControl::Fin) => {
self.remote_seq_no += 1;
self.rx_fin_received = true;
self.set_state(State::CloseWait);
self.timer.set_for_idle(timestamp, self.keep_alive);
}
@ -1174,6 +1216,7 @@ impl<'a> TcpSocket<'a> {
// if they also acknowledge our FIN.
(State::FinWait1, TcpControl::Fin) => {
self.remote_seq_no += 1;
self.rx_fin_received = true;
if ack_of_fin {
self.set_state(State::TimeWait);
self.timer.set_for_close(timestamp);
@ -1183,9 +1226,15 @@ impl<'a> TcpSocket<'a> {
}
}
// Data packets in FIN-WAIT-2 reset the idle timer.
(State::FinWait2, TcpControl::None) => {
self.timer.set_for_idle(timestamp, self.keep_alive);
}
// FIN packets in FIN-WAIT-2 state change it to TIME-WAIT.
(State::FinWait2, TcpControl::Fin) => {
self.remote_seq_no += 1;
self.rx_fin_received = true;
self.set_state(State::TimeWait);
self.timer.set_for_close(timestamp);
}
@ -1336,7 +1385,6 @@ impl<'a> TcpSocket<'a> {
// packets for every packet it receives.
net_trace!("{}:{}:{}: ACKing incoming segment",
self.meta.handle, self.local_endpoint, self.remote_endpoint);
self.remote_last_ack = Some(self.remote_seq_no + self.rx_buffer.len());
Ok(Some(self.ack_reply(ip_repr, &repr)))
} else {
Ok(None)
@ -1378,8 +1426,11 @@ impl<'a> TcpSocket<'a> {
}
fn window_to_update(&self) -> bool {
(self.rx_buffer.window() >> self.remote_win_shift) as u16 >
self.remote_last_win
match self.state {
State::SynSent | State::SynReceived | State::Established | State::FinWait1 | State::FinWait2 =>
(self.rx_buffer.window() >> self.remote_win_shift) as u16 > self.remote_last_win,
_ => false,
}
}
pub(crate) fn dispatch<F>(&mut self, timestamp: Instant, caps: &DeviceCapabilities,
@ -1646,8 +1697,8 @@ impl<'a> TcpSocket<'a> {
}
}
impl<'a> Into<Socket<'a, 'a>> for TcpSocket<'a> {
fn into(self) -> Socket<'a, 'a> {
impl<'a, 'b> Into<Socket<'a, 'b>> for TcpSocket<'a> {
fn into(self) -> Socket<'a, 'b> {
Socket::Tcp(self)
}
}
@ -1882,6 +1933,16 @@ mod test {
s
}
fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TcpSocket<'static> {
let mut s = socket();
s.state = State::SynSent;
s.local_endpoint = local;
s.remote_endpoint = REMOTE_END;
s.local_seq_no = LOCAL_SEQ;
s.remote_last_seq = LOCAL_SEQ;
s
}
fn socket_established_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> {
let mut s = socket_syn_received_with_buffer_sizes(tx_len, rx_len);
s.state = State::Established;
@ -2306,6 +2367,9 @@ mod test {
Err(Error::Unaddressable));
assert_eq!(s.connect((IpAddress::Unspecified, 80), LOCAL_END),
Err(Error::Unaddressable));
s.connect(REMOTE_END, LOCAL_END).expect("Connect failed with valid parameters");
assert_eq!(s.local_endpoint(), LOCAL_END);
assert_eq!(s.remote_endpoint(), REMOTE_END);
}
#[test]
@ -2366,7 +2430,7 @@ mod test {
let mut s = socket();
s.local_seq_no = LOCAL_SEQ;
s.connect(REMOTE_END, LOCAL_END).unwrap();
sanity!(s, socket_syn_sent());
sanity!(s, socket_syn_sent_with_local_ipendpoint(LOCAL_END));
}
#[test]
@ -2435,6 +2499,17 @@ mod test {
assert_eq!(s.state, State::SynSent);
}
#[test]
fn test_syn_sent_bad_ack() {
let mut s = socket_syn_sent();
send!(s, TcpRepr {
control: TcpControl::None,
ack_number: Some(TcpSeqNumber(1)),
..SEND_TEMPL
}, Err(Error::Dropped));
assert_eq!(s.state, State::Closed);
}
#[test]
fn test_syn_sent_close() {
let mut s = socket();
@ -2861,6 +2936,47 @@ mod test {
}]);
}
#[test]
fn test_established_rst_bad_seq() {
let mut s = socket_established();
send!(s, TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ, // Wrong seq
ack_number: None,
..SEND_TEMPL
}, Ok(Some(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1),
..RECV_TEMPL
})));
assert_eq!(s.state, State::Established);
// Send something to advance seq by 1
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1, // correct seq
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"a"[..],
..SEND_TEMPL
});
// Send wrong rst again, check that the challenge ack is correctly updated
// The ack number must be updated even if we don't call dispatch on the socket
// See https://github.com/smoltcp-rs/smoltcp/issues/338
send!(s, TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ, // Wrong seq
ack_number: None,
..SEND_TEMPL
}, Ok(Some(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 2), // this has changed
window_len: 63,
..RECV_TEMPL
})));
}
// =========================================================================================//
// Tests for the FIN-WAIT-1 state.
// =========================================================================================//
@ -2922,6 +3038,22 @@ mod test {
assert_eq!(s.state, State::FinWait1);
}
#[test]
fn test_fin_wait_1_recv() {
let mut s = socket_fin_wait_1();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
assert_eq!(s.state, State::FinWait1);
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
}
#[test]
fn test_fin_wait_1_close() {
let mut s = socket_fin_wait_1();
@ -2946,6 +3078,22 @@ mod test {
sanity!(s, socket_time_wait(false));
}
#[test]
fn test_fin_wait_2_recv() {
let mut s = socket_fin_wait_2();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
assert_eq!(s.state, State::FinWait2);
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
}
#[test]
fn test_fin_wait_2_close() {
let mut s = socket_fin_wait_2();
@ -3958,6 +4106,60 @@ mod test {
}));
}
#[test]
fn test_close_wait_no_window_update() {
let mut s = socket_established();
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &[1,2,3,4],
..SEND_TEMPL
});
assert_eq!(s.state, State::CloseWait);
// we ack the FIN, with the reduced window size.
recv!(s, Ok(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 6),
window_len: 60,
..RECV_TEMPL
}));
let rx_buf = &mut [0; 32];
assert_eq!(s.recv_slice(rx_buf), Ok(4));
// check that we do NOT send a window update even if it has changed.
recv!(s, Err(Error::Exhausted));
}
#[test]
fn test_time_wait_no_window_update() {
let mut s = socket_fin_wait_2();
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 2),
payload: &[1,2,3,4],
..SEND_TEMPL
});
assert_eq!(s.state, State::TimeWait);
// we ack the FIN, with the reduced window size.
recv!(s, Ok(TcpRepr {
seq_number: LOCAL_SEQ + 2,
ack_number: Some(REMOTE_SEQ + 6),
window_len: 60,
..RECV_TEMPL
}));
let rx_buf = &mut [0; 32];
assert_eq!(s.recv_slice(rx_buf), Ok(4));
// check that we do NOT send a window update even if it has changed.
recv!(s, Err(Error::Exhausted));
}
// =========================================================================================//
// Tests for flow control.
// =========================================================================================//
@ -4496,6 +4698,160 @@ mod test {
}));
}
// =========================================================================================//
// Tests for graceful vs ungraceful rx close
// =========================================================================================//
#[test]
fn test_rx_close_fin() {
let mut s = socket_established();
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished));
}
#[test]
fn test_rx_close_fin_in_fin_wait_1() {
let mut s = socket_fin_wait_1();
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
assert_eq!(s.state, State::Closing);
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished));
}
#[test]
fn test_rx_close_fin_in_fin_wait_2() {
let mut s = socket_fin_wait_2();
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1 + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
assert_eq!(s.state, State::TimeWait);
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished));
}
#[test]
fn test_rx_close_fin_with_hole() {
let mut s = socket_established();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
send!(s, TcpRepr {
control: TcpControl::Fin,
seq_number: REMOTE_SEQ + 1 + 6,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"ghi"[..],
..SEND_TEMPL
}, Ok(Some(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 3),
window_len: 61,
..RECV_TEMPL
})));
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
s.recv(|data| {
assert_eq!(data, b"");
(0, ())
}).unwrap();
send!(s, TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ + 1 + 9,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
});
// Error must be `Illegal` even if we've received a FIN,
// because we are missing data.
assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal));
}
#[test]
fn test_rx_close_rst() {
let mut s = socket_established();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
send!(s, TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ + 1 + 3,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
});
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal));
}
#[test]
fn test_rx_close_rst_with_hole() {
let mut s = socket_established();
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"abc"[..],
..SEND_TEMPL
});
send!(s, TcpRepr {
seq_number: REMOTE_SEQ + 1 + 6,
ack_number: Some(LOCAL_SEQ + 1),
payload: &b"ghi"[..],
..SEND_TEMPL
}, Ok(Some(TcpRepr {
seq_number: LOCAL_SEQ + 1,
ack_number: Some(REMOTE_SEQ + 1 + 3),
window_len: 61,
..RECV_TEMPL
})));
send!(s, TcpRepr {
control: TcpControl::Rst,
seq_number: REMOTE_SEQ + 1 + 9,
ack_number: Some(LOCAL_SEQ + 1),
..SEND_TEMPL
});
s.recv(|data| {
assert_eq!(data, b"abc");
(3, ())
}).unwrap();
assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal));
}
// =========================================================================================//
// Tests for packet filtering.
// =========================================================================================//

View File

@ -110,6 +110,30 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
!self.rx_buffer.is_empty()
}
/// Return the maximum number packets the socket can receive.
#[inline]
pub fn packet_recv_capacity(&self) -> usize {
self.rx_buffer.packet_capacity()
}
/// Return the maximum number packets the socket can transmit.
#[inline]
pub fn packet_send_capacity(&self) -> usize {
self.tx_buffer.packet_capacity()
}
/// Return the maximum number of bytes inside the recv buffer.
#[inline]
pub fn payload_recv_capacity(&self) -> usize {
self.rx_buffer.payload_capacity()
}
/// Return the maximum number of bytes inside the transmit buffer.
#[inline]
pub fn payload_send_capacity(&self) -> usize {
self.tx_buffer.payload_capacity()
}
/// Enqueue a packet to be sent to a given remote endpoint, and return a pointer
/// to its payload.
///

View File

@ -96,8 +96,8 @@ impl<'a, 'b, H> PacketBuffer<'a, 'b, H> {
} else {
// Add padding to the end of the ring buffer so that the
// contiguous window is at the beginning of the ring buffer.
*self.metadata_ring.enqueue_one()? = PacketMetadata::padding(size);
self.payload_ring.enqueue_many(size);
*self.metadata_ring.enqueue_one()? = PacketMetadata::padding(contig_window);
self.payload_ring.enqueue_many(contig_window);
}
}
@ -168,6 +168,16 @@ impl<'a, 'b, H> PacketBuffer<'a, 'b, H> {
Err(Error::Exhausted)
}
}
/// Return the maximum number packets that can be stored.
pub fn packet_capacity(&self) -> usize {
self.metadata_ring.capacity()
}
/// Return the maximum number of bytes in the payload ring buffer.
pub fn payload_capacity(&self) -> usize {
self.payload_ring.capacity()
}
}
#[cfg(test)]
@ -214,6 +224,14 @@ mod test {
assert_eq!(buffer.metadata_ring.len(), 0);
}
#[test]
fn test_padding_with_large_payload() {
let mut buffer = buffer();
assert!(buffer.enqueue(12, ()).is_ok());
assert!(buffer.dequeue().is_ok());
buffer.enqueue(12, ()).unwrap().copy_from_slice(b"abcdefghijkl");
}
#[test]
fn test_dequeue_with() {
let mut buffer = buffer();

View File

@ -52,6 +52,7 @@ pub enum DhcpOption<'a> {
ServerIdentifier(Ipv4Address),
Router(Ipv4Address),
SubnetMask(Ipv4Address),
MaximumDhcpMessageSize(u16),
Other { kind: u8, data: &'a [u8] }
}
@ -99,6 +100,9 @@ impl<'a> DhcpOption<'a> {
(field::OPT_SUBNET_MASK, 4) => {
option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
}
(field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
}
(_, _) => {
option = DhcpOption::Other { kind: kind, data: data };
}
@ -122,6 +126,9 @@ impl<'a> DhcpOption<'a> {
&DhcpOption::SubnetMask(ip) => {
2 + ip.as_bytes().len()
},
&DhcpOption::MaximumDhcpMessageSize(_) => {
4
}
&DhcpOption::Other { data, .. } => 2 + data.len()
}
}
@ -167,6 +174,10 @@ impl<'a> DhcpOption<'a> {
buffer[0] = field::OPT_SUBNET_MASK;
buffer[2..6].copy_from_slice(mask.as_bytes());
}
&DhcpOption::MaximumDhcpMessageSize(size) => {
buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE;
buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]);
}
&DhcpOption::Other { kind, data: provided } => {
buffer[0] = kind;
buffer[2..skip_length].copy_from_slice(provided);
@ -661,6 +672,8 @@ pub struct Repr<'a> {
pub parameter_request_list: Option<&'a [u8]>,
/// DNS servers
pub dns_servers: Option<[Option<Ipv4Address>; 3]>,
/// The maximum size dhcp packet the interface can receive
pub max_size: Option<u16>,
}
impl<'a> Repr<'a> {
@ -672,6 +685,7 @@ impl<'a> Repr<'a> {
if self.requested_ip.is_some() { len += 6; }
if self.client_identifier.is_some() { len += 9; }
if self.server_identifier.is_some() { len += 6; }
if self.max_size.is_some() { len += 4; }
if let Some(list) = self.parameter_request_list { len += list.len() + 2; }
len
@ -710,6 +724,7 @@ impl<'a> Repr<'a> {
let mut subnet_mask = None;
let mut parameter_request_list = None;
let mut dns_servers = None;
let mut max_size = None;
let mut options = packet.options()?;
while options.len() > 0 {
@ -736,6 +751,9 @@ impl<'a> Repr<'a> {
}
DhcpOption::SubnetMask(mask) => {
subnet_mask = Some(mask);
},
DhcpOption::MaximumDhcpMessageSize(size) => {
max_size = Some(size);
}
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
parameter_request_list = Some(data);
@ -760,7 +778,7 @@ impl<'a> Repr<'a> {
Ok(Repr {
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
broadcast, requested_ip, server_identifier, router,
subnet_mask, client_identifier, parameter_request_list, dns_servers,
subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
message_type: message_type?,
})
}
@ -802,6 +820,9 @@ impl<'a> Repr<'a> {
if let Some(ip) = self.requested_ip {
let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp);
}
if let Some(size) = self.max_size {
let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp);
}
if let Some(list) = self.parameter_request_list {
let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list };
let tmp = options; options = option.emit(tmp);
@ -837,7 +858,7 @@ mod test {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00,
0x00, 0x00, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
static ACK_BYTES: &[u8] = &[
@ -866,6 +887,7 @@ mod test {
const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]);
const DHCP_SIZE: u16 = 1500;
#[test]
fn test_deconstruct_discover() {
@ -883,18 +905,22 @@ mod test {
assert_eq!(packet.relay_agent_ip(), IP_NULL);
assert_eq!(packet.client_hardware_address(), CLIENT_MAC);
let options = packet.options().unwrap();
assert_eq!(options.len(), 3 + 9 + 6 + 6 + 1 + 7);
assert_eq!(options.len(), 3 + 9 + 6 + 4 + 6 + 1 + 7);
let (options, message_type) = DhcpOption::parse(options).unwrap();
assert_eq!(message_type, DhcpOption::MessageType(MessageType::Discover));
assert_eq!(options.len(), 9 + 6 + 6 + 1 + 7);
assert_eq!(options.len(), 9 + 6 + 4 + 6 + 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
assert_eq!(client_id, DhcpOption::ClientIdentifier(CLIENT_MAC));
assert_eq!(options.len(), 6 + 6 + 1 + 7);
assert_eq!(options.len(), 6 + 4 + 6 + 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
assert_eq!(client_id, DhcpOption::RequestedIp(IP_NULL));
assert_eq!(options.len(), 4 + 6 + 1 + 7);
let (options, msg_size) = DhcpOption::parse(options).unwrap();
assert_eq!(msg_size, DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE));
assert_eq!(options.len(), 6 + 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
@ -910,7 +936,7 @@ mod test {
#[test]
fn test_construct_discover() {
let mut bytes = vec![0xa5; 272];
let mut bytes = vec![0xa5; 276];
let mut packet = Packet::new_unchecked(&mut bytes);
packet.set_magic_number(MAGIC_COOKIE);
packet.set_sname_and_boot_file_to_zero();
@ -932,6 +958,7 @@ mod test {
let tmp = options; options = DhcpOption::MessageType(MessageType::Discover).emit(tmp);
let tmp = options; options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(tmp);
let tmp = options; options = DhcpOption::RequestedIp(IP_NULL).emit(tmp);
let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(tmp);
let option = DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42],
};
@ -940,7 +967,7 @@ mod test {
}
let packet = &mut packet.into_inner()[..];
for byte in &mut packet[265..272] {
for byte in &mut packet[269..276] {
*byte = 0; // padding bytes
}
@ -959,6 +986,7 @@ mod test {
subnet_mask: None,
relay_agent_ip: IP_NULL,
broadcast: false,
max_size: Some(DHCP_SIZE),
requested_ip: Some(IP_NULL),
client_identifier: Some(CLIENT_MAC),
server_identifier: None,

View File

@ -5,7 +5,9 @@ use {Error, Result};
use phy::ChecksumCapabilities;
use super::ip::checksum;
use super::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
use super::{MldRepr, NdiscRepr};
use super::MldRepr;
#[cfg(feature = "ethernet")]
use super::NdiscRepr;
enum_with_unknown! {
/// Internet protocol control message type.
@ -537,6 +539,7 @@ pub enum Repr<'a> {
seq_no: u16,
data: &'a [u8]
},
#[cfg(feature = "ethernet")]
Ndisc(NdiscRepr<'a>),
Mld(MldRepr<'a>),
#[doc(hidden)]
@ -619,6 +622,7 @@ impl<'a> Repr<'a> {
data: packet.payload()
})
},
#[cfg(feature = "ethernet")]
(msg_type, 0) if msg_type.is_ndisc() => {
NdiscRepr::parse(packet).map(|repr| Repr::Ndisc(repr))
},
@ -640,6 +644,7 @@ impl<'a> Repr<'a> {
&Repr::EchoReply { data, .. } => {
field::ECHO_SEQNO.end + data.len()
},
#[cfg(feature = "ethernet")]
&Repr::Ndisc(ndisc) => {
ndisc.buffer_len()
},
@ -711,6 +716,7 @@ impl<'a> Repr<'a> {
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
},
#[cfg(feature = "ethernet")]
&Repr::Ndisc(ndisc) => {
ndisc.emit(packet)
},

View File

@ -289,22 +289,22 @@ impl Repr {
fn max_resp_code_to_duration(value: u8) -> Duration {
let value: u64 = value.into();
let centisecs = if value < 128 {
let decisecs = if value < 128 {
value
} else {
let mant = value & 0xF;
let exp = (value >> 4) & 0x7;
(mant | 0x10) << (exp + 3)
};
Duration::from_millis(centisecs * 100)
Duration::from_millis(decisecs * 100)
}
fn duration_to_max_resp_code(duration: Duration) -> u8 {
let centisecs = duration.total_millis() / 100;
if centisecs < 128 {
centisecs as u8
} else if centisecs < 31744 {
let mut mant = centisecs >> 3;
let decisecs = duration.total_millis() / 100;
if decisecs < 128 {
decisecs as u8
} else if decisecs < 31744 {
let mut mant = decisecs >> 3;
let mut exp = 0u8;
while mant > 0x1F && exp < 0x8 {
mant >>= 1;

View File

@ -887,7 +887,7 @@ mod test {
}
#[test]
#[should_panic(expected = "destination and source slices have different lengths")]
#[should_panic(expected = "length")]
fn test_from_bytes_too_long() {
let _ = Address::from_bytes(&[0u8; 15]);
}

View File

@ -77,8 +77,9 @@ mod field {
pub mod pretty_print;
#[cfg(feature = "ethernet")]
mod ethernet;
#[cfg(feature = "proto-ipv4")]
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
mod arp;
pub(crate) mod ip;
#[cfg(feature = "proto-ipv4")]
@ -101,9 +102,9 @@ mod icmpv6;
mod icmp;
#[cfg(feature = "proto-igmp")]
mod igmp;
#[cfg(feature = "proto-ipv6")]
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
mod ndisc;
#[cfg(feature = "proto-ipv6")]
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
mod ndiscoption;
#[cfg(feature = "proto-ipv6")]
mod mld;
@ -114,12 +115,13 @@ pub(crate) mod dhcpv4;
pub use self::pretty_print::PrettyPrinter;
#[cfg(feature = "ethernet")]
pub use self::ethernet::{EtherType as EthernetProtocol,
Address as EthernetAddress,
Frame as EthernetFrame,
Repr as EthernetRepr};
#[cfg(feature = "proto-ipv4")]
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
pub use self::arp::{Hardware as ArpHardware,
Operation as ArpOperation,
Packet as ArpPacket,
@ -190,12 +192,12 @@ pub use self::icmpv6::{Message as Icmpv6Message,
pub use self::icmp::Repr as IcmpRepr;
#[cfg(feature = "proto-ipv6")]
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
pub use self::ndisc::{Repr as NdiscRepr,
RouterFlags as NdiscRouterFlags,
NeighborFlags as NdiscNeighborFlags};
#[cfg(feature = "proto-ipv6")]
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
pub use self::ndiscoption::{NdiscOption,
Repr as NdiscOptionRepr,
Type as NdiscOptionType,

View File

@ -7,7 +7,7 @@ easily human readable packet listings.
A packet can be formatted using the `PrettyPrinter` wrapper:
```rust
```rust,ignore
use smoltcp::wire::*;
let buffer = vec![
// Ethernet II

View File

@ -893,12 +893,12 @@ impl<'a> Repr<'a> {
packet.set_ack(self.ack_number.is_some());
{
let mut options = packet.options_mut();
if let Some(value) = self.window_scale {
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
}
if let Some(value) = self.max_seg_size {
let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp);
}
if let Some(value) = self.window_scale {
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
}
if self.sack_permitted {
let tmp = options; options = TcpOption::SackPermitted.emit(tmp);
} else if self.ack_number.is_some() && self.sack_ranges.iter().any(|s| s.is_some()) {