Browse Source

Add the proto-ipv4 feature.

Dan Robertson 7 years ago
parent
commit
507d2fe0ea
18 changed files with 592 additions and 350 deletions
  1. 14 11
      .travis.yml
  2. 9 7
      Cargo.toml
  3. 7 0
      README.md
  4. 50 14
      src/iface/ethernet.rs
  5. 32 33
      src/iface/neighbor.rs
  6. 3 1
      src/lib.rs
  7. 26 10
      src/parsers.rs
  8. 5 5
      src/socket/mod.rs
  9. 73 55
      src/socket/raw.rs
  10. 2 2
      src/socket/ref_.rs
  11. 1 1
      src/socket/set.rs
  12. 166 147
      src/socket/tcp.rs
  13. 92 58
      src/socket/udp.rs
  14. 2 0
      src/wire/ethernet.rs
  15. 75 5
      src/wire/ip.rs
  16. 10 1
      src/wire/mod.rs
  17. 13 0
      src/wire/tcp.rs
  18. 12 0
      src/wire/udp.rs

+ 14 - 11
.travis.yml

@@ -10,28 +10,31 @@ matrix:
     ### Test default configurations
     - rust: nightly
       env: FEATURES='default' MODE='test'
-    - rust: nightly
-      env: FEATURES='default proto-ipv6' MODE='test'
     ### Test select feature permutations, chosen to be as orthogonal as possible
     - rust: nightly
-      env: FEATURES='std phy-raw_socket socket-udp' MODE='test'
+      env: FEATURES='std phy-raw_socket proto-ipv6 socket-udp' MODE='test'
+    - rust: nightly
+      env: FEATURES='std phy-tap_interface proto-ipv6 socket-udp' MODE='test'
     - rust: nightly
-      env: FEATURES='std phy-tap_interface socket-udp' MODE='test'
+      env: FEATURES='std proto-ipv4 socket-raw' MODE='test'
     - rust: nightly
-      env: FEATURES='std socket-raw' MODE='test'
+      env: FEATURES='std proto-ipv6 socket-udp' MODE='test'
     - rust: nightly
-      env: FEATURES='std socket-udp' MODE='test'
+      env: FEATURES='std proto-ipv6 socket-tcp' MODE='test'
     - rust: nightly
-      env: FEATURES='std socket-tcp' MODE='test'
+      env: FEATURES='std proto-ipv4 socket-icmp socket-tcp' MODE='test'
     - rust: nightly
-      env: FEATURES='std socket-icmp' MODE='test'
+      env: FEATURES='std proto-ipv6 socket-icmp socket-tcp' MODE='test'
     ### Test select feature permutations, chosen to be as aggressive as possible
     - rust: nightly
-      env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp std' MODE='test'
+      env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp std'
+        MODE='test'
     - rust: nightly
-      env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp alloc' MODE='test'
+      env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc'
+        MODE='test'
     - rust: nightly
-      env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp' MODE='build'
+      env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp'
+        MODE='build'
 script:
   - cargo "$MODE" --no-default-features --features "$FEATURES"
 notifications:

+ 9 - 7
Cargo.toml

@@ -34,6 +34,7 @@ alloc = ["managed/alloc"]
 verbose = []
 "phy-raw_socket" = ["std", "libc"]
 "phy-tap_interface" = ["std", "libc"]
+"proto-ipv4" = []
 "proto-ipv6" = []
 "socket-raw" = []
 "socket-udp" = []
@@ -42,36 +43,37 @@ verbose = []
 default = [
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
   "phy-raw_socket", "phy-tap_interface",
+  "proto-ipv4", "proto-ipv6",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp"
 ]
 
 [[example]]
 name = "tcpdump"
-required-features = ["std", "phy-raw_socket"]
+required-features = ["std", "phy-raw_socket", "proto-ipv4"]
 
 [[example]]
 name = "httpclient"
-required-features = ["std", "phy-tap_interface", "socket-tcp"]
+required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp"]
 
 [[example]]
 name = "ping"
-required-features = ["std", "phy-tap_interface", "socket-icmp"]
+required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-icmp"]
 
 [[example]]
 name = "server"
-required-features = ["std", "phy-tap_interface", "socket-tcp", "socket-udp"]
+required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
 
 [[example]]
 name = "client"
-required-features = ["std", "phy-tap_interface", "socket-tcp", "socket-udp"]
+required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
 
 [[example]]
 name = "loopback"
-required-features = ["log", "socket-tcp"]
+required-features = ["log", "proto-ipv4", "socket-tcp"]
 
 [[example]]
 name = "benchmark"
-required-features = ["std", "phy-tap_interface", "socket-tcp"]
+required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp"]
 
 [profile.release]
 debug = 2

+ 7 - 0
README.md

@@ -145,6 +145,13 @@ and `smoltcp::socket::TcpSocket`, respectively.
 
 These features are enabled by default.
 
+### Features `proto-ipv4` and `proto-ipv6`
+
+Enable [IPv4] and [IPv6] respectively.
+
+[IPv4]: https://tools.ietf.org/rfc/rfc791.txt
+[IPv6]: https://tools.ietf.org/rfc/rfc8200.txt
+
 ## Hosted usage examples
 
 _smoltcp_, being a freestanding networking stack, needs to be able to transmit and receive

+ 50 - 14
src/iface/ethernet.rs

@@ -8,10 +8,14 @@ use {Error, Result};
 use phy::{Device, DeviceCapabilities, RxToken, TxToken};
 use wire::pretty_print::PrettyPrinter;
 use wire::{EthernetAddress, EthernetProtocol, EthernetFrame};
+#[cfg(feature = "proto-ipv4")]
 use wire::{Ipv4Address};
 use wire::{IpAddress, IpProtocol, IpRepr, IpCidr};
+#[cfg(feature = "proto-ipv4")]
 use wire::{ArpPacket, ArpRepr, ArpOperation};
+#[cfg(feature = "proto-ipv4")]
 use wire::{Ipv4Packet, Ipv4Repr};
+#[cfg(feature = "proto-ipv4")]
 use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
 #[cfg(feature = "socket-udp")]
 use wire::{UdpPacket, UdpRepr};
@@ -21,7 +25,7 @@ use wire::{TcpPacket, TcpRepr, TcpControl};
 use socket::{Socket, SocketSet, AnySocket};
 #[cfg(feature = "socket-raw")]
 use socket::RawSocket;
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 use socket::IcmpSocket;
 #[cfg(feature = "socket-udp")]
 use socket::UdpSocket;
@@ -50,6 +54,7 @@ struct InterfaceInner<'b, 'c> {
     neighbor_cache:         NeighborCache<'b>,
     ethernet_addr:          EthernetAddress,
     ip_addrs:               ManagedSlice<'c, IpCidr>,
+    #[cfg(feature = "proto-ipv4")]
     ipv4_gateway:           Option<Ipv4Address>,
     device_capabilities:    DeviceCapabilities,
 }
@@ -61,6 +66,7 @@ pub struct InterfaceBuilder <'b, 'c, DeviceT: for<'d> Device<'d>> {
     ethernet_addr:       Option<EthernetAddress>,
     neighbor_cache:      Option<NeighborCache<'b>>,
     ip_addrs:            ManagedSlice<'c, IpCidr>,
+    #[cfg(feature = "proto-ipv4")]
     ipv4_gateway:        Option<Ipv4Address>,
 }
 
@@ -97,6 +103,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT>
             ethernet_addr:       None,
             neighbor_cache:      None,
             ip_addrs:            ManagedSlice::Borrowed(&mut []),
+            #[cfg(feature = "proto-ipv4")]
             ipv4_gateway:        None
         }
     }
@@ -122,7 +129,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT>
     ///
     /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs
     pub fn ip_addrs<T>(mut self, ip_addrs: T) -> InterfaceBuilder<'b, 'c, DeviceT>
-            where T: Into<ManagedSlice<'c, IpCidr>>
+        where T: Into<ManagedSlice<'c, IpCidr>>
     {
         let ip_addrs = ip_addrs.into();
         InterfaceInner::check_ip_addrs(&ip_addrs);
@@ -137,8 +144,9 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT>
     /// This function panics if the given address is not unicast.
     ///
     /// [ipv4_gateway]: struct.EthernetInterface.html#method.ipv4_gateway
+    #[cfg(feature = "proto-ipv4")]
     pub fn ipv4_gateway<T>(mut self, gateway: T) -> InterfaceBuilder<'b, 'c, DeviceT>
-            where T: Into<Ipv4Address>
+        where T: Into<Ipv4Address>
     {
         let addr = gateway.into();
         InterfaceInner::check_gateway_addr(&addr);
@@ -172,7 +180,9 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT>
                     device: self.device,
                     inner: InterfaceInner {
                         ethernet_addr, device_capabilities, neighbor_cache,
-                        ip_addrs: self.ip_addrs, ipv4_gateway: self.ipv4_gateway,
+                        ip_addrs: self.ip_addrs,
+                        #[cfg(feature = "proto-ipv4")]
+                        ipv4_gateway: self.ipv4_gateway,
                     }
                 }
             },
@@ -184,7 +194,9 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT>
 #[derive(Debug, PartialEq)]
 enum Packet<'a> {
     None,
+    #[cfg(feature = "proto-ipv4")]
     Arp(ArpRepr),
+    #[cfg(feature = "proto-ipv4")]
     Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
     #[cfg(feature = "socket-raw")]
     Raw((IpRepr, &'a [u8])),
@@ -197,7 +209,10 @@ enum Packet<'a> {
 impl<'a> Packet<'a> {
     fn neighbor_addr(&self) -> Option<IpAddress> {
         match self {
-            &Packet::None | &Packet::Arp(_) => None,
+            &Packet::None => None,
+            #[cfg(feature = "proto-ipv4")]
+            &Packet::Arp(_) => None,
+            #[cfg(feature = "proto-ipv4")]
             &Packet::Icmpv4((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()),
             #[cfg(feature = "socket-raw")]
             &Packet::Raw((ref ip_repr, _)) => Some(ip_repr.dst_addr()),
@@ -245,6 +260,7 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
     }
 
     /// Get the IPv4 gateway of the interface.
+    #[cfg(feature = "proto-ipv4")]
     pub fn ipv4_gateway(&self) -> Option<Ipv4Address> {
         self.inner.ipv4_gateway
     }
@@ -253,6 +269,7 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
     ///
     /// # Panics
     /// This function panics if the given address is not unicast.
+    #[cfg(feature = "proto-ipv4")]
     pub fn set_ipv4_gateway<GatewayAddrT>(&mut self, gateway: GatewayAddrT)
             where GatewayAddrT: Into<Option<Ipv4Address>> {
         self.inner.ipv4_gateway = gateway.into();
@@ -376,11 +393,12 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
                             device_result = inner.dispatch(tx_token, timestamp, response);
                             device_result
                         }, &caps.checksum),
-                    #[cfg(feature = "socket-icmp")]
+                    #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
                     Socket::Icmp(ref mut socket) =>
                         socket.dispatch(&caps, |response| {
                             let tx_token = device.transmit().ok_or(Error::Exhausted)?;
                             device_result = match response {
+                                #[cfg(feature = "proto-ipv4")]
                                 (IpRepr::Ipv4(ipv4_repr), icmpv4_repr) => {
                                     let response = Packet::Icmpv4((ipv4_repr, icmpv4_repr));
                                     neighbor_addr = response.neighbor_addr();
@@ -449,6 +467,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn check_gateway_addr(addr: &Ipv4Address) {
         if !addr.is_unicast() {
             panic!("gateway IP address {} is not unicast", addr);
@@ -474,8 +493,10 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
 
         match eth_frame.ethertype() {
+            #[cfg(feature = "proto-ipv4")]
             EthernetProtocol::Arp =>
                 self.process_arp(timestamp, &eth_frame),
+            #[cfg(feature = "proto-ipv4")]
             EthernetProtocol::Ipv4 =>
                 self.process_ipv4(sockets, timestamp, &eth_frame),
             // Drop all other traffic.
@@ -483,6 +504,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn process_arp<'frame, T: AsRef<[u8]>>
                   (&mut self, timestamp: u64, eth_frame: &EthernetFrame<&'frame T>) ->
                   Result<Packet<'frame>>
@@ -524,6 +546,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn process_ipv4<'frame, T: AsRef<[u8]>>
                    (&mut self, sockets: &mut SocketSet, timestamp: u64,
                     eth_frame: &EthernetFrame<&'frame T>) ->
@@ -603,6 +626,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
                               ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
     {
@@ -613,7 +637,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         #[cfg(feature = "socket-icmp")]
         let mut handled_by_icmp_socket = false;
 
-        #[cfg(feature = "socket-icmp")]
+        #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
         for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) {
             if !icmp_socket.accepts(&ip_repr, &icmp_repr, &checksum_caps) { continue }
 
@@ -654,6 +678,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn icmpv4_reply<'frame, 'icmp: 'frame>
                    (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) ->
                    Packet<'frame>
@@ -696,6 +721,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
 
         // The packet wasn't handled by a socket, send an ICMP port unreachable packet.
         match ip_repr {
+            #[cfg(feature = "proto-ipv4")]
             IpRepr::Ipv4(ipv4_repr) => {
                 // Send back as much of the original payload as we can
                 let payload_len = cmp::min(
@@ -710,8 +736,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
             #[cfg(feature = "proto-ipv6")]
             IpRepr::Ipv6(_) => Err(Error::Unaddressable),
             IpRepr::Unspecified { .. } |
-            IpRepr::__Nonexhaustive =>
-                unreachable!()
+            IpRepr::__Nonexhaustive => Err(Error::Unaddressable),
         }
     }
 
@@ -752,6 +777,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
     {
         let checksum_caps = self.device_capabilities.checksum.clone();
         match packet {
+            #[cfg(feature = "proto-ipv4")]
             Packet::Arp(arp_repr) => {
                 let dst_hardware_addr =
                     match arp_repr {
@@ -767,6 +793,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
                     arp_repr.emit(&mut packet);
                 })
             },
+            #[cfg(feature = "proto-ipv4")]
             Packet::Icmpv4((ipv4_repr, icmpv4_repr)) => {
                 self.dispatch_ip(tx_token, timestamp, IpRepr::Ipv4(ipv4_repr),
                                  |_ip_repr, payload| {
@@ -849,8 +876,12 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
 
         // Route via a gateway.
-        match (addr, self.ipv4_gateway) {
-            (&IpAddress::Ipv4(_), Some(gateway)) => Ok(gateway.into()),
+        match addr {
+            #[cfg(feature = "proto-ipv4")]
+            &IpAddress::Ipv4(_) => match self.ipv4_gateway {
+                Some(gateway) => Ok(gateway.into()),
+                None => Err(Error::Unaddressable),
+            }
             _ => Err(Error::Unaddressable)
         }
     }
@@ -882,6 +913,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         }
 
         match (src_addr, dst_addr) {
+            #[cfg(feature = "proto-ipv4")]
             (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
                 net_debug!("address {} not in neighbor cache, sending ARP request",
                            dst_addr);
@@ -903,7 +935,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
 
                 Err(Error::Unaddressable)
             }
-            _ => unreachable!()
+            _ => Err(Error::Unaddressable)
         }
     }
 
@@ -921,8 +953,11 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| {
             frame.set_dst_addr(dst_hardware_addr);
             match ip_repr {
+                #[cfg(feature = "proto-ipv4")]
                 IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4),
-                _ => unreachable!()
+                #[cfg(feature = "proto-ipv6")]
+                IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6),
+                _ => return
             }
 
             ip_repr.emit(frame.payload_mut(), &checksum_caps);
@@ -934,6 +969,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
 }
 
 #[cfg(test)]
+#[cfg(feature = "proto-ipv4")]
 mod test {
     use std::collections::BTreeMap;
     use {Result, Error};
@@ -1300,7 +1336,7 @@ mod test {
     }
 
     #[test]
-    #[cfg(feature = "socket-icmp")]
+    #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
     fn test_icmpv4_socket() {
         use socket::{IcmpPacketBuffer, IcmpSocket, IcmpSocketBuffer, IcmpEndpoint};
         use wire::Icmpv4Packet;

+ 32 - 33
src/iface/neighbor.rs

@@ -157,81 +157,80 @@ impl<'a> Cache<'a> {
 
 #[cfg(test)]
 mod test {
-    use wire::Ipv4Address;
     use super::*;
+    use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
 
     const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
     const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
     const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
     const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
 
-    const PADDR_A: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 1]));
-    const PADDR_B: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 2]));
-    const PADDR_C: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 3]));
-    const PADDR_D: IpAddress = IpAddress::Ipv4(Ipv4Address([1, 0, 0, 4]));
-
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_fill() {
         let mut cache_storage = [Default::default(); 3];
         let mut cache = Cache::new(&mut cache_storage[..]);
 
-        assert_eq!(cache.lookup_pure(&PADDR_A, 0), None);
-        assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 0), None);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, 0), None);
 
-        cache.fill(PADDR_A, HADDR_A, 0);
-        assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
-        assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
-        assert_eq!(cache.lookup_pure(&PADDR_A, 2 * Cache::ENTRY_LIFETIME), None);
+        cache.fill(MOCK_IP_ADDR_1, HADDR_A, 0);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 0), Some(HADDR_A));
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, 0), None);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 2 * Cache::ENTRY_LIFETIME), None);
 
-        cache.fill(PADDR_A, HADDR_A, 0);
-        assert_eq!(cache.lookup_pure(&PADDR_B, 0), None);
+        cache.fill(MOCK_IP_ADDR_1, HADDR_A, 0);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, 0), None);
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_expire() {
         let mut cache_storage = [Default::default(); 3];
         let mut cache = Cache::new(&mut cache_storage[..]);
 
-        cache.fill(PADDR_A, HADDR_A, 0);
-        assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
-        assert_eq!(cache.lookup_pure(&PADDR_A, 2 * Cache::ENTRY_LIFETIME), None);
+        cache.fill(MOCK_IP_ADDR_1, HADDR_A, 0);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 0), Some(HADDR_A));
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 2 * Cache::ENTRY_LIFETIME), None);
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_replace() {
         let mut cache_storage = [Default::default(); 3];
         let mut cache = Cache::new(&mut cache_storage[..]);
 
-        cache.fill(PADDR_A, HADDR_A, 0);
-        assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_A));
-        cache.fill(PADDR_A, HADDR_B, 0);
-        assert_eq!(cache.lookup_pure(&PADDR_A, 0), Some(HADDR_B));
+        cache.fill(MOCK_IP_ADDR_1, HADDR_A, 0);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 0), Some(HADDR_A));
+        cache.fill(MOCK_IP_ADDR_1, HADDR_B, 0);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, 0), Some(HADDR_B));
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_evict() {
         let mut cache_storage = [Default::default(); 3];
         let mut cache = Cache::new(&mut cache_storage[..]);
 
-        cache.fill(PADDR_A, HADDR_A, 100);
-        cache.fill(PADDR_B, HADDR_B, 50);
-        cache.fill(PADDR_C, HADDR_C, 200);
-        assert_eq!(cache.lookup_pure(&PADDR_B, 1000), Some(HADDR_B));
-        assert_eq!(cache.lookup_pure(&PADDR_D, 1000), None);
+        cache.fill(MOCK_IP_ADDR_1, HADDR_A, 100);
+        cache.fill(MOCK_IP_ADDR_2, HADDR_B, 50);
+        cache.fill(MOCK_IP_ADDR_3, HADDR_C, 200);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, 1000), Some(HADDR_B));
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, 1000), None);
 
-        cache.fill(PADDR_D, HADDR_D, 300);
-        assert_eq!(cache.lookup_pure(&PADDR_B, 1000), None);
-        assert_eq!(cache.lookup_pure(&PADDR_D, 1000), Some(HADDR_D));
+        cache.fill(MOCK_IP_ADDR_4, HADDR_D, 300);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, 1000), None);
+        assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, 1000), Some(HADDR_D));
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_hush() {
         let mut cache_storage = [Default::default(); 3];
         let mut cache = Cache::new(&mut cache_storage[..]);
 
-        assert_eq!(cache.lookup(&PADDR_A, 0), Answer::NotFound);
-        assert_eq!(cache.lookup(&PADDR_A, 100), Answer::RateLimited);
-        assert_eq!(cache.lookup(&PADDR_A, 2000), Answer::NotFound);
+        assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, 0), Answer::NotFound);
+        assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, 100), Answer::RateLimited);
+        assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, 2000), Answer::NotFound);
     }
 }
-

+ 3 - 1
src/lib.rs

@@ -1,6 +1,8 @@
 #![cfg_attr(feature = "alloc", feature(alloc))]
 #![no_std]
-#![deny(unsafe_code, unused)]
+#![deny(unsafe_code)]
+// TODO: Change this to enable deny(unused) if IPv6 or IPv4 are enabled
+#![cfg_attr(feature = "proto-ipv4", 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

+ 26 - 10
src/parsers.rs

@@ -1,9 +1,10 @@
-#![cfg_attr(not(feature = "proto-ipv6"), allow(dead_code))]
+#![cfg_attr(not(all(feature = "proto-ipv6", feature = "proto-ipv4")), allow(dead_code))]
 
 use core::str::FromStr;
 use core::result;
 
 use wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint};
+#[cfg(feature = "proto-ipv4")]
 use wire::{Ipv4Address, Ipv4Cidr};
 #[cfg(feature = "proto-ipv6")]
 use wire::{Ipv6Address, Ipv6Cidr};
@@ -236,6 +237,7 @@ impl<'a> Parser<'a> {
         Ok(Ipv6Address::from_parts(&addr))
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn accept_ipv4(&mut self) -> Result<Ipv4Address> {
         let mut octets = [0u8; 4];
         for n in 0..4 {
@@ -248,8 +250,10 @@ impl<'a> Parser<'a> {
     }
 
     fn accept_ip(&mut self) -> Result<IpAddress> {
-        if let Some(ipv4) = self.try(|p| p.accept_ipv4()) {
-            return Ok(IpAddress::Ipv4(ipv4))
+        #[cfg(feature = "proto-ipv4")]
+        match self.try(|p| p.accept_ipv4()) {
+            Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
+            None => ()
         }
 
         #[cfg(feature = "proto-ipv6")]
@@ -261,6 +265,7 @@ impl<'a> Parser<'a> {
         Err(())
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn accept_ipv4_endpoint(&mut self) -> Result<IpEndpoint> {
         let ip = self.accept_ipv4()?;
 
@@ -275,7 +280,7 @@ impl<'a> Parser<'a> {
     }
 
     #[cfg(feature = "proto-ipv6")]
-    fn accept_ipv6_endpoint(&mut self, is_cidr: bool) -> Result<IpEndpoint> {
+    fn accept_ipv6_endpoint(&mut self) -> Result<IpEndpoint> {
         if self.lookahead_char(b'[') {
             self.accept_char(b'[')?;
             let ip = self.accept_ipv6(false)?;
@@ -285,18 +290,20 @@ impl<'a> Parser<'a> {
 
             Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: port as u16 })
         } else {
-            let ip = self.accept_ipv6(is_cidr)?;
+            let ip = self.accept_ipv6(false)?;
             Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0 })
         }
     }
 
     fn accept_ip_endpoint(&mut self) -> Result<IpEndpoint> {
-        if let Some(ipv4) = self.try(|p| p.accept_ipv4_endpoint()) {
-            return Ok(ipv4)
+        #[cfg(feature = "proto-ipv4")]
+        match self.try(|p| p.accept_ipv4_endpoint()) {
+            Some(ipv4) => return Ok(ipv4),
+            None => ()
         }
 
         #[cfg(feature = "proto-ipv6")]
-        match self.try(|p| p.accept_ipv6_endpoint(false)) {
+        match self.try(|p| p.accept_ipv6_endpoint()) {
             Some(ipv6) => return Ok(ipv6),
             None => ()
         }
@@ -314,6 +321,7 @@ impl FromStr for EthernetAddress {
     }
 }
 
+#[cfg(feature = "proto-ipv4")]
 impl FromStr for Ipv4Address {
     type Err = ();
 
@@ -342,6 +350,7 @@ impl FromStr for IpAddress {
     }
 }
 
+#[cfg(feature = "proto-ipv4")]
 impl FromStr for Ipv4Cidr {
     type Err = ();
 
@@ -377,8 +386,10 @@ impl FromStr for IpCidr {
 
     /// Parse a string representation of an IP CIDR.
     fn from_str(s: &str) -> Result<IpCidr> {
-        if let Ok(ipv4) = Ipv4Cidr::from_str(s) {
-            return Ok(IpCidr::Ipv4(ipv4))
+        #[cfg(feature = "proto-ipv4")]
+        match Ipv4Cidr::from_str(s) {
+            Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
+            Err(_) => ()
         }
 
         #[cfg(feature = "proto-ipv6")]
@@ -419,6 +430,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_mac() {
         assert_eq!(EthernetAddress::from_str(""), Err(()));
         assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),
@@ -440,6 +452,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_ipv4() {
         assert_eq!(Ipv4Address::from_str(""), Err(()));
         assert_eq!(Ipv4Address::from_str("1.2.3.4"),
@@ -500,6 +513,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_ip_ipv4() {
         assert_eq!(IpAddress::from_str(""), Err(()));
         assert_eq!(IpAddress::from_str("1.2.3.4"),
@@ -517,6 +531,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_cidr_ipv4() {
         let tests = [
             ("127.0.0.1/8",
@@ -564,6 +579,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_endpoint_ipv4() {
         assert_eq!(IpEndpoint::from_str(""), Err(()));
         assert_eq!(IpEndpoint::from_str("x"), Err(()));

+ 5 - 5
src/socket/mod.rs

@@ -15,7 +15,7 @@ use core::marker::PhantomData;
 mod meta;
 #[cfg(feature = "socket-raw")]
 mod raw;
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 mod icmp;
 #[cfg(feature = "socket-udp")]
 mod udp;
@@ -31,7 +31,7 @@ pub use self::raw::{PacketBuffer as RawPacketBuffer,
                     SocketBuffer as RawSocketBuffer,
                     RawSocket};
 
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 pub use self::icmp::{PacketBuffer as IcmpPacketBuffer,
                      SocketBuffer as IcmpSocketBuffer,
                      Endpoint as IcmpEndpoint,
@@ -67,7 +67,7 @@ pub(crate) use self::ref_::Session as SocketSession;
 pub enum Socket<'a, 'b: 'a> {
     #[cfg(feature = "socket-raw")]
     Raw(RawSocket<'a, 'b>),
-    #[cfg(feature = "socket-icmp")]
+    #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
     Icmp(IcmpSocket<'a, 'b>),
     #[cfg(feature = "socket-udp")]
     Udp(UdpSocket<'a, 'b>),
@@ -82,7 +82,7 @@ macro_rules! dispatch_socket {
         match $self_ {
             #[cfg(feature = "socket-raw")]
             &$( $mut_ )* Socket::Raw(ref $( $mut_ )* $socket) => $code,
-            #[cfg(feature = "socket-icmp")]
+            #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
             &$( $mut_ )* Socket::Icmp(ref $( $mut_ )* $socket) => $code,
             #[cfg(feature = "socket-udp")]
             &$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code,
@@ -141,7 +141,7 @@ macro_rules! from_socket {
 
 #[cfg(feature = "socket-raw")]
 from_socket!(RawSocket<'a, 'b>, Raw);
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 from_socket!(IcmpSocket<'a, 'b>, Icmp);
 #[cfg(feature = "socket-udp")]
 from_socket!(UdpSocket<'a, 'b>, Udp);

+ 73 - 55
src/socket/raw.rs

@@ -3,7 +3,9 @@ use managed::Managed;
 
 use {Error, Result};
 use phy::ChecksumCapabilities;
-use wire::{IpVersion, IpRepr, IpProtocol, Ipv4Repr, Ipv4Packet};
+use wire::{IpVersion, IpRepr, IpProtocol};
+#[cfg(feature = "proto-ipv4")]
+use wire::{Ipv4Repr, Ipv4Packet};
 use socket::{Socket, SocketMeta, SocketHandle};
 use storage::{Resettable, RingBuffer};
 
@@ -187,6 +189,7 @@ impl<'a, 'b> RawSocket<'a, 'b> {
         fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
                    checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
             match IpVersion::of_packet(buffer.as_ref())? {
+                #[cfg(feature = "proto-ipv4")]
                 IpVersion::Ipv4 => {
                     let mut packet = Ipv4Packet::new_checked(buffer.as_mut())?;
                     if packet.protocol() != protocol { return Err(Error::Unaddressable) }
@@ -241,6 +244,7 @@ impl<'a, 'b> RawSocket<'a, 'b> {
 
 #[cfg(test)]
 mod test {
+    #[cfg(feature = "proto-ipv4")]
     use wire::{Ipv4Address, IpRepr, Ipv4Repr};
     use super::*;
 
@@ -252,81 +256,89 @@ mod test {
         SocketBuffer::new(storage)
     }
 
-    fn socket(rx_buffer: SocketBuffer<'static, 'static>,
-              tx_buffer: SocketBuffer<'static, 'static>)
-            -> RawSocket<'static, 'static> {
-        match RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
-                             rx_buffer, tx_buffer) {
-            Socket::Raw(socket) => socket,
-            _ => unreachable!()
+    #[cfg(feature = "proto-ipv4")]
+    mod ipv4_locals {
+        use super::*;
+
+        pub fn socket(rx_buffer: SocketBuffer<'static, 'static>,
+                  tx_buffer: SocketBuffer<'static, 'static>)
+                -> RawSocket<'static, 'static> {
+            match RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
+                                 rx_buffer, tx_buffer) {
+                Socket::Raw(socket) => socket,
+                _ => unreachable!()
+            }
         }
-    }
 
-    const IP_PROTO: u8 = 63;
-    const HEADER_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
-        src_addr: Ipv4Address([10, 0, 0, 1]),
-        dst_addr: Ipv4Address([10, 0, 0, 2]),
-        protocol: IpProtocol::Unknown(IP_PROTO),
-        payload_len: 4,
-        hop_limit: 64
-    });
-    const PACKET_BYTES: [u8; 24] = [
-        0x45, 0x00, 0x00, 0x18,
-        0x00, 0x00, 0x40, 0x00,
-        0x40, 0x3f, 0x00, 0x00,
-        0x0a, 0x00, 0x00, 0x01,
-        0x0a, 0x00, 0x00, 0x02,
-        0xaa, 0x00, 0x00, 0xff
-    ];
-    const PACKET_PAYLOAD: [u8; 4] = [
-        0xaa, 0x00, 0x00, 0xff
-    ];
+        pub const IP_PROTO: u8 = 63;
+        pub const HEADER_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
+            src_addr: Ipv4Address([10, 0, 0, 1]),
+            dst_addr: Ipv4Address([10, 0, 0, 2]),
+            protocol: IpProtocol::Unknown(IP_PROTO),
+            payload_len: 4,
+            hop_limit: 64
+        });
+        pub const PACKET_BYTES: [u8; 24] = [
+            0x45, 0x00, 0x00, 0x18,
+            0x00, 0x00, 0x40, 0x00,
+            0x40, 0x3f, 0x00, 0x00,
+            0x0a, 0x00, 0x00, 0x01,
+            0x0a, 0x00, 0x00, 0x02,
+            0xaa, 0x00, 0x00, 0xff
+        ];
+        pub const PACKET_PAYLOAD: [u8; 4] = [
+            0xaa, 0x00, 0x00, 0xff
+        ];
+    }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_send_truncated() {
-        let mut socket = socket(buffer(0), buffer(1));
+        let mut socket = ipv4_locals::socket(buffer(0), buffer(1));
         assert_eq!(socket.send_slice(&[0; 32][..]), Err(Error::Truncated));
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_send_dispatch() {
-        let mut socket = socket(buffer(0), buffer(1));
+        let mut socket = ipv4_locals::socket(buffer(0), buffer(1));
 
         assert!(socket.can_send());
         assert_eq!(socket.dispatch(|_| unreachable!(), &ChecksumCapabilities::default()),
                    Err(Error::Exhausted));
 
-        assert_eq!(socket.send_slice(&PACKET_BYTES[..]), Ok(()));
+        assert_eq!(socket.send_slice(&ipv4_locals::PACKET_BYTES[..]), Ok(()));
         assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
         assert_eq!(socket.dispatch(|(ip_repr, ip_payload)| {
-            assert_eq!(ip_repr, HEADER_REPR);
-            assert_eq!(ip_payload, &PACKET_PAYLOAD);
+            assert_eq!(ip_repr, ipv4_locals::HEADER_REPR);
+            assert_eq!(ip_payload, &ipv4_locals::PACKET_PAYLOAD);
             Err(Error::Unaddressable)
         }, &ChecksumCapabilities::default()), Err(Error::Unaddressable));
         assert!(!socket.can_send());
 
         assert_eq!(socket.dispatch(|(ip_repr, ip_payload)| {
-            assert_eq!(ip_repr, HEADER_REPR);
-            assert_eq!(ip_payload, &PACKET_PAYLOAD);
+            assert_eq!(ip_repr, ipv4_locals::HEADER_REPR);
+            assert_eq!(ip_payload, &ipv4_locals::PACKET_PAYLOAD);
             Ok(())
         }, &ChecksumCapabilities::default()), Ok(()));
         assert!(socket.can_send());
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_send_illegal() {
-        let mut socket = socket(buffer(0), buffer(1));
+        let mut socket = ipv4_locals::socket(buffer(0), buffer(1));
 
-        let mut wrong_version = PACKET_BYTES.clone();
+        let mut wrong_version = ipv4_locals::PACKET_BYTES.clone();
         Ipv4Packet::new(&mut wrong_version).set_version(5);
 
         assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
         assert_eq!(socket.dispatch(|_| unreachable!(), &ChecksumCapabilities::default()),
                    Ok(()));
 
-        let mut wrong_protocol = PACKET_BYTES.clone();
+        let mut wrong_protocol = ipv4_locals::PACKET_BYTES.clone();
         Ipv4Packet::new(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
 
         assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
@@ -335,59 +347,65 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_recv_process() {
-        let mut socket = socket(buffer(1), buffer(0));
+        let mut socket = ipv4_locals::socket(buffer(1), buffer(0));
         assert!(!socket.can_recv());
 
-        let mut cksumd_packet = PACKET_BYTES.clone();
+        let mut cksumd_packet = ipv4_locals::PACKET_BYTES.clone();
         Ipv4Packet::new(&mut cksumd_packet).fill_checksum();
 
         assert_eq!(socket.recv(), Err(Error::Exhausted));
-        assert!(socket.accepts(&HEADER_REPR));
-        assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
+        assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
+        assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
+                                  &ChecksumCapabilities::default()),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&HEADER_REPR));
-        assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
+        assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
+        assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
+                                  &ChecksumCapabilities::default()),
                    Err(Error::Exhausted));
         assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
         assert!(!socket.can_recv());
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_recv_truncated_slice() {
-        let mut socket = socket(buffer(1), buffer(0));
+        let mut socket = ipv4_locals::socket(buffer(1), buffer(0));
 
-        assert!(socket.accepts(&HEADER_REPR));
-        assert_eq!(socket.process(&HEADER_REPR, &PACKET_PAYLOAD, &ChecksumCapabilities::default()),
-                   Ok(()));
+        assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
+        assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
+                                  &ChecksumCapabilities::default()), Ok(()));
 
         let mut slice = [0; 4];
         assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4));
-        assert_eq!(&slice, &PACKET_BYTES[..slice.len()]);
+        assert_eq!(&slice, &ipv4_locals::PACKET_BYTES[..slice.len()]);
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_recv_truncated_packet() {
-        let mut socket = socket(buffer(1), buffer(0));
+        let mut socket = ipv4_locals::socket(buffer(1), buffer(0));
 
         let mut buffer = vec![0; 128];
-        buffer[..PACKET_BYTES.len()].copy_from_slice(&PACKET_BYTES[..]);
+        buffer[..ipv4_locals::PACKET_BYTES.len()].copy_from_slice(&ipv4_locals::PACKET_BYTES[..]);
 
-        assert!(socket.accepts(&HEADER_REPR));
-        assert_eq!(socket.process(&HEADER_REPR, &buffer, &ChecksumCapabilities::default()),
+        assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
+        assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &buffer, &ChecksumCapabilities::default()),
                    Err(Error::Truncated));
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_doesnt_accept_wrong_proto() {
         let socket = match RawSocket::new(IpVersion::Ipv4,
-                                          IpProtocol::Unknown(IP_PROTO+1),
+                                          IpProtocol::Unknown(ipv4_locals::IP_PROTO+1),
                                           buffer(1), buffer(1)) {
             Socket::Raw(socket) => socket,
             _ => unreachable!()
         };
-        assert!(!socket.accepts(&HEADER_REPR));
+        assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
     }
 }

+ 2 - 2
src/socket/ref_.rs

@@ -2,7 +2,7 @@ use core::ops::{Deref, DerefMut};
 
 #[cfg(feature = "socket-raw")]
 use socket::RawSocket;
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 use socket::IcmpSocket;
 #[cfg(feature = "socket-udp")]
 use socket::UdpSocket;
@@ -21,7 +21,7 @@ pub trait Session {
 
 #[cfg(feature = "socket-raw")]
 impl<'a, 'b> Session for RawSocket<'a, 'b> {}
-#[cfg(feature = "socket-icmp")]
+#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
 impl<'a, 'b> Session for IcmpSocket<'a, 'b> {}
 #[cfg(feature = "socket-udp")]
 impl<'a, 'b> Session for UdpSocket<'a, 'b> {}

+ 1 - 1
src/socket/set.rs

@@ -140,7 +140,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> {
                     #[cfg(feature = "socket-raw")]
                     &mut Socket::Raw(_) =>
                         may_remove = true,
-                    #[cfg(feature = "socket-icmp")]
+                    #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
                     &mut Socket::Icmp(_) =>
                         may_remove = true,
                     #[cfg(feature = "socket-udp")]

+ 166 - 147
src/socket/tcp.rs

@@ -1498,39 +1498,23 @@ impl<'a> fmt::Write for TcpSocket<'a> {
 #[cfg(test)]
 mod test {
     use core::i32;
-    use wire::{IpAddress, IpRepr};
-    use wire::{Ipv4Address, IpCidr, Ipv4Repr};
+    use wire::{IpAddress, IpRepr, IpCidr};
+    use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED};
     use super::*;
 
-    #[test]
-    fn test_timer_retransmit() {
-        let mut r = Timer::default();
-        assert_eq!(r.should_retransmit(1000), None);
-        r.set_for_retransmit(1000);
-        assert_eq!(r.should_retransmit(1000), None);
-        assert_eq!(r.should_retransmit(1050), None);
-        assert_eq!(r.should_retransmit(1101), Some(101));
-        r.set_for_retransmit(1101);
-        assert_eq!(r.should_retransmit(1101), None);
-        assert_eq!(r.should_retransmit(1150), None);
-        assert_eq!(r.should_retransmit(1200), None);
-        assert_eq!(r.should_retransmit(1301), Some(300));
-        r.set_for_idle(1301, None);
-        assert_eq!(r.should_retransmit(1350), None);
-    }
+    // =========================================================================================//
+    // Constants
+    // =========================================================================================//
 
-    const LOCAL_IP:     IpAddress    = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 1]));
-    const REMOTE_IP:    IpAddress    = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 2]));
-    const OTHER_IP:     IpAddress    = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 3]));
     const LOCAL_PORT:   u16          = 80;
     const REMOTE_PORT:  u16          = 49500;
-    const LOCAL_END:    IpEndpoint   = IpEndpoint { addr: LOCAL_IP,  port: LOCAL_PORT  };
-    const REMOTE_END:   IpEndpoint   = IpEndpoint { addr: REMOTE_IP, port: REMOTE_PORT };
+    const LOCAL_END:    IpEndpoint   = IpEndpoint { addr: MOCK_IP_ADDR_1,  port: LOCAL_PORT  };
+    const REMOTE_END:   IpEndpoint   = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT };
     const LOCAL_SEQ:    TcpSeqNumber = TcpSeqNumber(10000);
     const REMOTE_SEQ:   TcpSeqNumber = TcpSeqNumber(-10000);
 
     const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified {
-        src_addr: LOCAL_IP, dst_addr: REMOTE_IP,
+        src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2,
         protocol: IpProtocol::Tcp, payload_len: 20,
         hop_limit: 64
     };
@@ -1542,7 +1526,7 @@ mod test {
         payload: &[]
     };
     const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified {
-        src_addr: REMOTE_IP, dst_addr: LOCAL_IP,
+        src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2,
         protocol: IpProtocol::Tcp, payload_len: 20,
         hop_limit: 64
     };
@@ -1554,11 +1538,20 @@ mod test {
         payload: &[]
     };
 
+    #[cfg(feature = "proto-ipv6")]
+    const BASE_MSS: u16 = 1460;
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    const BASE_MSS: u16 = 1480;
+
+    // =========================================================================================//
+    // Helper functions
+    // =========================================================================================//
+
     fn send(socket: &mut TcpSocket, timestamp: u64, repr: &TcpRepr) ->
            Result<Option<TcpRepr<'static>>> {
         let ip_repr = IpRepr::Unspecified {
-            src_addr:    REMOTE_IP,
-            dst_addr:    LOCAL_IP,
+            src_addr:    MOCK_IP_ADDR_2,
+            dst_addr:    MOCK_IP_ADDR_1,
             protocol:    IpProtocol::Tcp,
             payload_len: repr.buffer_len(),
             hop_limit:   64
@@ -1584,8 +1577,8 @@ mod test {
             let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap();
 
             assert_eq!(ip_repr.protocol(), IpProtocol::Tcp);
-            assert_eq!(ip_repr.src_addr(), LOCAL_IP);
-            assert_eq!(ip_repr.dst_addr(), REMOTE_IP);
+            assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1);
+            assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2);
             assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len());
 
             net_trace!("recv: {}", tcp_repr);
@@ -1682,6 +1675,102 @@ mod test {
         }
     }
 
+    fn socket_syn_received() -> TcpSocket<'static> {
+        let mut s = socket();
+        s.state           = State::SynReceived;
+        s.local_endpoint  = LOCAL_END;
+        s.remote_endpoint = REMOTE_END;
+        s.local_seq_no    = LOCAL_SEQ;
+        s.remote_seq_no   = REMOTE_SEQ + 1;
+        s.remote_last_seq = LOCAL_SEQ;
+        s.remote_win_len  = 256;
+        s
+    }
+
+    fn socket_syn_sent() -> TcpSocket<'static> {
+        let mut s = socket();
+        s.state           = State::SynSent;
+        s.local_endpoint  = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT);
+        s.remote_endpoint = REMOTE_END;
+        s.local_seq_no    = LOCAL_SEQ;
+        s.remote_last_seq = LOCAL_SEQ;
+        s
+    }
+
+    fn socket_established() -> TcpSocket<'static> {
+        let mut s = socket_syn_received();
+        s.state           = State::Established;
+        s.local_seq_no    = LOCAL_SEQ + 1;
+        s.remote_last_seq = LOCAL_SEQ + 1;
+        s.remote_last_ack = Some(REMOTE_SEQ + 1);
+        s.remote_last_win = 64;
+        s
+    }
+
+    fn socket_fin_wait_1() -> TcpSocket<'static> {
+        let mut s = socket_established();
+        s.state           = State::FinWait1;
+        s
+    }
+
+    fn socket_fin_wait_2() -> TcpSocket<'static> {
+        let mut s = socket_fin_wait_1();
+        s.state           = State::FinWait2;
+        s.local_seq_no    = LOCAL_SEQ + 1 + 1;
+        s.remote_last_seq = LOCAL_SEQ + 1 + 1;
+        s
+    }
+
+    fn socket_closing() -> TcpSocket<'static> {
+        let mut s = socket_fin_wait_1();
+        s.state           = State::Closing;
+        s.remote_last_seq = LOCAL_SEQ + 1 + 1;
+        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
+        s
+    }
+
+    fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> {
+        let mut s = socket_fin_wait_2();
+        s.state           = State::TimeWait;
+        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
+        if from_closing {
+            s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1);
+        }
+        s.timer           = Timer::Close { expires_at: 1_000 + CLOSE_DELAY };
+        s
+    }
+
+    fn socket_close_wait() -> TcpSocket<'static> {
+        let mut s = socket_established();
+        s.state           = State::CloseWait;
+        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
+        s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1);
+        s
+    }
+
+    fn socket_last_ack() -> TcpSocket<'static> {
+        let mut s = socket_close_wait();
+        s.state           = State::LastAck;
+        s
+    }
+
+    fn socket_recved() -> TcpSocket<'static> {
+        let mut s = socket_established();
+        send!(s, TcpRepr {
+            seq_number: REMOTE_SEQ + 1,
+            ack_number: Some(LOCAL_SEQ + 1),
+            payload:    &b"abcdef"[..],
+            ..SEND_TEMPL
+        });
+        recv!(s, [TcpRepr {
+            seq_number: LOCAL_SEQ + 1,
+            ack_number: Some(REMOTE_SEQ + 1 + 6),
+            window_len: 58,
+            ..RECV_TEMPL
+        }]);
+        s
+    }
+
     // =========================================================================================//
     // Tests for the CLOSED state.
     // =========================================================================================//
@@ -1795,17 +1884,6 @@ mod test {
     // =========================================================================================//
     // Tests for the SYN-RECEIVED state.
     // =========================================================================================//
-    fn socket_syn_received() -> TcpSocket<'static> {
-        let mut s = socket();
-        s.state           = State::SynReceived;
-        s.local_endpoint  = LOCAL_END;
-        s.remote_endpoint = REMOTE_END;
-        s.local_seq_no    = LOCAL_SEQ;
-        s.remote_seq_no   = REMOTE_SEQ + 1;
-        s.remote_last_seq = LOCAL_SEQ;
-        s.remote_win_len  = 256;
-        s
-    }
 
     #[test]
     fn test_syn_received_ack() {
@@ -1814,7 +1892,7 @@ mod test {
             control: TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
@@ -1833,7 +1911,7 @@ mod test {
             control: TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
@@ -1864,7 +1942,7 @@ mod test {
             control: TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
@@ -1888,24 +1966,15 @@ mod test {
     // =========================================================================================//
     // Tests for the SYN-SENT state.
     // =========================================================================================//
-    fn socket_syn_sent() -> TcpSocket<'static> {
-        let mut s = socket();
-        s.state           = State::SynSent;
-        s.local_endpoint  = IpEndpoint::new(IpAddress::v4(0, 0, 0, 0), LOCAL_PORT);
-        s.remote_endpoint = REMOTE_END;
-        s.local_seq_no    = LOCAL_SEQ;
-        s.remote_last_seq = LOCAL_SEQ;
-        s
-    }
 
     #[test]
     fn test_connect_validation() {
         let mut s = socket();
-        assert_eq!(s.connect((IpAddress::v4(0, 0, 0, 0), 80), LOCAL_END),
+        assert_eq!(s.connect((IpAddress::Unspecified, 80), LOCAL_END),
                    Err(Error::Unaddressable));
-        assert_eq!(s.connect(REMOTE_END, (IpAddress::v4(10, 0, 0, 0), 0)),
+        assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)),
                    Err(Error::Unaddressable));
-        assert_eq!(s.connect((IpAddress::v4(10, 0, 0, 0), 0), LOCAL_END),
+        assert_eq!(s.connect((MOCK_UNSPECIFIED, 0), LOCAL_END),
                    Err(Error::Unaddressable));
         assert_eq!(s.connect((IpAddress::Unspecified, 80), LOCAL_END),
                    Err(Error::Unaddressable));
@@ -1916,19 +1985,19 @@ mod test {
         let mut s = socket();
         s.local_seq_no = LOCAL_SEQ;
         s.connect(REMOTE_END, LOCAL_END.port).unwrap();
-        assert_eq!(s.local_endpoint, IpEndpoint::new(IpAddress::v4(0, 0, 0, 0), LOCAL_END.port));
+        assert_eq!(s.local_endpoint, IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port));
         recv!(s, [TcpRepr {
             control:    TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: None,
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
             control:    TcpControl::Syn,
             seq_number: REMOTE_SEQ,
             ack_number: Some(LOCAL_SEQ + 1),
-            max_seg_size: Some(1400),
+            max_seg_size: Some(BASE_MSS - 80),
             ..SEND_TEMPL
         });
         assert_eq!(s.local_endpoint, LOCAL_END);
@@ -1937,7 +2006,7 @@ mod test {
     #[test]
     fn test_connect_unspecified_local() {
         let mut s = socket();
-        assert_eq!(s.connect(REMOTE_END, (IpAddress::v4(0, 0, 0, 0), 80)),
+        assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)),
                    Ok(()));
         s.abort();
         assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)),
@@ -1948,7 +2017,7 @@ mod test {
     #[test]
     fn test_connect_specified_local() {
         let mut s = socket();
-        assert_eq!(s.connect(REMOTE_END, (IpAddress::v4(10, 0, 0, 2), 80)),
+        assert_eq!(s.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)),
                    Ok(()));
     }
 
@@ -1976,14 +2045,14 @@ mod test {
             control:    TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: None,
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
             control:    TcpControl::Syn,
             seq_number: REMOTE_SEQ,
             ack_number: Some(LOCAL_SEQ + 1),
-            max_seg_size: Some(1400),
+            max_seg_size: Some(BASE_MSS - 80),
             ..SEND_TEMPL
         });
         recv!(s, [TcpRepr {
@@ -2042,15 +2111,6 @@ mod test {
     // =========================================================================================//
     // Tests for the ESTABLISHED state.
     // =========================================================================================//
-    fn socket_established() -> TcpSocket<'static> {
-        let mut s = socket_syn_received();
-        s.state           = State::Established;
-        s.local_seq_no    = LOCAL_SEQ + 1;
-        s.remote_last_seq = LOCAL_SEQ + 1;
-        s.remote_last_ack = Some(REMOTE_SEQ + 1);
-        s.remote_last_win = 64;
-        s
-    }
 
     #[test]
     fn test_established_recv() {
@@ -2295,11 +2355,6 @@ mod test {
     // =========================================================================================//
     // Tests for the FIN-WAIT-1 state.
     // =========================================================================================//
-    fn socket_fin_wait_1() -> TcpSocket<'static> {
-        let mut s = socket_established();
-        s.state           = State::FinWait1;
-        s
-    }
 
     #[test]
     fn test_fin_wait_1_fin_ack() {
@@ -2368,13 +2423,6 @@ mod test {
     // =========================================================================================//
     // Tests for the FIN-WAIT-2 state.
     // =========================================================================================//
-    fn socket_fin_wait_2() -> TcpSocket<'static> {
-        let mut s = socket_fin_wait_1();
-        s.state           = State::FinWait2;
-        s.local_seq_no    = LOCAL_SEQ + 1 + 1;
-        s.remote_last_seq = LOCAL_SEQ + 1 + 1;
-        s
-    }
 
     #[test]
     fn test_fin_wait_2_fin() {
@@ -2399,13 +2447,6 @@ mod test {
     // =========================================================================================//
     // Tests for the CLOSING state.
     // =========================================================================================//
-    fn socket_closing() -> TcpSocket<'static> {
-        let mut s = socket_fin_wait_1();
-        s.state           = State::Closing;
-        s.remote_last_seq = LOCAL_SEQ + 1 + 1;
-        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
-        s
-    }
 
     #[test]
     fn test_closing_ack_fin() {
@@ -2434,16 +2475,6 @@ mod test {
     // =========================================================================================//
     // Tests for the TIME-WAIT state.
     // =========================================================================================//
-    fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> {
-        let mut s = socket_fin_wait_2();
-        s.state           = State::TimeWait;
-        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
-        if from_closing {
-            s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1);
-        }
-        s.timer           = Timer::Close { expires_at: 1_000 + CLOSE_DELAY };
-        s
-    }
 
     #[test]
     fn test_time_wait_from_fin_wait_2_ack() {
@@ -2505,13 +2536,6 @@ mod test {
     // =========================================================================================//
     // Tests for the CLOSE-WAIT state.
     // =========================================================================================//
-    fn socket_close_wait() -> TcpSocket<'static> {
-        let mut s = socket_established();
-        s.state           = State::CloseWait;
-        s.remote_seq_no   = REMOTE_SEQ + 1 + 1;
-        s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1);
-        s
-    }
 
     #[test]
     fn test_close_wait_ack() {
@@ -2541,12 +2565,6 @@ mod test {
     // =========================================================================================//
     // Tests for the LAST-ACK state.
     // =========================================================================================//
-    fn socket_last_ack() -> TcpSocket<'static> {
-        let mut s = socket_close_wait();
-        s.state           = State::LastAck;
-        s
-    }
-
     #[test]
     fn test_last_ack_fin_ack() {
         let mut s = socket_last_ack();
@@ -2575,6 +2593,7 @@ mod test {
     // =========================================================================================//
     // Tests for transitioning through multiple states.
     // =========================================================================================//
+
     #[test]
     fn test_listen() {
         let mut s = socket();
@@ -2598,7 +2617,7 @@ mod test {
             control: TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
@@ -2802,22 +2821,6 @@ mod test {
     // =========================================================================================//
     // Tests for retransmission on packet loss.
     // =========================================================================================//
-    fn socket_recved() -> TcpSocket<'static> {
-        let mut s = socket_established();
-        send!(s, TcpRepr {
-            seq_number: REMOTE_SEQ + 1,
-            ack_number: Some(LOCAL_SEQ + 1),
-            payload:    &b"abcdef"[..],
-            ..SEND_TEMPL
-        });
-        recv!(s, [TcpRepr {
-            seq_number: LOCAL_SEQ + 1,
-            ack_number: Some(REMOTE_SEQ + 1 + 6),
-            window_len: 58,
-            ..RECV_TEMPL
-        }]);
-        s
-    }
 
     #[test]
     fn test_duplicate_seq_ack() {
@@ -2907,14 +2910,14 @@ mod test {
             control:    TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }));
         recv!(s, time 150, Ok(TcpRepr { // retransmit
             control:    TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }));
         send!(s, TcpRepr {
@@ -3140,7 +3143,7 @@ mod test {
             control: TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }]);
         send!(s, TcpRepr {
@@ -3317,7 +3320,7 @@ mod test {
             control:    TcpControl::Syn,
             seq_number: LOCAL_SEQ,
             ack_number: None,
-            max_seg_size: Some(1480),
+            max_seg_size: Some(BASE_MSS),
             ..RECV_TEMPL
         }));
         assert_eq!(s.state, State::SynSent);
@@ -3533,13 +3536,7 @@ mod test {
 
         s.set_hop_limit(Some(0x2a));
         assert_eq!(s.dispatch(0, &caps, |(ip_repr, _)| {
-            assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
-                src_addr: Ipv4Address([10, 0, 0, 1]),
-                dst_addr: Ipv4Address([10, 0, 0, 2]),
-                protocol: IpProtocol::Tcp,
-                payload_len: 24,
-                hop_limit: 0x2a,
-            }));
+            assert_eq!(ip_repr.hop_limit(), 0x2a);
             Ok(())
         }), Ok(()));
     }
@@ -3686,8 +3683,8 @@ mod test {
         };
 
         let ip_repr = IpRepr::Unspecified {
-            src_addr:    REMOTE_IP,
-            dst_addr:    LOCAL_IP,
+            src_addr:    MOCK_IP_ADDR_2,
+            dst_addr:    MOCK_IP_ADDR_1,
             protocol:    IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit:   64
@@ -3695,8 +3692,8 @@ mod test {
         assert!(s.accepts(&ip_repr, &tcp_repr));
 
         let ip_repr_wrong_src = IpRepr::Unspecified {
-            src_addr:    OTHER_IP,
-            dst_addr:    LOCAL_IP,
+            src_addr:    MOCK_IP_ADDR_3,
+            dst_addr:    MOCK_IP_ADDR_1,
             protocol:    IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit:   64
@@ -3704,12 +3701,34 @@ mod test {
         assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr));
 
         let ip_repr_wrong_dst = IpRepr::Unspecified {
-            src_addr:    REMOTE_IP,
-            dst_addr:    OTHER_IP,
+            src_addr:    MOCK_IP_ADDR_2,
+            dst_addr:    MOCK_IP_ADDR_3,
             protocol:    IpProtocol::Tcp,
             payload_len: tcp_repr.buffer_len(),
             hop_limit:   64
         };
         assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr));
     }
+
+    // =========================================================================================//
+    // Timer tests
+    // =========================================================================================//
+
+    #[test]
+    fn test_timer_retransmit() {
+        let mut r = Timer::default();
+        assert_eq!(r.should_retransmit(1000), None);
+        r.set_for_retransmit(1000);
+        assert_eq!(r.should_retransmit(1000), None);
+        assert_eq!(r.should_retransmit(1050), None);
+        assert_eq!(r.should_retransmit(1101), Some(101));
+        r.set_for_retransmit(1101);
+        assert_eq!(r.should_retransmit(1101), None);
+        assert_eq!(r.should_retransmit(1150), None);
+        assert_eq!(r.should_retransmit(1200), None);
+        assert_eq!(r.should_retransmit(1301), Some(300));
+        r.set_for_idle(1301, None);
+        assert_eq!(r.should_retransmit(1350), None);
+    }
+
 }

+ 92 - 58
src/socket/udp.rs

@@ -258,7 +258,12 @@ impl<'a, 'b> UdpSocket<'a, 'b> {
 
 #[cfg(test)]
 mod test {
-    use wire::{IpAddress, Ipv4Address, IpRepr, Ipv4Repr, UdpRepr};
+    use wire::{IpAddress, IpRepr, UdpRepr};
+    #[cfg(feature = "proto-ipv4")]
+    use wire::Ipv4Repr;
+    #[cfg(feature = "proto-ipv6")]
+    use wire::Ipv6Repr;
+    use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
     use super::*;
 
     fn buffer(packets: usize) -> SocketBuffer<'static, 'static> {
@@ -278,12 +283,53 @@ mod test {
         }
     }
 
-    const LOCAL_IP:    IpAddress  = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 1]));
-    const REMOTE_IP:   IpAddress  = IpAddress::Ipv4(Ipv4Address([10, 0, 0, 2]));
     const LOCAL_PORT:  u16        = 53;
     const REMOTE_PORT: u16        = 49500;
-    const LOCAL_END:   IpEndpoint = IpEndpoint { addr: LOCAL_IP,  port: LOCAL_PORT  };
-    const REMOTE_END:  IpEndpoint = IpEndpoint { addr: REMOTE_IP, port: REMOTE_PORT };
+
+    pub const LOCAL_END:   IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT  };
+    pub const REMOTE_END:  IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT };
+
+    pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
+        src_addr: MOCK_IP_ADDR_1,
+        dst_addr: MOCK_IP_ADDR_2,
+        protocol: IpProtocol::Udp,
+        payload_len: 8 + 6,
+        hop_limit: 64,
+    };
+
+    const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
+        src_port: LOCAL_PORT,
+        dst_port: REMOTE_PORT,
+        payload: b"abcdef"
+    };
+
+    const REMOTE_UDP_REPR: UdpRepr = UdpRepr {
+        src_port: REMOTE_PORT,
+        dst_port: LOCAL_PORT,
+        payload: b"abcdef"
+    };
+
+    fn remote_ip_repr() -> IpRepr {
+        match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_1) {
+            #[cfg(feature = "proto-ipv4")]
+            (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr {
+                src_addr: src,
+                dst_addr: dst,
+                protocol: IpProtocol::Udp,
+                payload_len: 8 + 6,
+                hop_limit: 64
+            }),
+            #[cfg(feature = "proto-ipv6")]
+            (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
+                src_addr: src,
+                dst_addr: dst,
+                next_header: IpProtocol::Udp,
+                payload_len: 8 + 6,
+                hop_limit: 64
+            }),
+            _ => unreachable!()
+        }
+    }
 
     #[test]
     fn test_bind_unaddressable() {
@@ -298,18 +344,12 @@ mod test {
         assert_eq!(socket.bind(2), Err(Error::Illegal));
     }
 
-    const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
-        src_addr: LOCAL_IP,
-        dst_addr: REMOTE_IP,
-        protocol: IpProtocol::Udp,
-        payload_len: 8 + 6,
-        hop_limit: 64,
-    };
-    const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
-        src_port: LOCAL_PORT,
-        dst_port: REMOTE_PORT,
-        payload: b"abcdef"
-    };
+    #[test]
+    #[should_panic(expected = "the time-to-live value of a packet must not be zero")]
+    fn test_set_hop_limit_zero() {
+        let mut s = socket(buffer(0), buffer(1));
+        s.set_hop_limit(Some(0));
+    }
 
     #[test]
     fn test_send_unaddressable() {
@@ -361,19 +401,6 @@ mod test {
         assert!(socket.can_send());
     }
 
-    const REMOTE_IP_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
-        src_addr: Ipv4Address([10, 0, 0, 2]),
-        dst_addr: Ipv4Address([10, 0, 0, 1]),
-        protocol: IpProtocol::Udp,
-        payload_len: 8 + 6,
-        hop_limit: 64
-    });
-    const REMOTE_UDP_REPR: UdpRepr = UdpRepr {
-        src_port: REMOTE_PORT,
-        dst_port: LOCAL_PORT,
-        payload: b"abcdef"
-    };
-
     #[test]
     fn test_recv_process() {
         let mut socket = socket(buffer(1), buffer(0));
@@ -382,13 +409,13 @@ mod test {
         assert!(!socket.can_recv());
         assert_eq!(socket.recv(), Err(Error::Exhausted));
 
-        assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
+        assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
+        assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
                    Err(Error::Exhausted));
         assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
         assert!(!socket.can_recv());
@@ -399,8 +426,8 @@ mod test {
         let mut socket = socket(buffer(1), buffer(0));
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
-        assert!(socket.accepts(&REMOTE_IP_REPR, &REMOTE_UDP_REPR));
-        assert_eq!(socket.process(&REMOTE_IP_REPR, &REMOTE_UDP_REPR),
+        assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
+        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
                    Ok(()));
 
         let mut slice = [0; 4];
@@ -414,8 +441,8 @@ mod test {
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
         let udp_repr = UdpRepr { payload: &[0; 100][..], ..REMOTE_UDP_REPR };
-        assert!(socket.accepts(&REMOTE_IP_REPR, &udp_repr));
-        assert_eq!(socket.process(&REMOTE_IP_REPR, &udp_repr),
+        assert!(socket.accepts(&remote_ip_repr(), &udp_repr));
+        assert_eq!(socket.process(&remote_ip_repr(), &udp_repr),
                    Err(Error::Truncated));
     }
 
@@ -428,8 +455,8 @@ mod test {
         assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
         assert_eq!(s.dispatch(|(ip_repr, _)| {
             assert_eq!(ip_repr, IpRepr::Unspecified{
-                src_addr: LOCAL_IP,
-                dst_addr: REMOTE_IP,
+                src_addr: MOCK_IP_ADDR_1,
+                dst_addr: MOCK_IP_ADDR_2,
                 protocol: IpProtocol::Udp,
                 payload_len: 8 + 6,
                 hop_limit: 0x2a,
@@ -438,40 +465,47 @@ mod test {
         }), Ok(()));
     }
 
-    #[test]
-    #[should_panic(expected = "the time-to-live value of a packet must not be zero")]
-    fn test_set_hop_limit_zero() {
-        let mut s = socket(buffer(0), buffer(1));
-        s.set_hop_limit(Some(0));
-    }
-
     #[test]
     fn test_doesnt_accept_wrong_port() {
         let mut socket = socket(buffer(1), buffer(0));
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
         let mut udp_repr = REMOTE_UDP_REPR;
-        assert!(socket.accepts(&REMOTE_IP_REPR, &udp_repr));
+        assert!(socket.accepts(&remote_ip_repr(), &udp_repr));
         udp_repr.dst_port += 1;
-        assert!(!socket.accepts(&REMOTE_IP_REPR, &udp_repr));
+        assert!(!socket.accepts(&remote_ip_repr(), &udp_repr));
     }
 
     #[test]
     fn test_doesnt_accept_wrong_ip() {
-        let ip_repr = IpRepr::Ipv4(Ipv4Repr {
-            src_addr: Ipv4Address([10, 0, 0, 2]),
-            dst_addr: Ipv4Address([10, 0, 0, 10]),
-            protocol: IpProtocol::Udp,
-            payload_len: 8 + 6,
-            hop_limit: 64
-        });
+        fn generate_bad_repr() -> IpRepr {
+            match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_3) {
+                #[cfg(feature = "proto-ipv4")]
+                (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr {
+                    src_addr: src,
+                    dst_addr: dst,
+                    protocol: IpProtocol::Udp,
+                    payload_len: 8 + 6,
+                    hop_limit: 64
+                }),
+                #[cfg(feature = "proto-ipv6")]
+                (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
+                    src_addr: src,
+                    dst_addr: dst,
+                    next_header: IpProtocol::Udp,
+                    payload_len: 8 + 6,
+                    hop_limit: 64
+                }),
+                _ => unreachable!()
+            }
+        }
 
         let mut port_bound_socket = socket(buffer(1), buffer(0));
         assert_eq!(port_bound_socket.bind(LOCAL_PORT), Ok(()));
-        assert!(port_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));
+        assert!(port_bound_socket.accepts(&generate_bad_repr(), &REMOTE_UDP_REPR));
 
         let mut ip_bound_socket = socket(buffer(1), buffer(0));
         assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(()));
-        assert!(!ip_bound_socket.accepts(&ip_repr, &REMOTE_UDP_REPR));
+        assert!(!ip_bound_socket.accepts(&generate_bad_repr(), &REMOTE_UDP_REPR));
     }
 }

+ 2 - 0
src/wire/ethernet.rs

@@ -217,10 +217,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
         write!(f, "{}{}", indent, frame)?;
 
         match frame.ethertype() {
+            #[cfg(feature = "proto-ipv4")]
             EtherType::Arp => {
                 indent.increase(f)?;
                 super::ArpPacket::<&[u8]>::pretty_print(&frame.payload(), f, indent)
             }
+            #[cfg(feature = "proto-ipv4")]
             EtherType::Ipv4 => {
                 indent.increase(f)?;
                 super::Ipv4Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)

+ 75 - 5
src/wire/ip.rs

@@ -3,6 +3,7 @@ use core::convert::From;
 
 use {Error, Result};
 use phy::ChecksumCapabilities;
+#[cfg(feature = "proto-ipv4")]
 use super::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr};
 #[cfg(feature = "proto-ipv6")]
 use super::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
@@ -11,6 +12,7 @@ use super::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 pub enum Version {
     Unspecified,
+    #[cfg(feature = "proto-ipv4")]
     Ipv4,
     #[cfg(feature = "proto-ipv6")]
     Ipv6,
@@ -25,6 +27,7 @@ impl Version {
     /// unknown versions result in `Err(Error::Unrecognized)`.
     pub fn of_packet(data: &[u8]) -> Result<Version> {
         match data[0] >> 4 {
+            #[cfg(feature = "proto-ipv4")]
             4 => Ok(Version::Ipv4),
             #[cfg(feature = "proto-ipv6")]
             6 => Ok(Version::Ipv6),
@@ -37,6 +40,7 @@ impl fmt::Display for Version {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &Version::Unspecified => write!(f, "IPv?"),
+            #[cfg(feature = "proto-ipv4")]
             &Version::Ipv4 => write!(f, "IPv4"),
             #[cfg(feature = "proto-ipv6")]
             &Version::Ipv6 => write!(f, "IPv6"),
@@ -84,6 +88,7 @@ pub enum Address {
     /// May be used as a placeholder for storage where the address is not assigned yet.
     Unspecified,
     /// An IPv4 address.
+    #[cfg(feature = "proto-ipv4")]
     Ipv4(Ipv4Address),
     /// An IPv6 address.
     #[cfg(feature = "proto-ipv6")]
@@ -94,6 +99,7 @@ pub enum Address {
 
 impl Address {
     /// Create an address wrapping an IPv4 address with the given octets.
+    #[cfg(feature = "proto-ipv4")]
     pub fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
         Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
     }
@@ -109,6 +115,7 @@ impl Address {
     pub fn is_unicast(&self) -> bool {
         match self {
             &Address::Unspecified     => false,
+            #[cfg(feature = "proto-ipv4")]
             &Address::Ipv4(addr)      => addr.is_unicast(),
             #[cfg(feature = "proto-ipv6")]
             &Address::Ipv6(addr)      => addr.is_unicast(),
@@ -120,6 +127,7 @@ impl Address {
     pub fn is_broadcast(&self) -> bool {
         match self {
             &Address::Unspecified     => false,
+            #[cfg(feature = "proto-ipv4")]
             &Address::Ipv4(addr)      => addr.is_broadcast(),
             #[cfg(feature = "proto-ipv6")]
             &Address::Ipv6(_)         => false,
@@ -131,6 +139,7 @@ impl Address {
     pub fn is_unspecified(&self) -> bool {
         match self {
             &Address::Unspecified     => true,
+            #[cfg(feature = "proto-ipv4")]
             &Address::Ipv4(addr)      => addr.is_unspecified(),
             #[cfg(feature = "proto-ipv6")]
             &Address::Ipv6(addr)      => addr.is_unspecified(),
@@ -142,6 +151,7 @@ impl Address {
     pub fn to_unspecified(&self) -> Address {
         match self {
             &Address::Unspecified     => Address::Unspecified,
+            #[cfg(feature = "proto-ipv4")]
             &Address::Ipv4(_)         => Address::Ipv4(Ipv4Address::UNSPECIFIED),
             #[cfg(feature = "proto-ipv6")]
             &Address::Ipv6(_)         => Address::Ipv6(Ipv6Address::UNSPECIFIED),
@@ -156,6 +166,7 @@ impl Default for Address {
     }
 }
 
+#[cfg(feature = "proto-ipv4")]
 impl From<Ipv4Address> for Address {
     fn from(addr: Ipv4Address) -> Self {
         Address::Ipv4(addr)
@@ -173,6 +184,7 @@ impl fmt::Display for Address {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &Address::Unspecified     => write!(f, "*"),
+            #[cfg(feature = "proto-ipv4")]
             &Address::Ipv4(addr)      => write!(f, "{}", addr),
             #[cfg(feature = "proto-ipv6")]
             &Address::Ipv6(addr)      => write!(f, "{}", addr),
@@ -185,6 +197,7 @@ impl fmt::Display for Address {
 /// subnet masking prefix length.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Cidr {
+    #[cfg(feature = "proto-ipv4")]
     Ipv4(Ipv4Cidr),
     #[cfg(feature = "proto-ipv6")]
     Ipv6(Ipv6Cidr),
@@ -200,6 +213,7 @@ impl Cidr {
     /// the given prefix length is invalid for the given address.
     pub fn new(addr: Address, prefix_len: u8) -> Cidr {
         match addr {
+            #[cfg(feature = "proto-ipv4")]
             Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
             #[cfg(feature = "proto-ipv6")]
             Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
@@ -213,6 +227,7 @@ impl Cidr {
     /// Return the IP address of this CIDR block.
     pub fn address(&self) -> Address {
         match self {
+            #[cfg(feature = "proto-ipv4")]
             &Cidr::Ipv4(cidr)      => Address::Ipv4(cidr.address()),
             #[cfg(feature = "proto-ipv6")]
             &Cidr::Ipv6(cidr)      => Address::Ipv6(cidr.address()),
@@ -223,6 +238,7 @@ impl Cidr {
     /// Return the prefix length of this CIDR block.
     pub fn prefix_len(&self) -> u8 {
         match self {
+            #[cfg(feature = "proto-ipv4")]
             &Cidr::Ipv4(cidr)      => cidr.prefix_len(),
             #[cfg(feature = "proto-ipv6")]
             &Cidr::Ipv6(cidr)      => cidr.prefix_len(),
@@ -234,12 +250,13 @@ impl Cidr {
     /// the given address.
     pub fn contains_addr(&self, addr: &Address) -> bool {
         match (self, addr) {
+            #[cfg(feature = "proto-ipv4")]
             (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) =>
                 cidr.contains_addr(addr),
             #[cfg(feature = "proto-ipv6")]
             (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) =>
                 cidr.contains_addr(addr),
-            #[cfg(feature = "proto-ipv6")]
+            #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
             (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) =>
                 false,
             (_, &Address::Unspecified) =>
@@ -256,12 +273,13 @@ impl Cidr {
     /// the subnetwork described by the given CIDR block.
     pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
         match (self, subnet) {
+            #[cfg(feature = "proto-ipv4")]
             (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) =>
                 cidr.contains_subnet(other),
             #[cfg(feature = "proto-ipv6")]
             (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) =>
                 cidr.contains_subnet(other),
-            #[cfg(feature = "proto-ipv6")]
+            #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
             (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) =>
                 false,
             (&Cidr::__Nonexhaustive, _) |
@@ -274,6 +292,7 @@ impl Cidr {
 impl fmt::Display for Cidr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
+            #[cfg(feature = "proto-ipv4")]
             &Cidr::Ipv4(cidr)      => write!(f, "{}", cidr),
             #[cfg(feature = "proto-ipv6")]
             &Cidr::Ipv6(cidr)      => write!(f, "{}", cidr),
@@ -338,6 +357,7 @@ pub enum Repr {
         payload_len: usize,
         hop_limit:   u8
     },
+    #[cfg(feature = "proto-ipv4")]
     Ipv4(Ipv4Repr),
     #[cfg(feature = "proto-ipv6")]
     Ipv6(Ipv6Repr),
@@ -345,6 +365,7 @@ pub enum Repr {
     __Nonexhaustive
 }
 
+#[cfg(feature = "proto-ipv4")]
 impl From<Ipv4Repr> for Repr {
     fn from(repr: Ipv4Repr) -> Repr {
         Repr::Ipv4(repr)
@@ -363,6 +384,7 @@ impl Repr {
     pub fn version(&self) -> Version {
         match self {
             &Repr::Unspecified { .. } => Version::Unspecified,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(_) => Version::Ipv4,
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(_) => Version::Ipv6,
@@ -374,6 +396,7 @@ impl Repr {
     pub fn src_addr(&self) -> Address {
         match self {
             &Repr::Unspecified { src_addr, .. } => src_addr,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
@@ -385,6 +408,7 @@ impl Repr {
     pub fn dst_addr(&self) -> Address {
         match self {
             &Repr::Unspecified { dst_addr, .. } => dst_addr,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
@@ -396,6 +420,7 @@ impl Repr {
     pub fn protocol(&self) -> Protocol {
         match self {
             &Repr::Unspecified { protocol, .. } => protocol,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) => repr.protocol,
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(repr) => repr.next_header,
@@ -407,6 +432,7 @@ impl Repr {
     pub fn payload_len(&self) -> usize {
         match self {
             &Repr::Unspecified { payload_len, .. } => payload_len,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) => repr.payload_len,
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(repr) => repr.payload_len,
@@ -419,6 +445,7 @@ impl Repr {
         match self {
             &mut Repr::Unspecified { ref mut payload_len, .. } =>
                 *payload_len = length,
+            #[cfg(feature = "proto-ipv4")]
             &mut Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) =>
                 *payload_len = length,
             #[cfg(feature = "proto-ipv6")]
@@ -432,6 +459,7 @@ impl Repr {
     pub fn hop_limit(&self) -> u8 {
         match self {
             &Repr::Unspecified { hop_limit, .. }    => hop_limit,
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
             #[cfg(feature = "proto-ipv6")]
             &Repr::Ipv6(Ipv6Repr { hop_limit, ..})  => hop_limit,
@@ -466,6 +494,7 @@ impl Repr {
         }
 
         match self {
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Unspecified {
                 src_addr: Address::Ipv4(src_addr),
                 dst_addr: Address::Ipv4(dst_addr),
@@ -494,6 +523,7 @@ impl Repr {
                 }))
             }
 
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Unspecified {
                 src_addr: Address::Unspecified,
                 dst_addr: Address::Ipv4(dst_addr),
@@ -537,6 +567,7 @@ impl Repr {
                 }
             }
 
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(mut repr) =>
                 resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs),
 
@@ -559,6 +590,7 @@ impl Repr {
         match self {
             &Repr::Unspecified { .. } =>
                 panic!("unspecified IP representation"),
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) =>
                 repr.buffer_len(),
             #[cfg(feature = "proto-ipv6")]
@@ -577,6 +609,7 @@ impl Repr {
         match self {
             &Repr::Unspecified { .. } =>
                 panic!("unspecified IP representation"),
+            #[cfg(feature = "proto-ipv4")]
             &Repr::Ipv4(repr) =>
                 repr.emit(&mut Ipv4Packet::new(buffer), &checksum_caps),
             #[cfg(feature = "proto-ipv6")]
@@ -654,6 +687,7 @@ pub mod checksum {
     pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
                          protocol: Protocol, length: u32) -> u16 {
         match (src_addr, dst_addr) {
+            #[cfg(feature = "proto-ipv4")]
             (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
                 let mut proto_len = [0u8; 4];
                 proto_len[1] = protocol.into();
@@ -697,12 +731,15 @@ use super::pretty_print::{PrettyPrint, PrettyIndent};
 
 pub fn pretty_print_ip_payload<T: Into<Repr>>(f: &mut fmt::Formatter, indent: &mut PrettyIndent,
                                               ip_repr: T, payload: &[u8]) -> fmt::Result {
-    use wire::{Icmpv4Packet, TcpPacket, TcpRepr, UdpPacket, UdpRepr};
+    #[cfg(feature = "proto-ipv4")]
+    use wire::Icmpv4Packet;
+    use wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
     use wire::ip::checksum::format_checksum;
 
     let checksum_caps = ChecksumCapabilities::ignored();
     let repr = ip_repr.into();
     match repr.protocol() {
+        #[cfg(feature = "proto-ipv4")]
         Protocol::Icmp => {
             indent.increase(f)?;
             Icmpv4Packet::<&[u8]>::pretty_print(&payload.as_ref(), f, indent)
@@ -748,10 +785,43 @@ pub fn pretty_print_ip_payload<T: Into<Repr>>(f: &mut fmt::Formatter, indent: &m
 }
 
 #[cfg(test)]
-mod test {
+pub(crate) mod test {
+    #![allow(unused)]
+
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
+                                                                              0, 0, 0, 0, 0, 0, 0, 1]));
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
+                                                                              0, 0, 0, 0, 0, 0, 0, 2]));
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
+                                                                              0, 0, 0, 0, 0, 0, 0, 3]));
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
+                                                                              0, 0, 0, 0, 0, 0, 0, 4]));
+    #[cfg(feature = "proto-ipv6")]
+    pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
+
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
+    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
+    pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
+
+
     use super::*;
-    use wire::{Ipv4Address, IpProtocol, IpAddress, Ipv4Repr, IpCidr};
+    use wire::{IpAddress, IpProtocol,IpCidr};
+    #[cfg(feature = "proto-ipv4")]
+    use wire::{Ipv4Address, Ipv4Repr};
+
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn ip_repr_lower() {
         let ip_addr_a = Ipv4Address::new(1, 2, 3, 4);
         let ip_addr_b = Ipv4Address::new(5, 6, 7, 8);

+ 10 - 1
src/wire/mod.rs

@@ -46,6 +46,8 @@
 //!
 /*!
 ```rust
+# #[cfg(feature = "proto-ipv4")]
+# {
 use smoltcp::phy::ChecksumCapabilities;
 use smoltcp::wire::*;
 let repr = Ipv4Repr {
@@ -67,6 +69,7 @@ let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
                           .expect("malformed packet");
     assert_eq!(repr, parsed);
 }
+# }
 ```
 */
 
@@ -78,11 +81,14 @@ mod field {
 pub mod pretty_print;
 
 mod ethernet;
+#[cfg(feature = "proto-ipv4")]
 mod arp;
-mod ip;
+pub(crate) mod ip;
+#[cfg(feature = "proto-ipv4")]
 mod ipv4;
 #[cfg(feature = "proto-ipv6")]
 mod ipv6;
+#[cfg(feature = "proto-ipv4")]
 mod icmpv4;
 mod udp;
 mod tcp;
@@ -93,6 +99,7 @@ pub use self::ethernet::{EtherType as EthernetProtocol,
                          Address as EthernetAddress,
                          Frame as EthernetFrame};
 
+#[cfg(feature = "proto-ipv4")]
 pub use self::arp::{Hardware as ArpHardware,
                     Operation as ArpOperation,
                     Packet as ArpPacket,
@@ -105,6 +112,7 @@ pub use self::ip::{Version as IpVersion,
                    Repr as IpRepr,
                    Cidr as IpCidr};
 
+#[cfg(feature = "proto-ipv4")]
 pub use self::ipv4::{Address as Ipv4Address,
                      Packet as Ipv4Packet,
                      Repr as Ipv4Repr,
@@ -116,6 +124,7 @@ pub use self::ipv6::{Address as Ipv6Address,
                      Repr as Ipv6Repr,
                      Cidr as Ipv6Cidr};
 
+#[cfg(feature = "proto-ipv4")]
 pub use self::icmpv4::{Message as Icmpv4Message,
                        DstUnreachable as Icmpv4DstUnreachable,
                        Redirect as Icmpv4Redirect,

+ 13 - 0
src/wire/tcp.rs

@@ -854,12 +854,16 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
 
 #[cfg(test)]
 mod test {
+    #[cfg(feature = "proto-ipv4")]
     use wire::Ipv4Address;
     use super::*;
 
+    #[cfg(feature = "proto-ipv4")]
     const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
+    #[cfg(feature = "proto-ipv4")]
     const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
 
+    #[cfg(feature = "proto-ipv4")]
     static PACKET_BYTES: [u8; 28] =
         [0xbf, 0x00, 0x00, 0x50,
          0x01, 0x23, 0x45, 0x67,
@@ -869,13 +873,16 @@ mod test {
          0x03, 0x03, 0x0c, 0x01,
          0xaa, 0x00, 0x00, 0xff];
 
+    #[cfg(feature = "proto-ipv4")]
     static OPTION_BYTES: [u8; 4] =
         [0x03, 0x03, 0x0c, 0x01];
 
+    #[cfg(feature = "proto-ipv4")]
     static PAYLOAD_BYTES: [u8; 4] =
         [0xaa, 0x00, 0x00, 0xff];
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_deconstruct() {
         let packet = Packet::new(&PACKET_BYTES[..]);
         assert_eq!(packet.src_port(), 48896);
@@ -898,6 +905,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_construct() {
         let mut bytes = vec![0xa5; PACKET_BYTES.len()];
         let mut packet = Packet::new(&mut bytes);
@@ -923,6 +931,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_truncated() {
         let packet = Packet::new(&PACKET_BYTES[..23]);
         assert_eq!(packet.check_len(), Err(Error::Truncated));
@@ -936,6 +945,7 @@ mod test {
         assert_eq!(packet.check_len(), Err(Error::Malformed));
     }
 
+    #[cfg(feature = "proto-ipv4")]
     static SYN_PACKET_BYTES: [u8; 24] =
         [0xbf, 0x00, 0x00, 0x50,
          0x01, 0x23, 0x45, 0x67,
@@ -944,6 +954,7 @@ mod test {
          0x7a, 0x8d, 0x00, 0x00,
          0xaa, 0x00, 0x00, 0xff];
 
+    #[cfg(feature = "proto-ipv4")]
     fn packet_repr() -> Repr<'static> {
         Repr {
             src_port:     48896,
@@ -958,6 +969,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_parse() {
         let packet = Packet::new(&SYN_PACKET_BYTES[..]);
         let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
@@ -965,6 +977,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_emit() {
         let repr = packet_repr();
         let mut bytes = vec![0xa5; repr.buffer_len()];

+ 12 - 0
src/wire/udp.rs

@@ -210,6 +210,7 @@ impl<'a> Repr<'a> {
         // Valid checksum is expected...
         if checksum_caps.udpv4.rx() && !packet.verify_checksum(src_addr, dst_addr) {
             match (src_addr, dst_addr) {
+                #[cfg(feature = "proto-ipv4")]
                 (&IpAddress::Ipv4(_), &IpAddress::Ipv4(_))
                         if packet.checksum() != 0 => {
                     // ... except on UDP-over-IPv4, where it can be omitted.
@@ -282,21 +283,27 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
 
 #[cfg(test)]
 mod test {
+    #[cfg(feature = "proto-ipv4")]
     use wire::Ipv4Address;
     use super::*;
 
+    #[cfg(feature = "proto-ipv4")]
     const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
+    #[cfg(feature = "proto-ipv4")]
     const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
 
+    #[cfg(feature = "proto-ipv4")]
     static PACKET_BYTES: [u8; 12] =
         [0xbf, 0x00, 0x00, 0x35,
          0x00, 0x0c, 0x12, 0x4d,
          0xaa, 0x00, 0x00, 0xff];
 
+    #[cfg(feature = "proto-ipv4")]
     static PAYLOAD_BYTES: [u8; 4] =
         [0xaa, 0x00, 0x00, 0xff];
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_deconstruct() {
         let packet = Packet::new(&PACKET_BYTES[..]);
         assert_eq!(packet.src_port(), 48896);
@@ -308,6 +315,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_construct() {
         let mut bytes = vec![0xa5; 12];
         let mut packet = Packet::new(&mut bytes);
@@ -329,6 +337,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_zero_checksum() {
         let mut bytes = vec![0; 8];
         let mut packet = Packet::new(&mut bytes);
@@ -339,6 +348,7 @@ mod test {
         assert_eq!(packet.checksum(), 0xffff);
     }
 
+    #[cfg(feature = "proto-ipv4")]
     fn packet_repr() -> Repr<'static> {
         Repr {
             src_port: 48896,
@@ -348,6 +358,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_parse() {
         let packet = Packet::new(&PACKET_BYTES[..]);
         let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
@@ -355,6 +366,7 @@ mod test {
     }
 
     #[test]
+    #[cfg(feature = "proto-ipv4")]
     fn test_emit() {
         let repr = packet_repr();
         let mut bytes = vec![0xa5; repr.buffer_len()];