Merge branch 'master' into dhcpv4-use-requested_ip
This commit is contained in:
commit
3e22502f61
30
.travis.yml
30
.travis.yml
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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: {:?}",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -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"),
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()) }
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
// =========================================================================================//
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Reference in New Issue