浏览代码

Add Context struct.

Dario Nieuwenhuis 3 年之前
父节点
当前提交
8a225837e2
共有 9 个文件被更改,包括 357 次插入297 次删除
  1. 0 2
      .github/workflows/test.yml
  2. 159 142
      src/iface/interface.rs
  3. 11 0
      src/phy/mod.rs
  4. 21 17
      src/socket/dhcpv4.rs
  5. 29 31
      src/socket/icmp.rs
  6. 42 2
      src/socket/mod.rs
  7. 19 29
      src/socket/raw.rs
  8. 62 60
      src/socket/tcp.rs
  9. 14 14
      src/socket/udp.rs

+ 0 - 2
.github/workflows/test.yml

@@ -43,8 +43,6 @@ jobs:
           # Test alloc feature which requires nightly.
           - rust: nightly
             features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
-          - rust: nightly
-            features: alloc proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
     steps:
       - uses: actions/checkout@v2
       - uses: actions-rs/toolchain@v1

+ 159 - 142
src/iface/interface.rs

@@ -45,7 +45,6 @@ struct InterfaceInner<'a> {
     /// When to report for (all or) the next multicast group membership via IGMP
     #[cfg(feature = "proto-igmp")]
     igmp_report_state:      IgmpReportState,
-    device_capabilities:    DeviceCapabilities,
 }
 
 /// A builder structure used for creating a network interface.
@@ -230,7 +229,6 @@ let iface = InterfaceBuilder::new(device)
                 #[cfg(feature = "proto-ipv4")]
                 any_ip: self.any_ip,
                 routes: self.routes,
-                device_capabilities,
                 #[cfg(feature = "medium-ethernet")]
                 neighbor_cache,
                 #[cfg(feature = "proto-igmp")]
@@ -420,6 +418,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
     /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent`
     /// indicates whether an initial immediate announcement has been sent.
     pub fn join_multicast_group<T: Into<IpAddress>>(&mut self, addr: T, _timestamp: Instant) -> Result<bool> {
+
         match addr.into() {
             #[cfg(feature = "proto-igmp")]
             IpAddress::Ipv4(addr) => {
@@ -430,9 +429,10 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                     Ok(false)
                 } else if let Some(pkt) =
                         self.inner.igmp_report_packet(IgmpVersion::Version2, addr) {
+                    let cx = self.context(_timestamp);
                     // Send initial membership report
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, _timestamp, pkt)?;
+                    self.inner.dispatch_ip(&cx, tx_token, pkt)?;
                     Ok(true)
                 } else {
                     Ok(false)
@@ -456,9 +456,10 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                 if was_not_present {
                     Ok(false)
                 } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) {
+                    let cx = self.context(_timestamp);
                     // Send group leave packet
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, _timestamp, pkt)?;
+                    self.inner.dispatch_ip(&cx, tx_token, pkt)?;
                     Ok(true)
                 } else {
                     Ok(false)
@@ -535,13 +536,15 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
     /// a very common occurrence and on a production system it should not even
     /// be logged.
     pub fn poll(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result<bool> {
+        let cx = self.context(timestamp);
+
         let mut readiness_may_have_changed = false;
         loop {
-            let processed_any = self.socket_ingress(sockets, timestamp);
-            let emitted_any   = self.socket_egress(sockets, timestamp)?;
+            let processed_any = self.socket_ingress(&cx, sockets);
+            let emitted_any   = self.socket_egress(&cx, sockets)?;
 
             #[cfg(feature = "proto-igmp")]
-            self.igmp_egress(timestamp)?;
+            self.igmp_egress(&cx, timestamp)?;
 
             if processed_any || emitted_any {
                 readiness_may_have_changed = true;
@@ -561,10 +564,12 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
     /// [poll]: #method.poll
     /// [Instant]: struct.Instant.html
     pub fn poll_at(&self, sockets: &SocketSet, timestamp: Instant) -> Option<Instant> {
+        let cx = self.context(timestamp);
+
         sockets.iter().filter_map(|socket| {
-            let socket_poll_at = socket.poll_at();
+            let socket_poll_at = socket.poll_at(&cx);
             match socket.meta().poll_at(socket_poll_at, |ip_addr|
-                self.inner.has_neighbor(&ip_addr, timestamp)) {
+                self.inner.has_neighbor(&cx, &ip_addr)) {
                     PollAt::Ingress => None,
                     PollAt::Time(instant) => Some(instant),
                     PollAt::Now => Some(Instant::from_millis(0)),
@@ -592,19 +597,19 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
         }
     }
 
-    fn socket_ingress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> bool {
+    fn socket_ingress(&mut self, cx: &Context, sockets: &mut SocketSet) -> bool {
         let mut processed_any = false;
         let &mut Self { ref mut device, ref mut inner } = self;
         while let Some((rx_token, tx_token)) = device.receive() {
-            if let Err(err) = rx_token.consume(timestamp, |frame| {
-                match inner.device_capabilities.medium {
+            if let Err(err) = rx_token.consume(cx.now, |frame| {
+                match cx.caps.medium {
                     #[cfg(feature = "medium-ethernet")]
                     Medium::Ethernet => {
-                        match inner.process_ethernet(sockets, timestamp, &frame) {
+                        match inner.process_ethernet(cx, sockets, &frame) {
                             Ok(response) => {
                                 processed_any = true;
                                 if let Some(packet) = response {
-                                    if let Err(err) = inner.dispatch(tx_token, timestamp, packet) {
+                                    if let Err(err) = inner.dispatch(cx, tx_token, packet) {
                                         net_debug!("Failed to send response: {}", err);
                                     }
                                 }
@@ -619,11 +624,11 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                     }
                     #[cfg(feature = "medium-ip")]
                     Medium::Ip => {
-                        match inner.process_ip(sockets, timestamp, &frame) {
+                        match inner.process_ip(cx, sockets, &frame) {
                             Ok(response) => {
                                 processed_any = true;
                                 if let Some(packet) = response {
-                                    if let Err(err) = inner.dispatch_ip(tx_token, timestamp, packet) {
+                                    if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) {
                                         net_debug!("Failed to send response: {}", err);
                                     }
                                 }
@@ -642,13 +647,13 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
         processed_any
     }
 
-    fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result<bool> {
+    fn socket_egress(&mut self, cx: &Context, sockets: &mut SocketSet) -> Result<bool> {
         let _caps = self.device.capabilities();
 
         let mut emitted_any = false;
         for mut socket in sockets.iter_mut() {
-            if !socket.meta_mut().egress_permitted(timestamp, |ip_addr|
-                    self.inner.has_neighbor(&ip_addr, timestamp)) {
+            if !socket.meta_mut().egress_permitted(cx.now, |ip_addr|
+                    self.inner.has_neighbor(cx, &ip_addr)) {
                 continue
             }
 
@@ -661,28 +666,21 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                     let response = $response;
                     neighbor_addr = Some(response.ip_repr().dst_addr());
                     let tx_token = device.transmit().ok_or(Error::Exhausted)?;
-                    device_result = inner.dispatch_ip(tx_token, timestamp, response);
+                    device_result = inner.dispatch_ip(cx, tx_token, response);
                     device_result
                 })
             }
 
-            let _ip_mtu = match _caps.medium {
-                #[cfg(feature = "medium-ethernet")]
-                Medium::Ethernet => _caps.max_transmission_unit - EthernetFrame::<&[u8]>::header_len(),
-                #[cfg(feature = "medium-ip")]
-                Medium::Ip => _caps.max_transmission_unit,
-            };
-
 
             let socket_result =
                 match *socket {
                     #[cfg(feature = "socket-raw")]
                     Socket::Raw(ref mut socket) =>
-                        socket.dispatch(&_caps.checksum, |response|
+                        socket.dispatch(cx, |response|
                             respond!(IpPacket::Raw(response))),
                     #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
                     Socket::Icmp(ref mut socket) =>
-                        socket.dispatch(|response| {
+                        socket.dispatch(cx, |response| {
                             match response {
                                 #[cfg(feature = "proto-ipv4")]
                                 (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) =>
@@ -695,17 +693,17 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                         }),
                     #[cfg(feature = "socket-udp")]
                     Socket::Udp(ref mut socket) =>
-                        socket.dispatch(|response|
+                        socket.dispatch(cx, |response|
                             respond!(IpPacket::Udp(response))),
                     #[cfg(feature = "socket-tcp")]
                     Socket::Tcp(ref mut socket) => {
-                        socket.dispatch(timestamp, _ip_mtu, |response|
+                        socket.dispatch(cx, |response|
                             respond!(IpPacket::Tcp(response)))
                     }
                     #[cfg(feature = "socket-dhcpv4")]
                     Socket::Dhcpv4(ref mut socket) =>
                         // todo don't unwrap
-                        socket.dispatch(timestamp, inner.ethernet_addr.unwrap(), _ip_mtu, |response|
+                        socket.dispatch(cx, |response|
                             respond!(IpPacket::Dhcpv4(response))),
                 };
 
@@ -717,7 +715,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                     // requests from the socket. However, without an additional rate limiting
                     // mechanism, we would spin on every socket that has yet to discover its
                     // neighboor.
-                    socket.meta_mut().neighbor_missing(timestamp,
+                    socket.meta_mut().neighbor_missing(cx.now,
                         neighbor_addr.expect("non-IP response packet"));
                     break
                 }
@@ -735,14 +733,14 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
     /// Depending on `igmp_report_state` and the therein contained
     /// timeouts, send IGMP membership reports.
     #[cfg(feature = "proto-igmp")]
-    fn igmp_egress(&mut self, timestamp: Instant) -> Result<bool> {
+    fn igmp_egress(&mut self, cx: &Context, timestamp: Instant) -> Result<bool> {
         match self.inner.igmp_report_state {
             IgmpReportState::ToSpecificQuery { version, timeout, group }
                     if timestamp >= timeout => {
                 if let Some(pkt) = self.inner.igmp_report_packet(version, group) {
                     // Send initial membership report
                     let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                    self.inner.dispatch_ip(tx_token, timestamp, pkt)?;
+                    self.inner.dispatch_ip(cx, tx_token, pkt)?;
                 }
 
                 self.inner.igmp_report_state = IgmpReportState::Inactive;
@@ -760,7 +758,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                         if let Some(pkt) = self.inner.igmp_report_packet(version, addr) {
                             // Send initial membership report
                             let tx_token = self.device.transmit().ok_or(Error::Exhausted)?;
-                            self.inner.dispatch_ip(tx_token, timestamp, pkt)?;
+                            self.inner.dispatch_ip(cx, tx_token, pkt)?;
                         }
 
                         let next_timeout = (timeout + interval).max(timestamp);
@@ -779,6 +777,15 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
             _ => Ok(false)
         }
     }
+
+    fn context(&self, now: Instant) -> Context {
+        Context {
+            now,
+            caps: self.device.capabilities(),
+            #[cfg(feature = "medium-ethernet")]
+            ethernet_address: self.inner.ethernet_addr,
+        }
+    }
 }
 
 impl<'a> InterfaceInner<'a> {
@@ -852,7 +859,7 @@ impl<'a> InterfaceInner<'a> {
 
     #[cfg(feature = "medium-ethernet")]
     fn process_ethernet<'frame, T: AsRef<[u8]>>
-                       (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) ->
+                       (&mut self, cx: &Context, sockets: &mut SocketSet, frame: &'frame T) ->
                        Result<Option<EthernetPacket<'frame>>>
     {
         let eth_frame = EthernetFrame::new_checked(frame)?;
@@ -868,7 +875,7 @@ impl<'a> InterfaceInner<'a> {
         match eth_frame.ethertype() {
             #[cfg(feature = "proto-ipv4")]
             EthernetProtocol::Arp =>
-                self.process_arp(timestamp, &eth_frame),
+                self.process_arp(cx.now, &eth_frame),
             #[cfg(feature = "proto-ipv4")]
             EthernetProtocol::Ipv4 => {
                 let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?;
@@ -876,11 +883,11 @@ impl<'a> InterfaceInner<'a> {
                     // Fill the neighbor cache from IP header of unicast frames.
                     let ip_addr = IpAddress::Ipv4(ipv4_packet.src_addr());
                     if self.in_same_network(&ip_addr) {
-                        self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), timestamp);
+                        self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), cx.now);
                     }
                 }
 
-                self.process_ipv4(sockets, timestamp, &ipv4_packet).map(|o| o.map(EthernetPacket::Ip))
+                self.process_ipv4(cx, sockets, &ipv4_packet).map(|o| o.map(EthernetPacket::Ip))
             }
             #[cfg(feature = "proto-ipv6")]
             EthernetProtocol::Ipv6 => {
@@ -889,12 +896,12 @@ impl<'a> InterfaceInner<'a> {
                     // Fill the neighbor cache from IP header of unicast frames.
                     let ip_addr = IpAddress::Ipv6(ipv6_packet.src_addr());
                     if self.in_same_network(&ip_addr) &&
-                            self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, timestamp).found() {
-                        self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), timestamp);
+                            self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, cx.now).found() {
+                        self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), cx.now);
                     }
                 }
 
-                self.process_ipv6(sockets, timestamp, &ipv6_packet).map(|o| o.map(EthernetPacket::Ip))
+                self.process_ipv6(cx, sockets, &ipv6_packet).map(|o| o.map(EthernetPacket::Ip))
             }
             // Drop all other traffic.
             _ => Err(Error::Unrecognized),
@@ -903,19 +910,19 @@ impl<'a> InterfaceInner<'a> {
 
     #[cfg(feature = "medium-ip")]
     fn process_ip<'frame, T: AsRef<[u8]>>
-                  (&mut self, sockets: &mut SocketSet, timestamp: Instant, ip_payload: &'frame T) ->
+                  (&mut self, cx: &Context, sockets: &mut SocketSet, ip_payload: &'frame T) ->
                   Result<Option<IpPacket<'frame>>>
     {
         match IpVersion::of_packet(ip_payload.as_ref()) {
             #[cfg(feature = "proto-ipv4")]
             Ok(IpVersion::Ipv4) => {
                 let ipv4_packet = Ipv4Packet::new_checked(ip_payload)?;
-                self.process_ipv4(sockets, timestamp, &ipv4_packet)
+                self.process_ipv4(cx, sockets, &ipv4_packet)
             }
             #[cfg(feature = "proto-ipv6")]
             Ok(IpVersion::Ipv6) => {
                 let ipv6_packet = Ipv6Packet::new_checked(ip_payload)?;
-                self.process_ipv6(sockets, timestamp, &ipv6_packet)
+                self.process_ipv6(cx, sockets, &ipv6_packet)
             }
             // Drop all other traffic.
             _ => Err(Error::Unrecognized),
@@ -963,16 +970,15 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "socket-raw"))]
-    fn raw_socket_filter<'frame>(&mut self, sockets: &mut SocketSet, ip_repr: &IpRepr,
+    fn raw_socket_filter<'frame>(&mut self, cx: &Context, sockets: &mut SocketSet, ip_repr: &IpRepr,
                                  ip_payload: &'frame [u8]) -> bool {
-        let checksum_caps = self.device_capabilities.checksum.clone();
         let mut handled_by_raw_socket = false;
 
         // Pass every IP packet to all raw sockets we have registered.
         for mut raw_socket in sockets.iter_mut().filter_map(RawSocket::downcast) {
             if !raw_socket.accepts(&ip_repr) { continue }
 
-            match raw_socket.process(&ip_repr, ip_payload, &checksum_caps) {
+            match raw_socket.process(cx, &ip_repr, ip_payload) {
                 // The packet is valid and handled by socket.
                 Ok(()) => handled_by_raw_socket = true,
                 // The socket buffer is full or the packet was truncated
@@ -986,7 +992,7 @@ impl<'a> InterfaceInner<'a> {
 
     #[cfg(feature = "proto-ipv6")]
     fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>
-                   (&mut self, sockets: &mut SocketSet, timestamp: Instant,
+                   (&mut self, cx: &Context, sockets: &mut SocketSet,
                     ipv6_packet: &Ipv6Packet<&'frame T>) ->
                    Result<Option<IpPacket<'frame>>> {
         let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?;
@@ -1000,11 +1006,11 @@ impl<'a> InterfaceInner<'a> {
         let ip_payload = ipv6_packet.payload();
 
         #[cfg(feature = "socket-raw")]
-        let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
+        let handled_by_raw_socket = self.raw_socket_filter(cx, sockets, &ipv6_repr.into(), ip_payload);
         #[cfg(not(feature = "socket-raw"))]
         let handled_by_raw_socket = false;
 
-        self.process_nxt_hdr(sockets, timestamp, ipv6_repr, ipv6_repr.next_header,
+        self.process_nxt_hdr(cx, sockets, ipv6_repr, ipv6_repr.next_header,
                              handled_by_raw_socket, ip_payload)
     }
 
@@ -1012,24 +1018,24 @@ impl<'a> InterfaceInner<'a> {
     /// function.
     #[cfg(feature = "proto-ipv6")]
     fn process_nxt_hdr<'frame>
-                   (&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr,
+                   (&mut self, cx: &Context, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr,
                     nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8])
                    -> Result<Option<IpPacket<'frame>>>
     {
         match nxt_hdr {
             IpProtocol::Icmpv6 =>
-                self.process_icmpv6(sockets, timestamp, ipv6_repr.into(), ip_payload),
+                self.process_icmpv6(cx, sockets, ipv6_repr.into(), ip_payload),
 
             #[cfg(feature = "socket-udp")]
             IpProtocol::Udp =>
-                self.process_udp(sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload),
+                self.process_udp(cx, sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload),
 
             #[cfg(feature = "socket-tcp")]
             IpProtocol::Tcp =>
-                self.process_tcp(sockets, timestamp, ipv6_repr.into(), ip_payload),
+                self.process_tcp(cx, sockets, ipv6_repr.into(), ip_payload),
 
             IpProtocol::HopByHop =>
-                self.process_hopbyhop(sockets, timestamp, ipv6_repr, handled_by_raw_socket, ip_payload),
+                self.process_hopbyhop(cx, sockets, ipv6_repr, handled_by_raw_socket, ip_payload),
 
             #[cfg(feature = "socket-raw")]
             _ if handled_by_raw_socket =>
@@ -1053,12 +1059,11 @@ impl<'a> InterfaceInner<'a> {
 
     #[cfg(feature = "proto-ipv4")]
     fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized>
-                   (&mut self, sockets: &mut SocketSet, timestamp: Instant,
+                   (&mut self, cx: &Context, sockets: &mut SocketSet,
                     ipv4_packet: &Ipv4Packet<&'frame T>) ->
                    Result<Option<IpPacket<'frame>>>
     {
-        let checksum_caps = self.device_capabilities.checksum.clone();
-        let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?;
+        let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &cx.caps.checksum)?;
 
         if !self.is_unicast_v4(ipv4_repr.src_addr) {
             // Discard packets with non-unicast source addresses.
@@ -1070,7 +1075,7 @@ impl<'a> InterfaceInner<'a> {
         let ip_payload = ipv4_packet.payload();
 
         #[cfg(feature = "socket-raw")]
-        let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
+        let handled_by_raw_socket = self.raw_socket_filter(cx, sockets, &ip_repr, ip_payload);
         #[cfg(not(feature = "socket-raw"))]
         let handled_by_raw_socket = false;
 
@@ -1084,14 +1089,10 @@ impl<'a> InterfaceInner<'a> {
                 if udp_packet.src_port() == DHCP_SERVER_PORT && udp_packet.dst_port() == DHCP_CLIENT_PORT {
                     if let Some(mut dhcp_socket) = sockets.iter_mut().filter_map(Dhcpv4Socket::downcast).next() {
                         let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
-                        let checksum_caps = self.device_capabilities.checksum.clone();
-                        let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &checksum_caps)?;
+                        let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?;
                         let udp_payload = udp_packet.payload();
 
-                        // NOTE(unwrap): we checked for is_some above.
-                        let ethernet_addr = self.ethernet_addr.unwrap();
-
-                        match dhcp_socket.process(timestamp, ethernet_addr, &ipv4_repr, &udp_repr, udp_payload) {
+                        match dhcp_socket.process(cx, &ipv4_repr, &udp_repr, udp_payload) {
                             // The packet is valid and handled by socket.
                             Ok(()) => return Ok(None),
                             // The packet is malformed, or the socket buffer is full.
@@ -1109,7 +1110,7 @@ impl<'a> InterfaceInner<'a> {
             // Ignore IP packets not directed at us, or broadcast, or any of the multicast groups.
             // If AnyIP is enabled, also check if the packet is routed locally.
             if !self.any_ip ||
-                    self.routes.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), timestamp)
+                    self.routes.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), cx.now)
                         .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) {
                 return Ok(None);
             }
@@ -1117,19 +1118,19 @@ impl<'a> InterfaceInner<'a> {
 
         match ipv4_repr.protocol {
             IpProtocol::Icmp =>
-                self.process_icmpv4(sockets, ip_repr, ip_payload),
+                self.process_icmpv4(cx, sockets, ip_repr, ip_payload),
 
             #[cfg(feature = "proto-igmp")]
             IpProtocol::Igmp =>
-                self.process_igmp(timestamp, ipv4_repr, ip_payload),
+                self.process_igmp(cx, ipv4_repr, ip_payload),
 
             #[cfg(feature = "socket-udp")]
             IpProtocol::Udp =>
-                self.process_udp(sockets, ip_repr, handled_by_raw_socket, ip_payload),
+                self.process_udp(cx, sockets, ip_repr, handled_by_raw_socket, ip_payload),
 
             #[cfg(feature = "socket-tcp")]
             IpProtocol::Tcp =>
-                self.process_tcp(sockets, timestamp, ip_repr, ip_payload),
+                self.process_tcp(cx, sockets, ip_repr, ip_payload),
 
             _ if handled_by_raw_socket =>
                 Ok(None),
@@ -1179,7 +1180,7 @@ impl<'a> InterfaceInner<'a> {
     /// Membership must not be reported immediately in order to avoid flooding the network
     /// after a query is broadcasted by a router; this is not currently done.
     #[cfg(feature = "proto-igmp")]
-    fn process_igmp<'frame>(&mut self, timestamp: Instant, ipv4_repr: Ipv4Repr,
+    fn process_igmp<'frame>(&mut self, cx: &Context, ipv4_repr: Ipv4Repr,
                             ip_payload: &'frame [u8]) -> Result<Option<IpPacket<'frame>>> {
         let igmp_packet = IgmpPacket::new_checked(ip_payload)?;
         let igmp_repr = IgmpRepr::parse(&igmp_packet)?;
@@ -1204,7 +1205,7 @@ impl<'a> InterfaceInner<'a> {
                             }
                         };
                         self.igmp_report_state = IgmpReportState::ToGeneralQuery {
-                            version, timeout: timestamp + interval, interval, next_index: 0
+                            version, timeout: cx.now + interval, interval, next_index: 0
                         };
                     }
                 } else {
@@ -1213,7 +1214,7 @@ impl<'a> InterfaceInner<'a> {
                         // Don't respond immediately
                         let timeout = max_resp_time / 4;
                         self.igmp_report_state = IgmpReportState::ToSpecificQuery {
-                            version, timeout: timestamp + timeout, group: group_addr
+                            version, timeout: cx.now + timeout, group: group_addr
                         };
                     }
                 }
@@ -1228,22 +1229,21 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "proto-ipv6")]
-    fn process_icmpv6<'frame>(&mut self, _sockets: &mut SocketSet, _timestamp: Instant,
+    fn process_icmpv6<'frame>(&mut self, cx: &Context, _sockets: &mut SocketSet,
                               ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result<Option<IpPacket<'frame>>>
     {
         let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?;
-        let checksum_caps = self.device_capabilities.checksum.clone();
         let icmp_repr = Icmpv6Repr::parse(&ip_repr.src_addr(), &ip_repr.dst_addr(),
-                                          &icmp_packet, &checksum_caps)?;
+                                          &icmp_packet, &cx.caps.checksum)?;
 
         #[cfg(feature = "socket-icmp")]
         let mut handled_by_icmp_socket = false;
 
         #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))]
         for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) {
-            if !icmp_socket.accepts(&ip_repr, &icmp_repr.into(), &checksum_caps) { continue }
+            if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue }
 
-            match icmp_socket.process(&ip_repr, &icmp_repr.into(), &checksum_caps) {
+            match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) {
                 // The packet is valid and handled by socket.
                 Ok(()) => handled_by_icmp_socket = true,
                 // The socket buffer is full.
@@ -1271,7 +1271,7 @@ impl<'a> InterfaceInner<'a> {
             // Forward any NDISC packets to the ndisc packet handler
             #[cfg(feature = "medium-ethernet")]
             Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr {
-                IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(_timestamp, ipv6_repr, repr),
+                IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx.now, ipv6_repr, repr),
                 _ => Ok(None)
             },
 
@@ -1332,7 +1332,7 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "proto-ipv6")]
-    fn process_hopbyhop<'frame>(&mut self, sockets: &mut SocketSet, timestamp: Instant,
+    fn process_hopbyhop<'frame>(&mut self, cx: &Context, sockets: &mut SocketSet,
                                 ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool,
                                 ip_payload: &'frame [u8]) -> Result<Option<IpPacket<'frame>>>
     {
@@ -1357,26 +1357,25 @@ impl<'a> InterfaceInner<'a> {
                 }
             }
         }
-        self.process_nxt_hdr(sockets, timestamp, ipv6_repr, hbh_repr.next_header,
+        self.process_nxt_hdr(cx, sockets, ipv6_repr, hbh_repr.next_header,
                              handled_by_raw_socket, &ip_payload[hbh_repr.buffer_len()..])
     }
 
     #[cfg(feature = "proto-ipv4")]
-    fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
+    fn process_icmpv4<'frame>(&self, cx: &Context, _sockets: &mut SocketSet, ip_repr: IpRepr,
                               ip_payload: &'frame [u8]) -> Result<Option<IpPacket<'frame>>>
     {
         let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?;
-        let checksum_caps = self.device_capabilities.checksum.clone();
-        let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &checksum_caps)?;
+        let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &cx.caps.checksum)?;
 
         #[cfg(feature = "socket-icmp")]
         let mut handled_by_icmp_socket = false;
 
         #[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.into(), &checksum_caps) { continue }
+            if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue }
 
-            match icmp_socket.process(&ip_repr, &icmp_repr.into(), &checksum_caps) {
+            match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) {
                 // The packet is valid and handled by socket.
                 Ok(()) => handled_by_icmp_socket = true,
                 // The socket buffer is full.
@@ -1472,20 +1471,19 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "socket-udp")]
-    fn process_udp<'frame>(&self, sockets: &mut SocketSet,
+    fn process_udp<'frame>(&self, cx: &Context, sockets: &mut SocketSet,
                            ip_repr: IpRepr, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) ->
                           Result<Option<IpPacket<'frame>>>
     {
         let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
         let udp_packet = UdpPacket::new_checked(ip_payload)?;
-        let checksum_caps = self.device_capabilities.checksum.clone();
-        let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &checksum_caps)?;
+        let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?;
         let udp_payload = udp_packet.payload();
 
         for mut udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) {
             if !udp_socket.accepts(&ip_repr, &udp_repr) { continue }
 
-            match udp_socket.process(&ip_repr, &udp_repr, udp_payload) {
+            match udp_socket.process(cx, &ip_repr, &udp_repr, udp_payload) {
                 // The packet is valid and handled by socket.
                 Ok(()) => return Ok(None),
                 // The packet is malformed, or the socket buffer is full.
@@ -1528,19 +1526,18 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "socket-tcp")]
-    fn process_tcp<'frame>(&self, sockets: &mut SocketSet, timestamp: Instant,
+    fn process_tcp<'frame>(&self, cx: &Context, sockets: &mut SocketSet,
                            ip_repr: IpRepr, ip_payload: &'frame [u8]) ->
                           Result<Option<IpPacket<'frame>>>
     {
         let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
         let tcp_packet = TcpPacket::new_checked(ip_payload)?;
-        let checksum_caps = self.device_capabilities.checksum.clone();
-        let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &checksum_caps)?;
+        let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?;
 
         for mut tcp_socket in sockets.iter_mut().filter_map(TcpSocket::downcast) {
             if !tcp_socket.accepts(&ip_repr, &tcp_repr) { continue }
 
-            match tcp_socket.process(timestamp, &ip_repr, &tcp_repr) {
+            match tcp_socket.process(cx, &ip_repr, &tcp_repr) {
                 // The packet is valid and handled by socket.
                 Ok(reply) => return Ok(reply.map(IpPacket::Tcp)),
                 // The packet is malformed, or doesn't match the socket state,
@@ -1559,7 +1556,7 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "medium-ethernet")]
-    fn dispatch<Tx>(&mut self, tx_token: Tx, timestamp: Instant,
+    fn dispatch<Tx>(&mut self, cx: &Context, tx_token: Tx,
                     packet: EthernetPacket) -> Result<()>
         where Tx: TxToken
     {
@@ -1571,7 +1568,7 @@ impl<'a> InterfaceInner<'a> {
                         ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
                     };
 
-                self.dispatch_ethernet(tx_token, timestamp, arp_repr.buffer_len(), |mut frame| {
+                self.dispatch_ethernet(cx, tx_token, arp_repr.buffer_len(), |mut frame| {
                     frame.set_dst_addr(dst_hardware_addr);
                     frame.set_ethertype(EthernetProtocol::Arp);
 
@@ -1580,18 +1577,18 @@ impl<'a> InterfaceInner<'a> {
                 })
             },
             EthernetPacket::Ip(packet) => {
-                self.dispatch_ip(tx_token, timestamp, packet)
+                self.dispatch_ip(cx, tx_token, packet)
             },
         }
     }
 
     #[cfg(feature = "medium-ethernet")]
-    fn dispatch_ethernet<Tx, F>(&mut self, tx_token: Tx, timestamp: Instant,
+    fn dispatch_ethernet<Tx, F>(&mut self, cx: &Context, tx_token: Tx,
                                 buffer_len: usize, f: F) -> Result<()>
         where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>)
     {
         let tx_len = EthernetFrame::<&[u8]>::buffer_len(buffer_len);
-        tx_token.consume(timestamp, tx_len, |tx_buffer| {
+        tx_token.consume(cx.now, tx_len, |tx_buffer| {
             debug_assert!(tx_buffer.as_ref().len() == tx_len);
             let mut frame = EthernetFrame::new_unchecked(tx_buffer);
             frame.set_src_addr(self.ethernet_addr.unwrap());
@@ -1621,13 +1618,13 @@ impl<'a> InterfaceInner<'a> {
         }
     }
 
-    fn has_neighbor(&self, addr: &IpAddress, timestamp: Instant) -> bool {
-        match self.route(addr, timestamp) {
+    fn has_neighbor(&self, cx: &Context, addr: &IpAddress) -> bool {
+        match self.route(addr, cx.now) {
             Ok(_routed_addr) => {
-                match self.device_capabilities.medium {
+                match cx.caps.medium {
                     #[cfg(feature = "medium-ethernet")]
                     Medium::Ethernet => self.neighbor_cache.as_ref().unwrap()
-                        .lookup(&_routed_addr, timestamp)
+                        .lookup(&_routed_addr, cx.now)
                         .found(),
                     #[cfg(feature = "medium-ip")]
                     Medium::Ip => true,
@@ -1638,7 +1635,7 @@ impl<'a> InterfaceInner<'a> {
     }
 
     #[cfg(feature = "medium-ethernet")]
-    fn lookup_hardware_addr<Tx>(&mut self, tx_token: Tx, timestamp: Instant,
+    fn lookup_hardware_addr<Tx>(&mut self, cx: &Context, tx_token: Tx,
                                 src_addr: &IpAddress, dst_addr: &IpAddress) ->
                                Result<(EthernetAddress, Tx)>
         where Tx: TxToken
@@ -1669,9 +1666,9 @@ impl<'a> InterfaceInner<'a> {
             }
         }
 
-        let dst_addr = self.route(dst_addr, timestamp)?;
+        let dst_addr = self.route(dst_addr, cx.now)?;
 
-        match self.neighbor_cache.as_mut().unwrap().lookup(&dst_addr, timestamp) {
+        match self.neighbor_cache.as_mut().unwrap().lookup(&dst_addr, cx.now) {
             NeighborAnswer::Found(hardware_addr) =>
                 return Ok((hardware_addr, tx_token)),
             NeighborAnswer::RateLimited =>
@@ -1693,7 +1690,7 @@ impl<'a> InterfaceInner<'a> {
                     target_protocol_addr: dst_addr,
                 };
 
-                self.dispatch_ethernet(tx_token, timestamp, arp_repr.buffer_len(), |mut frame| {
+                self.dispatch_ethernet(cx, tx_token, arp_repr.buffer_len(), |mut frame| {
                     frame.set_dst_addr(EthernetAddress::BROADCAST);
                     frame.set_ethertype(EthernetProtocol::Arp);
 
@@ -1722,29 +1719,28 @@ impl<'a> InterfaceInner<'a> {
                     solicit,
                 ));
 
-                self.dispatch_ip(tx_token, timestamp, packet)?;
+                self.dispatch_ip(cx, tx_token, packet)?;
             }
 
             _ => ()
         }
         // The request got dispatched, limit the rate on the cache.
-        self.neighbor_cache.as_mut().unwrap().limit_rate(timestamp);
+        self.neighbor_cache.as_mut().unwrap().limit_rate(cx.now);
         Err(Error::Unaddressable)
     }
 
-    fn dispatch_ip<Tx: TxToken>(&mut self, tx_token: Tx, timestamp: Instant,
+    fn dispatch_ip<Tx: TxToken>(&mut self, cx: &Context, tx_token: Tx,
                           packet: IpPacket) -> Result<()> {
         let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?;
-        let caps = self.device_capabilities.clone();
 
-        match self.device_capabilities.medium {
+        match cx.caps.medium {
             #[cfg(feature = "medium-ethernet")]
             Medium::Ethernet => {
                 let (dst_hardware_addr, tx_token) =
-                    self.lookup_hardware_addr(tx_token, timestamp,
+                    self.lookup_hardware_addr(cx, tx_token,
                                             &ip_repr.src_addr(), &ip_repr.dst_addr())?;
 
-                self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| {
+                self.dispatch_ethernet(cx, tx_token, ip_repr.total_len(), |mut frame| {
                     frame.set_dst_addr(dst_hardware_addr);
                     match ip_repr {
                         #[cfg(feature = "proto-ipv4")]
@@ -1754,22 +1750,22 @@ impl<'a> InterfaceInner<'a> {
                         _ => return
                     }
 
-                    ip_repr.emit(frame.payload_mut(), &caps.checksum);
+                    ip_repr.emit(frame.payload_mut(), &cx.caps.checksum);
 
                     let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..];
-                    packet.emit_payload(ip_repr, payload, &caps);
+                    packet.emit_payload(ip_repr, payload, &cx.caps);
                 })
             }
             #[cfg(feature = "medium-ip")]
             Medium::Ip => {
                 let tx_len = ip_repr.total_len();
-                tx_token.consume(timestamp, tx_len, |mut tx_buffer| {
+                tx_token.consume(cx.now, tx_len, |mut tx_buffer| {
                     debug_assert!(tx_buffer.as_ref().len() == tx_len);
 
-                    ip_repr.emit(&mut tx_buffer, &caps.checksum);
+                    ip_repr.emit(&mut tx_buffer, &cx.caps.checksum);
 
                     let payload = &mut tx_buffer[ip_repr.buffer_len()..];
-                    packet.emit_payload(ip_repr, payload, &caps);
+                    packet.emit_payload(ip_repr, payload, &cx.caps);
 
                     Ok(())
                 })
@@ -1949,7 +1945,8 @@ mod test {
         // Ensure that the unknown protocol frame does not trigger an
         // ICMP error response when the destination address is a
         // broadcast address
-        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame),
                    Ok(None));
     }
 
@@ -1978,7 +1975,8 @@ mod test {
         // Ensure that the unknown protocol frame does not trigger an
         // ICMP error response when the destination address is a
         // broadcast address
-        assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_ipv6(&cx, &mut socket_set, &frame),
                    Ok(None));
     }
 
@@ -2028,7 +2026,8 @@ mod test {
 
         // Ensure that the unknown protocol triggers an error response.
         // And we correctly handle no payload.
-        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame),
                    Ok(Some(expected_repr)));
     }
 
@@ -2128,7 +2127,8 @@ mod test {
 
         // Ensure that the unknown protocol triggers an error response.
         // And we correctly handle no payload.
-        assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr, false, data),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_udp(&cx, &mut socket_set, ip_repr, false, data),
                    Ok(Some(expected_repr)));
 
         let ip_repr = IpRepr::Ipv4(Ipv4Repr {
@@ -2148,7 +2148,7 @@ mod test {
         // Ensure that the port unreachable error does not trigger an
         // ICMP error response when the destination address is a
         // broadcast address and no socket is bound to the port.
-        assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr,
+        assert_eq!(iface.inner.process_udp(&cx, &mut socket_set, ip_repr,
                    false, packet_broadcast.into_inner()), Ok(None));
     }
 
@@ -2212,7 +2212,8 @@ mod test {
                       &ChecksumCapabilities::default());
 
         // Packet should be handled by bound UDP socket
-        assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr, false, packet.into_inner()),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_udp(&cx, &mut socket_set, ip_repr, false, packet.into_inner()),
                    Ok(None));
 
         {
@@ -2276,7 +2277,8 @@ mod test {
         };
         let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
 
-        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame),
                    Ok(Some(expected_packet)));
     }
 
@@ -2365,14 +2367,16 @@ mod test {
             payload_len: expected_icmp_repr.buffer_len()
         };
 
+        let cx = iface.context(Instant::from_secs(0));
+
         // The expected packet does not exceed the IPV4_MIN_MTU
         assert_eq!(expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), MIN_MTU);
         // The expected packet and the generated packet are equal
         #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
-        assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr.into(), false, payload),
+        assert_eq!(iface.inner.process_udp(&cx, &mut socket_set, ip_repr.into(), false, payload),
                    Ok(Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr)))));
         #[cfg(feature = "proto-ipv6")]
-        assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr.into(), false, payload),
+        assert_eq!(iface.inner.process_udp(&cx, &mut socket_set, ip_repr.into(), false, payload),
                    Ok(Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr)))));
     }
 
@@ -2405,8 +2409,10 @@ mod test {
             repr.emit(&mut packet);
         }
 
+        let cx = iface.context(Instant::from_secs(0));
+
         // Ensure an ARP Request for us triggers an ARP Reply
-        assert_eq!(iface.inner.process_ethernet(&mut socket_set, Instant::from_millis(0), frame.into_inner()),
+        assert_eq!(iface.inner.process_ethernet(&cx, &mut socket_set, frame.into_inner()),
                    Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
                        operation: ArpOperation::Reply,
                        source_hardware_addr: local_hw_addr,
@@ -2416,7 +2422,7 @@ mod test {
                    }))));
 
         // Ensure the address of the requestor was entered in the cache
-        assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0),
+        assert_eq!(iface.inner.lookup_hardware_addr(&cx, MockTxToken,
             &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr)),
             Ok((remote_hw_addr, MockTxToken)));
     }
@@ -2471,12 +2477,14 @@ mod test {
             payload_len: icmpv6_expected.buffer_len()
         };
 
+        let cx = iface.context(Instant::from_secs(0));
+
         // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement
-        assert_eq!(iface.inner.process_ethernet(&mut socket_set, Instant::from_millis(0), frame.into_inner()),
+        assert_eq!(iface.inner.process_ethernet(&cx, &mut socket_set, frame.into_inner()),
                    Ok(Some(EthernetPacket::Ip(IpPacket::Icmpv6((ipv6_expected, icmpv6_expected))))));
 
         // Ensure the address of the requestor was entered in the cache
-        assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0),
+        assert_eq!(iface.inner.lookup_hardware_addr(&cx, MockTxToken,
             &IpAddress::Ipv6(local_ip_addr), &IpAddress::Ipv6(remote_ip_addr)),
             Ok((remote_hw_addr, MockTxToken)));
     }
@@ -2508,12 +2516,14 @@ mod test {
             repr.emit(&mut packet);
         }
 
+        let cx = iface.context(Instant::from_secs(0));
+
         // Ensure an ARP Request for someone else does not trigger an ARP Reply
-        assert_eq!(iface.inner.process_ethernet(&mut socket_set, Instant::from_millis(0), frame.into_inner()),
+        assert_eq!(iface.inner.process_ethernet(&cx, &mut socket_set, frame.into_inner()),
                    Ok(None));
 
         // Ensure the address of the requestor was entered in the cache
-        assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0),
+        assert_eq!(iface.inner.lookup_hardware_addr(&cx, MockTxToken,
             &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])),
             &IpAddress::Ipv4(remote_ip_addr)),
             Ok((remote_hw_addr, MockTxToken)));
@@ -2573,7 +2583,8 @@ mod test {
             dst_addr: ipv4_repr.src_addr,
             ..ipv4_repr
         };
-        assert_eq!(iface.inner.process_icmpv4(&mut socket_set, ip_repr, icmp_data),
+        let cx = iface.context(Instant::from_secs(0));
+        assert_eq!(iface.inner.process_icmpv4(&cx, &mut socket_set, ip_repr, icmp_data),
                    Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))));
 
         {
@@ -2656,9 +2667,11 @@ mod test {
             hop_limit:   0x40,
         };
 
+        let cx = iface.context(Instant::from_secs(0));
+
         // Ensure the unknown next header causes a ICMPv6 Parameter Problem
         // error message to be sent to the sender.
-        assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame),
+        assert_eq!(iface.inner.process_ipv6(&cx, &mut socket_set, &frame),
                    Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))));
     }
 
@@ -2739,7 +2752,8 @@ mod test {
         // loopback have been processed, including responses to
         // GENERAL_QUERY_BYTES. Therefore `recv_all()` would return 0
         // pkts that could be checked.
-        iface.socket_ingress(&mut socket_set, timestamp);
+        let cx = iface.context(timestamp);
+        iface.socket_ingress(&cx, &mut socket_set);
 
         // Leave multicast groups
         let timestamp = Instant::now();
@@ -2811,7 +2825,8 @@ mod test {
             Ipv4Packet::new_unchecked(&bytes)
         };
 
-        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_millis(0));
+        assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame),
                    Ok(None));
     }
 
@@ -2869,7 +2884,8 @@ mod test {
             Ipv4Packet::new_unchecked(&bytes)
         };
 
-        let frame = iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame);
+        let cx = iface.context(Instant::from_millis(0));
+        let frame = iface.inner.process_ipv4(&cx, &mut socket_set, &frame);
 
         // because the packet could not be handled we should send an Icmp message
         assert!(match frame {
@@ -2945,7 +2961,8 @@ mod test {
             Ipv4Packet::new_unchecked(&bytes)
         };
 
-        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+        let cx = iface.context(Instant::from_millis(0));
+        assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame),
                    Ok(None));
 
         {

+ 11 - 0
src/phy/mod.rs

@@ -230,6 +230,17 @@ pub struct DeviceCapabilities {
     pub checksum: ChecksumCapabilities,
 }
 
+impl DeviceCapabilities {
+    pub fn ip_mtu(&self) -> usize {
+        match self.medium {
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len(),
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => self.max_transmission_unit,
+        }
+    }
+}
+
 /// Type of medium of a device.
 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]

+ 21 - 17
src/socket/dhcpv4.rs

@@ -1,10 +1,10 @@
 use crate::{Error, Result};
-use crate::wire::{EthernetAddress, IpProtocol, IpAddress,
+use crate::wire::{IpProtocol, IpAddress,
            Ipv4Cidr, Ipv4Address, Ipv4Repr,
            UdpRepr, UDP_HEADER_LEN,
            DhcpPacket, DhcpRepr, DhcpMessageType, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, DHCP_MAX_DNS_SERVER_COUNT};
 use crate::wire::dhcpv4::{field as dhcpv4_field};
-use crate::socket::SocketMeta;
+use crate::socket::{SocketMeta, Context};
 use crate::time::{Instant, Duration};
 use crate::socket::SocketHandle;
 
@@ -150,7 +150,7 @@ impl Dhcpv4Socket {
         self.max_lease_duration = max_lease_duration;
     }
 
-    pub(crate) fn poll_at(&self) -> PollAt {
+    pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
         let t = match &self.state {
             ClientState::Discovering(state) => state.retry_at,
             ClientState::Requesting(state) => state.retry_at,
@@ -159,7 +159,7 @@ impl Dhcpv4Socket {
         PollAt::Time(t)
     }
 
-    pub(crate) fn process(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
+    pub(crate) fn process(&mut self, cx: &Context, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
         let src_ip = ip_repr.src_addr;
 
         // This is enforced in interface.rs.
@@ -179,7 +179,7 @@ impl Dhcpv4Socket {
                 return Ok(());
             }
         };
-        if dhcp_repr.client_hardware_address != ethernet_addr { return Ok(()) }
+        if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { return Ok(()) }
         if dhcp_repr.transaction_id != self.transaction_id { return Ok(()) }
         let server_identifier = match dhcp_repr.server_identifier {
             Some(server_identifier) => server_identifier,
@@ -199,7 +199,7 @@ impl Dhcpv4Socket {
                 }
                 
                 self.state = ClientState::Requesting(RequestState {
-                    retry_at: now,
+                    retry_at: cx.now,
                     retry: 0,
                     server: ServerInfo {
                         address: src_ip,
@@ -209,7 +209,7 @@ impl Dhcpv4Socket {
                 });
             }
             (ClientState::Requesting(state), DhcpMessageType::Ack) => {
-                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, &dhcp_repr, self.max_lease_duration) {
+                if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
                     self.config_changed = true;
                     self.state = ClientState::Renewing(RenewState{
                         server: state.server,
@@ -223,7 +223,7 @@ impl Dhcpv4Socket {
                 self.reset();
             }
             (ClientState::Renewing(state), DhcpMessageType::Ack) => {
-                if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, &dhcp_repr, self.max_lease_duration) {
+                if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
                     state.renew_at = renew_at;
                     state.expires_at = expires_at;
                     if state.config != config {
@@ -298,9 +298,13 @@ impl Dhcpv4Socket {
         Some((config, renew_at, expires_at))
     }
 
-    pub(crate) fn dispatch<F>(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_mtu: usize, emit: F) -> Result<()>
+    pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
             where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> {
 
+        // note: Dhcpv4Socket is only usable in ethernet mediums, so the 
+        // unwrap can never fail.
+        let ethernet_addr = cx.ethernet_address.unwrap();
+
         // Worst case biggest IPv4 header length.
         // 0x0f * 4 = 60 bytes.
         const MAX_IPV4_HEADER_LEN: usize = 60;
@@ -324,7 +328,7 @@ impl Dhcpv4Socket {
             client_identifier: Some(ethernet_addr),
             server_identifier: None,
             parameter_request_list: Some(PARAMETER_REQUEST_LIST),
-            max_size: Some((ip_mtu - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16),
+            max_size: Some((cx.caps.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16),
             lease_duration: None,
             dns_servers: None,
         };
@@ -344,7 +348,7 @@ impl Dhcpv4Socket {
 
         match &mut self.state {
             ClientState::Discovering(state) => {
-                if now < state.retry_at {
+                if cx.now < state.retry_at {
                     return Err(Error::Exhausted)
                 }
 
@@ -354,12 +358,12 @@ impl Dhcpv4Socket {
                 emit((ipv4_repr, udp_repr, dhcp_repr))?;
 
                 // Update state AFTER the packet has been successfully sent.
-                state.retry_at = now + DISCOVER_TIMEOUT;
+                state.retry_at = cx.now + DISCOVER_TIMEOUT;
                 self.transaction_id = next_transaction_id;
                 Ok(())
             }
             ClientState::Requesting(state) => {
-                if now < state.retry_at {
+                if cx.now < state.retry_at {
                     return Err(Error::Exhausted)
                 }
 
@@ -380,21 +384,21 @@ impl Dhcpv4Socket {
                 emit((ipv4_repr, udp_repr, dhcp_repr))?;
 
                 // Exponential backoff: Double every 2 retries.
-                state.retry_at = now + (REQUEST_TIMEOUT << (state.retry as u32 / 2));
+                state.retry_at = cx.now + (REQUEST_TIMEOUT << (state.retry as u32 / 2));
                 state.retry += 1;
 
                 self.transaction_id = next_transaction_id;
                 Ok(())
             }
             ClientState::Renewing(state) => {
-                if state.expires_at <= now {
+                if state.expires_at <= cx.now {
                     net_debug!("DHCP lease expired");
                     self.reset();
                     // return Ok so we get polled again
                     return Ok(())
                 }
     
-                if now < state.renew_at {
+                if cx.now < state.renew_at {
                     return Err(Error::Exhausted)
                 }
 
@@ -413,7 +417,7 @@ impl Dhcpv4Socket {
                 // of the remaining time until T2 (in RENEWING state) and one-half of
                 // the remaining lease time (in REBINDING state), down to a minimum of
                 // 60 seconds, before retransmitting the DHCPREQUEST message.
-                state.renew_at = now + MIN_RENEW_TIMEOUT.max((state.expires_at - now) / 2);
+                state.renew_at = cx.now + MIN_RENEW_TIMEOUT.max((state.expires_at - cx.now) / 2);
 
                 self.transaction_id = next_transaction_id;
                 Ok(())

+ 29 - 31
src/socket/icmp.rs

@@ -4,7 +4,7 @@ use core::task::Waker;
 
 use crate::{Error, Result};
 use crate::phy::ChecksumCapabilities;
-use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
+use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
 use crate::storage::{PacketBuffer, PacketMetadata};
 #[cfg(feature = "async")]
 use crate::socket::WakerRegistration;
@@ -326,8 +326,7 @@ impl<'a> IcmpSocket<'a> {
 
     /// Filter determining which packets received by the interface are appended to
     /// the given sockets received buffer.
-    pub(crate) fn accepts(&self, ip_repr: &IpRepr, icmp_repr: &IcmpRepr,
-                          cksum: &ChecksumCapabilities) -> bool {
+    pub(crate) fn accepts(&self, cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> bool {
         match (&self.endpoint, icmp_repr) {
             // If we are bound to ICMP errors associated to a UDP port, only
             // accept Destination Unreachable messages with the data containing
@@ -336,7 +335,7 @@ impl<'a> IcmpSocket<'a> {
             (&Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }))
                     if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
                 let packet = UdpPacket::new_unchecked(data);
-                match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), cksum) {
+                match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
                     Ok(repr) => endpoint.port == repr.src_port,
                     Err(_) => false,
                 }
@@ -345,7 +344,7 @@ impl<'a> IcmpSocket<'a> {
             (&Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }))
                     if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
                 let packet = UdpPacket::new_unchecked(data);
-                match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), cksum) {
+                match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
                     Ok(repr) => endpoint.port == repr.src_port,
                     Err(_) => false,
                 }
@@ -369,8 +368,7 @@ impl<'a> IcmpSocket<'a> {
         }
     }
 
-    pub(crate) fn process(&mut self, ip_repr: &IpRepr, icmp_repr: &IcmpRepr,
-                          _cksum: &ChecksumCapabilities) -> Result<()> {
+    pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> Result<()> {
         match *icmp_repr {
             #[cfg(feature = "proto-ipv4")]
             IcmpRepr::Ipv4(ref icmp_repr) => {
@@ -401,7 +399,7 @@ impl<'a> IcmpSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn dispatch<F>(&mut self, emit: F) -> Result<()>
+    pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
         where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>
     {
         let handle    = self.meta.handle;
@@ -447,7 +445,7 @@ impl<'a> IcmpSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn poll_at(&self) -> PollAt {
+    pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
         if self.tx_buffer.is_empty() {
             PollAt::Ingress
         } else {
@@ -532,7 +530,7 @@ mod test_ipv4 {
         let mut socket = socket(buffer(0), buffer(1));
         let checksum = ChecksumCapabilities::default();
 
-        assert_eq!(socket.dispatch(|_| unreachable!()),
+        assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                    Err(Error::Exhausted));
 
         // This buffer is too long
@@ -547,7 +545,7 @@ mod test_ipv4 {
         assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV4_REPR);
             assert_eq!(icmp_repr, ECHOV4_REPR.into());
             Err(Error::Unaddressable)
@@ -555,7 +553,7 @@ mod test_ipv4 {
         // buffer is not taken off of the tx queue due to the error
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV4_REPR);
             assert_eq!(icmp_repr, ECHOV4_REPR.into());
             Ok(())
@@ -576,7 +574,7 @@ mod test_ipv4 {
         s.set_hop_limit(Some(0x2a));
 
         assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
-        assert_eq!(s.dispatch(|(ip_repr, _)| {
+        assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
             assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
                 src_addr: Ipv4Address::UNSPECIFIED,
                 dst_addr: REMOTE_IPV4,
@@ -603,13 +601,13 @@ mod test_ipv4 {
         ECHOV4_REPR.emit(&mut packet, &checksum);
         let data = &packet.into_inner()[..];
 
-        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum));
-        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum));
-        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
                    Err(Error::Exhausted));
 
         assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into())));
@@ -633,7 +631,7 @@ mod test_ipv4 {
 
         // Ensure that a packet with an identifier that isn't the bound
         // ID is not accepted
-        assert!(!socket.accepts(&REMOTE_IPV4_REPR, &icmp_repr.into(), &checksum));
+        assert!(!socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &icmp_repr.into()));
     }
 
     #[test]
@@ -678,8 +676,8 @@ mod test_ipv4 {
 
         // Ensure we can accept ICMP error response to the bound
         // UDP port
-        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum));
-        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
                    Ok(()));
         assert!(socket.can_recv());
 
@@ -737,7 +735,7 @@ mod test_ipv6 {
         let mut socket = socket(buffer(0), buffer(1));
         let checksum = ChecksumCapabilities::default();
 
-        assert_eq!(socket.dispatch(|_| unreachable!()),
+        assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                    Err(Error::Exhausted));
 
         // This buffer is too long
@@ -752,7 +750,7 @@ mod test_ipv6 {
         assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV6_REPR);
             assert_eq!(icmp_repr, ECHOV6_REPR.into());
             Err(Error::Unaddressable)
@@ -760,7 +758,7 @@ mod test_ipv6 {
         // buffer is not taken off of the tx queue due to the error
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV6_REPR);
             assert_eq!(icmp_repr, ECHOV6_REPR.into());
             Ok(())
@@ -781,7 +779,7 @@ mod test_ipv6 {
         s.set_hop_limit(Some(0x2a));
 
         assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
-        assert_eq!(s.dispatch(|(ip_repr, _)| {
+        assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
             assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr {
                 src_addr: Ipv6Address::UNSPECIFIED,
                 dst_addr: REMOTE_IPV6,
@@ -808,13 +806,13 @@ mod test_ipv6 {
         ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
         let data = &packet.into_inner()[..];
 
-        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum));
-        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum));
-        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
                    Err(Error::Exhausted));
 
         assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into())));
@@ -838,7 +836,7 @@ mod test_ipv6 {
 
         // Ensure that a packet with an identifier that isn't the bound
         // ID is not accepted
-        assert!(!socket.accepts(&REMOTE_IPV6_REPR, &icmp_repr.into(), &checksum));
+        assert!(!socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &icmp_repr.into()));
     }
 
     #[test]
@@ -883,8 +881,8 @@ mod test_ipv6 {
 
         // Ensure we can accept ICMP error response to the bound
         // UDP port
-        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum));
-        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum),
+        assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
+        assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
                    Ok(()));
         assert!(socket.can_recv());
 

+ 42 - 2
src/socket/mod.rs

@@ -11,6 +11,7 @@ The interface implemented by this module uses explicit buffering: you decide on
 size for a buffer, allocate it, and let the networking stack use it.
 */
 
+use crate::phy::DeviceCapabilities;
 use crate::time::Instant;
 
 mod meta;
@@ -138,8 +139,8 @@ impl<'a> Socket<'a> {
         dispatch_socket!(mut self, |socket| &mut socket.meta)
     }
 
-    pub(crate) fn poll_at(&self) -> PollAt {
-        dispatch_socket!(self, |socket| socket.poll_at())
+    pub(crate) fn poll_at(&self, cx: &Context) -> PollAt {
+        dispatch_socket!(self, |socket| socket.poll_at(cx))
     }
 }
 
@@ -180,3 +181,42 @@ from_socket!(UdpSocket<'a>, Udp);
 from_socket!(TcpSocket<'a>, Tcp);
 #[cfg(feature = "socket-dhcpv4")]
 from_socket!(Dhcpv4Socket, Dhcpv4);
+
+/// Data passed to sockets when processing.
+#[derive(Clone, Debug)]
+pub(crate) struct Context {
+    pub now: Instant,
+    #[cfg(feature = "medium-ethernet")]
+    pub ethernet_address: Option<crate::wire::EthernetAddress>,
+    pub caps: DeviceCapabilities,
+}
+
+#[cfg(test)]
+impl Context {
+
+    pub(crate) const DUMMY: Context = Context {
+        caps: DeviceCapabilities {
+            #[cfg(feature = "medium-ethernet")]
+            medium: crate::phy::Medium::Ethernet,
+            #[cfg(not(feature = "medium-ethernet"))]
+            medium: crate::phy::Medium::Ip,
+            checksum: crate::phy::ChecksumCapabilities{
+                #[cfg(feature = "proto-ipv4")]
+                icmpv4: crate::phy::Checksum::Both,
+                #[cfg(feature = "proto-ipv6")]
+                icmpv6: crate::phy::Checksum::Both,
+                ipv4: crate::phy::Checksum::Both,
+                tcp: crate::phy::Checksum::Both,
+                udp: crate::phy::Checksum::Both,
+            },
+            max_burst_size: None,
+            #[cfg(feature = "medium-ethernet")]
+            max_transmission_unit: 1514,
+            #[cfg(not(feature = "medium-ethernet"))]
+            max_transmission_unit: 1500,
+        },
+        ethernet_address: None,
+        now: Instant{millis: 0},
+    };
+
+}

+ 19 - 29
src/socket/raw.rs

@@ -4,7 +4,7 @@ use core::task::Waker;
 
 use crate::{Error, Result};
 use crate::phy::ChecksumCapabilities;
-use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
+use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
 use crate::storage::{PacketBuffer, PacketMetadata};
 #[cfg(feature = "async")]
 use crate::socket::WakerRegistration;
@@ -206,14 +206,13 @@ impl<'a> RawSocket<'a> {
         true
     }
 
-    pub(crate) fn process(&mut self, ip_repr: &IpRepr, payload: &[u8],
-                          checksum_caps: &ChecksumCapabilities) -> Result<()> {
+    pub(crate) fn process(&mut self, cx: &Context, ip_repr: &IpRepr, payload: &[u8]) -> Result<()> {
         debug_assert!(self.accepts(ip_repr));
 
         let header_len = ip_repr.buffer_len();
         let total_len  = header_len + payload.len();
         let packet_buf = self.rx_buffer.enqueue(total_len, ())?;
-        ip_repr.emit(&mut packet_buf[..header_len], &checksum_caps);
+        ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum);
         packet_buf[header_len..].copy_from_slice(payload);
 
         net_trace!("{}:{}:{}: receiving {} octets",
@@ -226,8 +225,7 @@ impl<'a> RawSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn dispatch<F>(&mut self, checksum_caps: &ChecksumCapabilities, emit: F) ->
-                             Result<()>
+    pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
             where F: FnOnce((IpRepr, &[u8])) -> Result<()> {
         fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
                    _checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
@@ -264,7 +262,7 @@ impl<'a> RawSocket<'a> {
         let ip_protocol = self.ip_protocol;
         let ip_version  = self.ip_version;
         self.tx_buffer.dequeue_with(|&mut (), packet_buf| {
-            match prepare(ip_protocol, packet_buf, &checksum_caps) {
+            match prepare(ip_protocol, packet_buf, &cx.caps.checksum) {
                 Ok((ip_repr, raw_packet)) => {
                     net_trace!("{}:{}:{}: sending {} octets",
                                handle, ip_version, ip_protocol,
@@ -287,7 +285,7 @@ impl<'a> RawSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn poll_at(&self) -> PollAt {
+    pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
         if self.tx_buffer.is_empty() {
             PollAt::Ingress
         } else {
@@ -401,25 +399,24 @@ mod test {
 
                 #[test]
                 fn test_send_dispatch() {
-                    let checksum_caps = &ChecksumCapabilities::default();
                     let mut socket = $socket(buffer(0), buffer(1));
 
                     assert!(socket.can_send());
-                    assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
+                    assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                                Err(Error::Exhausted));
 
                     assert_eq!(socket.send_slice(&$packet[..]), Ok(()));
                     assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
                     assert!(!socket.can_send());
 
-                    assert_eq!(socket.dispatch(&checksum_caps, |(ip_repr, ip_payload)| {
+                    assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
                         assert_eq!(ip_repr, $hdr);
                         assert_eq!(ip_payload, &$payload);
                         Err(Error::Unaddressable)
                     }), Err(Error::Unaddressable));
                     assert!(!socket.can_send());
 
-                    assert_eq!(socket.dispatch(&checksum_caps, |(ip_repr, ip_payload)| {
+                    assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
                         assert_eq!(ip_repr, $hdr);
                         assert_eq!(ip_payload, &$payload);
                         Ok(())
@@ -432,8 +429,7 @@ mod test {
                     let mut socket = $socket(buffer(1), buffer(0));
 
                     assert!(socket.accepts(&$hdr));
-                    assert_eq!(socket.process(&$hdr, &$payload,
-                                              &ChecksumCapabilities::default()), Ok(()));
+                    assert_eq!(socket.process(&Context::DUMMY, &$hdr, &$payload), Ok(()));
 
                     let mut slice = [0; 4];
                     assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4));
@@ -448,8 +444,7 @@ mod test {
                     buffer[..$packet.len()].copy_from_slice(&$packet[..]);
 
                     assert!(socket.accepts(&$hdr));
-                    assert_eq!(socket.process(&$hdr, &buffer, &ChecksumCapabilities::default()),
-                               Err(Error::Truncated));
+                    assert_eq!(socket.process(&Context::DUMMY, &$hdr, &buffer), Err(Error::Truncated));
                 }
             }
         }
@@ -467,7 +462,6 @@ mod test {
     #[test]
     #[cfg(feature = "proto-ipv4")]
     fn test_send_illegal() {
-        let checksum_caps = &ChecksumCapabilities::default();
         #[cfg(feature = "proto-ipv4")]
         {
             let mut socket = ipv4_locals::socket(buffer(0), buffer(2));
@@ -476,14 +470,14 @@ mod test {
             Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6);
 
             assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
-            assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
+            assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                        Ok(()));
 
             let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
             Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
 
             assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
-            assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
+            assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                        Ok(()));
         }
         #[cfg(feature = "proto-ipv6")]
@@ -494,14 +488,14 @@ mod test {
             Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4);
 
             assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
-            assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
+            assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                        Ok(()));
 
             let mut wrong_protocol = ipv6_locals::PACKET_BYTES;
             Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp);
 
             assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
-            assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
+            assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                        Ok(()));
         }
     }
@@ -518,14 +512,12 @@ mod test {
 
             assert_eq!(socket.recv(), Err(Error::Exhausted));
             assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
-            assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
-                                      &ChecksumCapabilities::default()),
+            assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
                        Ok(()));
             assert!(socket.can_recv());
 
             assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
-            assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
-                                      &ChecksumCapabilities::default()),
+            assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
                        Err(Error::Exhausted));
             assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
             assert!(!socket.can_recv());
@@ -537,14 +529,12 @@ mod test {
 
             assert_eq!(socket.recv(), Err(Error::Exhausted));
             assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
-            assert_eq!(socket.process(&ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD,
-                                      &ChecksumCapabilities::default()),
+            assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
                        Ok(()));
             assert!(socket.can_recv());
 
             assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
-            assert_eq!(socket.process(&ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD,
-                                      &ChecksumCapabilities::default()),
+            assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
                        Err(Error::Exhausted));
             assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..]));
             assert!(!socket.can_recv());

+ 62 - 60
src/socket/tcp.rs

@@ -8,7 +8,7 @@ use core::task::Waker;
 
 use crate::{Error, Result};
 use crate::time::{Duration, Instant};
-use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
+use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
 use crate::storage::{Assembler, RingBuffer};
 #[cfg(feature = "async")]
 use crate::socket::WakerRegistration;
@@ -1132,7 +1132,7 @@ impl<'a> TcpSocket<'a> {
         true
     }
 
-    pub(crate) fn process(&mut self, timestamp: Instant, ip_repr: &IpRepr, repr: &TcpRepr) ->
+    pub(crate) fn process(&mut self, cx: &Context, ip_repr: &IpRepr, repr: &TcpRepr) ->
                          Result<Option<(IpRepr, TcpRepr<'static>)>> {
         debug_assert!(self.accepts(ip_repr, repr));
 
@@ -1267,7 +1267,7 @@ impl<'a> TcpSocket<'a> {
                     // If we're in the TIME-WAIT state, restart the TIME-WAIT timeout, since
                     // the remote end may not have realized we've closed the connection.
                     if self.state == State::TimeWait {
-                        self.timer.set_for_close(timestamp);
+                        self.timer.set_for_close(cx.now);
                     }
 
                     return Ok(Some(self.ack_reply(ip_repr, &repr)))
@@ -1296,7 +1296,7 @@ impl<'a> TcpSocket<'a> {
                     ack_of_fin = true;
                 }
 
-                self.rtte.on_ack(timestamp, ack_number);
+                self.rtte.on_ack(cx.now, ack_number);
             }
         }
 
@@ -1356,13 +1356,13 @@ impl<'a> TcpSocket<'a> {
                     self.remote_win_shift = 0;
                 }
                 self.set_state(State::SynReceived);
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // ACK packets in the SYN-RECEIVED state change it to ESTABLISHED.
             (State::SynReceived, TcpControl::None) => {
                 self.set_state(State::Established);
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // FIN packets in the SYN-RECEIVED state change it to CLOSE-WAIT.
@@ -1372,7 +1372,7 @@ impl<'a> TcpSocket<'a> {
                 self.remote_seq_no  += 1;
                 self.rx_fin_received = true;
                 self.set_state(State::CloseWait);
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // SYN|ACK packets in the SYN-SENT state change it to ESTABLISHED.
@@ -1387,14 +1387,14 @@ impl<'a> TcpSocket<'a> {
                     self.remote_mss = max_seg_size as usize;
                 }
                 self.set_state(State::Established);
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // ACK packets in ESTABLISHED state reset the retransmit timer,
             // except for duplicate ACK packets which preserve it.
             (State::Established, TcpControl::None) => {
                 if !self.timer.is_retransmit() || ack_len != 0 {
-                    self.timer.set_for_idle(timestamp, self.keep_alive);
+                    self.timer.set_for_idle(cx.now, self.keep_alive);
                 }
             },
 
@@ -1403,7 +1403,7 @@ impl<'a> TcpSocket<'a> {
                 self.remote_seq_no  += 1;
                 self.rx_fin_received = true;
                 self.set_state(State::CloseWait);
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // ACK packets in FIN-WAIT-1 state change it to FIN-WAIT-2, if we've already
@@ -1412,7 +1412,7 @@ impl<'a> TcpSocket<'a> {
                 if ack_of_fin {
                     self.set_state(State::FinWait2);
                 }
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // FIN packets in FIN-WAIT-1 state change it to CLOSING, or to TIME-WAIT
@@ -1422,16 +1422,16 @@ impl<'a> TcpSocket<'a> {
                 self.rx_fin_received = true;
                 if ack_of_fin {
                     self.set_state(State::TimeWait);
-                    self.timer.set_for_close(timestamp);
+                    self.timer.set_for_close(cx.now);
                 } else {
                     self.set_state(State::Closing);
-                    self.timer.set_for_idle(timestamp, self.keep_alive);
+                    self.timer.set_for_idle(cx.now, self.keep_alive);
                 }
             }
 
             // Data packets in FIN-WAIT-2 reset the idle timer.
             (State::FinWait2, TcpControl::None) => {
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // FIN packets in FIN-WAIT-2 state change it to TIME-WAIT.
@@ -1439,22 +1439,22 @@ impl<'a> TcpSocket<'a> {
                 self.remote_seq_no  += 1;
                 self.rx_fin_received = true;
                 self.set_state(State::TimeWait);
-                self.timer.set_for_close(timestamp);
+                self.timer.set_for_close(cx.now);
             }
 
             // ACK packets in CLOSING state change it to TIME-WAIT.
             (State::Closing, TcpControl::None) => {
                 if ack_of_fin {
                     self.set_state(State::TimeWait);
-                    self.timer.set_for_close(timestamp);
+                    self.timer.set_for_close(cx.now);
                 } else {
-                    self.timer.set_for_idle(timestamp, self.keep_alive);
+                    self.timer.set_for_idle(cx.now, self.keep_alive);
                 }
             }
 
             // ACK packets in CLOSE-WAIT state reset the retransmit timer.
             (State::CloseWait, TcpControl::None) => {
-                self.timer.set_for_idle(timestamp, self.keep_alive);
+                self.timer.set_for_idle(cx.now, self.keep_alive);
             }
 
             // ACK packets in LAST-ACK state change it to CLOSED.
@@ -1465,7 +1465,7 @@ impl<'a> TcpSocket<'a> {
                     self.local_endpoint  = IpEndpoint::default();
                     self.remote_endpoint = IpEndpoint::default();
                 } else {
-                    self.timer.set_for_idle(timestamp, self.keep_alive);
+                    self.timer.set_for_idle(cx.now, self.keep_alive);
                 }
             }
 
@@ -1477,7 +1477,7 @@ impl<'a> TcpSocket<'a> {
         }
 
         // Update remote state.
-        self.remote_last_ts = Some(timestamp);
+        self.remote_last_ts = Some(cx.now);
 
         // RFC 1323: The window field (SEG.WND) in the header of every incoming segment, with the
         // exception of SYN segments, is left-shifted by Snd.Wind.Scale bits before updating SND.WND.
@@ -1600,7 +1600,7 @@ impl<'a> TcpSocket<'a> {
                             self.meta.handle, self.local_endpoint, self.remote_endpoint
                         );
 
-                        Some(timestamp + ack_delay)
+                        Some(cx.now + ack_delay)
                     }
                     // RFC1122 says "in a stream of full-sized segments there SHOULD be an ACK
                     // for at least every second segment".
@@ -1687,8 +1687,7 @@ impl<'a> TcpSocket<'a> {
         }
     }
 
-    pub(crate) fn dispatch<F>(&mut self, timestamp: Instant, ip_mtu: usize,
-                              emit: F) -> Result<()>
+    pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
             where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> {
         if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) }
 
@@ -1700,17 +1699,17 @@ impl<'a> TcpSocket<'a> {
             // period of time, it isn't anymore, and the local endpoint is talking.
             // So, we start counting the timeout not from the last received packet
             // but from the first transmitted one.
-            self.remote_last_ts = Some(timestamp);
+            self.remote_last_ts = Some(cx.now);
         }
 
         // Check if any state needs to be changed because of a timer.
-        if self.timed_out(timestamp) {
+        if self.timed_out(cx.now) {
             // If a timeout expires, we should abort the connection.
             net_debug!("{}:{}:{}: timeout exceeded",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
             self.set_state(State::Closed);
         } else if !self.seq_to_transmit() {
-            if let Some(retransmit_delta) = self.timer.should_retransmit(timestamp) {
+            if let Some(retransmit_delta) = self.timer.should_retransmit(cx.now) {
                 // If a retransmit timer expired, we should resend data starting at the last ACK.
                 net_debug!("{}:{}:{}: retransmitting at t+{}",
                            self.meta.handle, self.local_endpoint, self.remote_endpoint,
@@ -1725,11 +1724,11 @@ impl<'a> TcpSocket<'a> {
             // If we have data to transmit and it fits into partner's window, do it.
             net_trace!("{}:{}:{}: outgoing segment will send data or flags",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
-        } else if self.ack_to_transmit() && self.delayed_ack_expired(timestamp) {
+        } else if self.ack_to_transmit() && self.delayed_ack_expired(cx.now) {
             // If we have data to acknowledge, do it.
             net_trace!("{}:{}:{}: outgoing segment will acknowledge",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
-        } else if self.window_to_update() && self.delayed_ack_expired(timestamp) {
+        } else if self.window_to_update() && self.delayed_ack_expired(cx.now) {
             // If we have window length increase to advertise, do it.
             net_trace!("{}:{}:{}: outgoing segment will update window",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
@@ -1737,15 +1736,15 @@ impl<'a> TcpSocket<'a> {
             // If we need to abort the connection, do it.
             net_trace!("{}:{}:{}: outgoing segment will abort connection",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
-        } else if self.timer.should_retransmit(timestamp).is_some() {
+        } else if self.timer.should_retransmit(cx.now).is_some() {
             // If we have packets to retransmit, do it.
             net_trace!("{}:{}:{}: retransmit timer expired",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
-        } else if self.timer.should_keep_alive(timestamp) {
+        } else if self.timer.should_keep_alive(cx.now) {
             // If we need to transmit a keep-alive packet, do it.
             net_trace!("{}:{}:{}: keep-alive timer expired",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
-        } else if self.timer.should_close(timestamp) {
+        } else if self.timer.should_close(cx.now) {
             // If we have spent enough time in the TIME-WAIT state, close the socket.
             net_trace!("{}:{}:{}: TIME-WAIT timer expired",
                        self.meta.handle, self.local_endpoint, self.remote_endpoint);
@@ -1833,7 +1832,7 @@ impl<'a> TcpSocket<'a> {
                 // 3. MSS we can send, determined by our MTU.
                 let size = win_limit
                     .min(self.remote_mss)
-                    .min(ip_mtu - ip_repr.buffer_len() - repr.mss_header_len());
+                    .min(cx.caps.ip_mtu() - ip_repr.buffer_len() - repr.mss_header_len());
 
                 let offset = self.remote_last_seq - self.local_seq_no;
                 repr.payload = self.tx_buffer.get_allocated(offset, size);
@@ -1860,7 +1859,7 @@ impl<'a> TcpSocket<'a> {
         // sequence space will elicit an ACK, we only need to send an explicit packet if we
         // couldn't fill the sequence space with anything.
         let is_keep_alive;
-        if self.timer.should_keep_alive(timestamp) && repr.is_empty() {
+        if self.timer.should_keep_alive(cx.now) && repr.is_empty() {
             repr.seq_number = repr.seq_number - 1;
             repr.payload    = b"\x00"; // RFC 1122 says we should do this
             is_keep_alive = true;
@@ -1895,7 +1894,7 @@ impl<'a> TcpSocket<'a> {
 
         if repr.control == TcpControl::Syn {
             // Fill the MSS option. See RFC 6691 for an explanation of this calculation.
-            let mut max_segment_size = ip_mtu;
+            let mut max_segment_size = cx.caps.ip_mtu();
             max_segment_size -= ip_repr.buffer_len();
             max_segment_size -= repr.mss_header_len();
             repr.max_seg_size = Some(max_segment_size as u16);
@@ -1913,7 +1912,7 @@ impl<'a> TcpSocket<'a> {
 
         // We've sent something, whether useful data or a keep-alive packet, so rewind
         // the keep-alive timer.
-        self.timer.rewind_keep_alive(timestamp, self.keep_alive);
+        self.timer.rewind_keep_alive(cx.now, self.keep_alive);
 
         // Reset delayed-ack timer
         if self.ack_delay_until.is_some() {
@@ -1934,13 +1933,13 @@ impl<'a> TcpSocket<'a> {
         self.remote_last_win = repr.window_len;
 
         if repr.segment_len() > 0 {
-            self.rtte.on_send(timestamp, repr.seq_number + repr.segment_len());
+            self.rtte.on_send(cx.now, repr.seq_number + repr.segment_len());
         }
 
         if !self.seq_to_transmit() && repr.segment_len() > 0 {
             // If we've transmitted all data we could (and there was something at all,
             // data or flag, to transmit, not just an ACK), wind up the retransmit timer.
-            self.timer.set_for_retransmit(timestamp, self.rtte.retransmission_timeout());
+            self.timer.set_for_retransmit(cx.now, self.rtte.retransmission_timeout());
         }
 
         if self.state == State::Closed {
@@ -1953,7 +1952,7 @@ impl<'a> TcpSocket<'a> {
     }
 
     #[allow(clippy::if_same_then_else)]
-    pub(crate) fn poll_at(&self) -> PollAt {
+    pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
         // The logic here mirrors the beginning of dispatch() closely.
         if !self.remote_endpoint.is_specified() {
             // No one to talk to, nothing to transmit.
@@ -2059,9 +2058,9 @@ mod test {
     };
 
     #[cfg(feature = "proto-ipv6")]
-    const BASE_MSS: u16 = 1460;
+    const BASE_MSS: u16 = 1440;
     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
-    const BASE_MSS: u16 = 1480;
+    const BASE_MSS: u16 = 1460;
 
     // =========================================================================================//
     // Helper functions
@@ -2079,7 +2078,10 @@ mod test {
         net_trace!("send: {}", repr);
 
         assert!(socket.accepts(&ip_repr, repr));
-        match socket.process(timestamp, &ip_repr, repr) {
+
+        let mut cx = Context::DUMMY.clone();
+        cx.now = timestamp;
+        match socket.process(&cx, &ip_repr, repr) {
             Ok(Some((_ip_repr, repr))) => {
                 net_trace!("recv: {}", repr);
                 Ok(Some(repr))
@@ -2091,8 +2093,9 @@ mod test {
 
     fn recv<F>(socket: &mut TcpSocket, timestamp: Instant, mut f: F)
             where F: FnMut(Result<TcpRepr>) {
-        let mtu = 1520;
-        let result = socket.dispatch(timestamp, mtu, |(ip_repr, tcp_repr)| {
+        let mut cx = Context::DUMMY.clone();
+        cx.now = timestamp;
+        let result = socket.dispatch(&cx, |(ip_repr, tcp_repr)| {
             let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap();
 
             assert_eq!(ip_repr.protocol(), IpProtocol::Tcp);
@@ -4811,7 +4814,7 @@ mod test {
     fn test_listen_timeout() {
         let mut s = socket_listen();
         s.set_timeout(Some(Duration::from_millis(100)));
-        assert_eq!(s.poll_at(), PollAt::Ingress);
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Ingress);
     }
 
     #[test]
@@ -4830,7 +4833,7 @@ mod test {
             ..RECV_TEMPL
         }));
         assert_eq!(s.state, State::SynSent);
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(250)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(250)));
         recv!(s, time 250, Ok(TcpRepr {
             control:    TcpControl::Rst,
             seq_number: LOCAL_SEQ + 1,
@@ -4846,23 +4849,23 @@ mod test {
         let mut s = socket_established();
         s.set_timeout(Some(Duration::from_millis(1000)));
         recv!(s, time 250, Err(Error::Exhausted));
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(1250)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(1250)));
         s.send_slice(b"abcdef").unwrap();
-        assert_eq!(s.poll_at(), PollAt::Now);
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Now);
         recv!(s, time 255, Ok(TcpRepr {
             seq_number: LOCAL_SEQ + 1,
             ack_number: Some(REMOTE_SEQ + 1),
             payload:    &b"abcdef"[..],
             ..RECV_TEMPL
         }));
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(955)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(955)));
         recv!(s, time 955, Ok(TcpRepr {
             seq_number: LOCAL_SEQ + 1,
             ack_number: Some(REMOTE_SEQ + 1),
             payload:    &b"abcdef"[..],
             ..RECV_TEMPL
         }));
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(1255)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(1255)));
         recv!(s, time 1255, Ok(TcpRepr {
             control:    TcpControl::Rst,
             seq_number: LOCAL_SEQ + 1 + 6,
@@ -4884,13 +4887,13 @@ mod test {
             ..RECV_TEMPL
         }));
         recv!(s, time 100, Err(Error::Exhausted));
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(150)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(150)));
         send!(s, time 105, TcpRepr {
             seq_number: REMOTE_SEQ + 1,
             ack_number: Some(LOCAL_SEQ + 1),
             ..SEND_TEMPL
         });
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(155)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(155)));
         recv!(s, time 155, Ok(TcpRepr {
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
@@ -4898,7 +4901,7 @@ mod test {
             ..RECV_TEMPL
         }));
         recv!(s, time 155, Err(Error::Exhausted));
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(205)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(205)));
         recv!(s, time 200, Err(Error::Exhausted));
         recv!(s, time 205, Ok(TcpRepr {
             control:    TcpControl::Rst,
@@ -4954,14 +4957,14 @@ mod test {
         s.set_timeout(Some(Duration::from_millis(200)));
         s.remote_last_ts = Some(Instant::from_millis(100));
         s.abort();
-        assert_eq!(s.poll_at(), PollAt::Now);
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Now);
         recv!(s, time 100, Ok(TcpRepr {
             control:    TcpControl::Rst,
             seq_number: LOCAL_SEQ + 1,
             ack_number: Some(REMOTE_SEQ + 1),
             ..RECV_TEMPL
         }));
-        assert_eq!(s.poll_at(), PollAt::Ingress);
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Ingress);
     }
 
     // =========================================================================================//
@@ -4988,7 +4991,7 @@ mod test {
         s.set_keep_alive(Some(Duration::from_millis(100)));
 
         // drain the forced keep-alive packet
-        assert_eq!(s.poll_at(), PollAt::Now);
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Now);
         recv!(s, time 0, Ok(TcpRepr {
             seq_number: LOCAL_SEQ,
             ack_number: Some(REMOTE_SEQ + 1),
@@ -4996,7 +4999,7 @@ mod test {
             ..RECV_TEMPL
         }));
 
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(100)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(100)));
         recv!(s, time 95, Err(Error::Exhausted));
         recv!(s, time 100, Ok(TcpRepr {
             seq_number: LOCAL_SEQ,
@@ -5005,7 +5008,7 @@ mod test {
             ..RECV_TEMPL
         }));
 
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(200)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(200)));
         recv!(s, time 195, Err(Error::Exhausted));
         recv!(s, time 200, Ok(TcpRepr {
             seq_number: LOCAL_SEQ,
@@ -5019,7 +5022,7 @@ mod test {
             ack_number: Some(LOCAL_SEQ + 1),
             ..SEND_TEMPL
         });
-        assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(350)));
+        assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Time(Instant::from_millis(350)));
         recv!(s, time 345, Err(Error::Exhausted));
         recv!(s, time 350, Ok(TcpRepr {
             seq_number: LOCAL_SEQ,
@@ -5036,10 +5039,9 @@ mod test {
     #[test]
     fn test_set_hop_limit() {
         let mut s = socket_syn_received();
-        let mtu = 1520;
 
         s.set_hop_limit(Some(0x2a));
-        assert_eq!(s.dispatch(Instant::from_millis(0), mtu, |(ip_repr, _)| {
+        assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
             assert_eq!(ip_repr.hop_limit(), 0x2a);
             Ok(())
         }), Ok(()));

+ 14 - 14
src/socket/udp.rs

@@ -3,7 +3,7 @@ use core::cmp::min;
 use core::task::Waker;
 
 use crate::{Error, Result};
-use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
+use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
 use crate::storage::{PacketBuffer, PacketMetadata};
 use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
 #[cfg(feature = "async")]
@@ -293,7 +293,7 @@ impl<'a> UdpSocket<'a> {
         true
     }
 
-    pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
+    pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
         debug_assert!(self.accepts(ip_repr, repr));
 
         let size = payload.len();
@@ -311,7 +311,7 @@ impl<'a> UdpSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn dispatch<F>(&mut self, emit: F) -> Result<()>
+    pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
             where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> {
         let handle    = self.handle();
         let endpoint  = self.endpoint;
@@ -342,7 +342,7 @@ impl<'a> UdpSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn poll_at(&self) -> PollAt {
+    pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
         if self.tx_buffer.is_empty() {
             PollAt::Ingress
         } else {
@@ -465,14 +465,14 @@ mod test {
         assert_eq!(socket.bind(LOCAL_END), Ok(()));
 
         assert!(socket.can_send());
-        assert_eq!(socket.dispatch(|_| unreachable!()),
+        assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
                    Err(Error::Exhausted));
 
         assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
         assert_eq!(socket.send_slice(b"123456", REMOTE_END), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, udp_repr, payload)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
             assert_eq!(ip_repr, LOCAL_IP_REPR);
             assert_eq!(udp_repr, LOCAL_UDP_REPR);
             assert_eq!(payload, PAYLOAD);
@@ -480,7 +480,7 @@ mod test {
         }), Err(Error::Unaddressable));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(|(ip_repr, udp_repr, payload)| {
+        assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
             assert_eq!(ip_repr, LOCAL_IP_REPR);
             assert_eq!(udp_repr, LOCAL_UDP_REPR);
             assert_eq!(payload, PAYLOAD);
@@ -498,12 +498,12 @@ mod test {
         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, PAYLOAD),
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
                    Ok(()));
         assert!(socket.can_recv());
 
         assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
-        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
                    Err(Error::Exhausted));
         assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
         assert!(!socket.can_recv());
@@ -516,7 +516,7 @@ mod test {
 
         assert_eq!(socket.peek(), Err(Error::Exhausted));
 
-        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
                    Ok(()));
         assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
         assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
@@ -529,7 +529,7 @@ mod test {
         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, PAYLOAD),
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
                    Ok(()));
 
         let mut slice = [0; 4];
@@ -542,7 +542,7 @@ mod test {
         let mut socket = socket(buffer(1), buffer(0));
         assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
 
-        assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
                    Ok(()));
 
         let mut slice = [0; 4];
@@ -560,7 +560,7 @@ mod test {
 
         s.set_hop_limit(Some(0x2a));
         assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
-        assert_eq!(s.dispatch(|(ip_repr, _, _)| {
+        assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
             assert_eq!(ip_repr, IpRepr::Unspecified{
                 src_addr: MOCK_IP_ADDR_1,
                 dst_addr: MOCK_IP_ADDR_2,
@@ -637,7 +637,7 @@ mod test {
             src_port: REMOTE_PORT,
             dst_port: LOCAL_PORT,
         };
-        assert_eq!(socket.process(&remote_ip_repr(), &repr, &[]), Ok(()));
+        assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), Ok(()));
         assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
     }