Эх сурвалжийг харах

Merge branch 'master' into dhcpv4-use-requested_ip

whitequark 4 жил өмнө
parent
commit
929dc7d9ea

+ 19 - 11
.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

+ 3 - 1
Cargo.toml

@@ -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"

+ 2 - 2
README.md

@@ -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`

+ 2 - 2
examples/benchmark.rs

@@ -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")]

+ 2 - 1
examples/server.rs

@@ -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: {:?}",

+ 1 - 1
src/dhcp/clientv4.rs

@@ -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,

+ 1 - 1
src/dhcp/mod.rs

@@ -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};

+ 72 - 12
src/iface/ethernet.rs

@@ -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() {

+ 6 - 0
src/iface/mod.rs

@@ -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};

+ 40 - 37
src/iface/neighbor.rs

@@ -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);
     }

+ 1 - 1
src/iface/route.rs

@@ -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 {

+ 19 - 13
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"),

+ 7 - 2
src/parsers.rs

@@ -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"),

+ 5 - 1
src/phy/mod.rs

@@ -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,

+ 17 - 8
src/phy/sys/mod.rs

@@ -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()) }
     }
 

+ 24 - 0
src/socket/icmp.rs

@@ -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 {

+ 5 - 2
src/socket/meta.rs

@@ -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
                 }

+ 24 - 0
src/socket/raw.rs

@@ -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,

+ 1 - 1
src/socket/set.rs

@@ -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 {

+ 373 - 17
src/socket/tcp.rs

@@ -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.
     // =========================================================================================//

+ 24 - 0
src/socket/udp.rs

@@ -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.
     ///

+ 20 - 2
src/storage/packet_buffer.rs

@@ -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();

+ 35 - 7
src/wire/dhcpv4.rs

@@ -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,

+ 7 - 1
src/wire/icmpv6.rs

@@ -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)
             },

+ 7 - 7
src/wire/igmp.rs

@@ -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;

+ 1 - 1
src/wire/ipv6.rs

@@ -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]);
     }

+ 8 - 6
src/wire/mod.rs

@@ -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,

+ 1 - 1
src/wire/pretty_print.rs

@@ -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

+ 3 - 3
src/wire/tcp.rs

@@ -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()) {