From d88a469c4f7929b8bc5e50dddd7d89f100537a6d Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 6 Oct 2019 22:02:40 +0100 Subject: [PATCH 001/566] dhcpv4: Instead of configuring the ifaces ip address when a dhcp server offers and ip, we now use the requested_ip option, based on the offered ip (yiaddr). The user now only has to configure the iface once the dhcp transaction has completed (Ack'd). --- src/dhcp/clientv4.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 361e899c3..feecd1bd7 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -34,6 +34,7 @@ struct RequestState { retry: u16, endpoint_ip: Ipv4Address, server_identifier: Ipv4Address, + requested_ip: Ipv4Address, } #[derive(Debug)] @@ -118,8 +119,7 @@ impl Client { /// DHCP requests when timeouts are ready. /// /// Applying the obtained network configuration is left to the - /// user. You must configure the new IPv4 address from the - /// returned `Config`. Otherwise, DHCP will not work. + /// user. /// /// A Config can be returned from any valid DHCP reply. The client /// performs no bookkeeping on configuration or their changes. @@ -198,9 +198,8 @@ impl Client { }; net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); - let config = if (dhcp_repr.message_type == DhcpMessageType::Offer || - dhcp_repr.message_type == DhcpMessageType::Ack) && - dhcp_repr.your_ip != Ipv4Address::UNSPECIFIED { + // once we recieve the ack, we can pass the config to the user + let config = if dhcp_repr.message_type == DhcpMessageType::Ack { let address = dhcp_repr.subnet_mask .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len()) .map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len)); @@ -221,6 +220,7 @@ impl Client { retry: 0, endpoint_ip: *src_ip, server_identifier, + requested_ip: dhcp_repr.your_ip // use the offered ip }; Some(ClientState::Requesting(r_state)) } @@ -321,15 +321,9 @@ impl Client { addr: Ipv4Address::BROADCAST.into(), port: UDP_SERVER_PORT, }; - let requested_ip = match iface.ipv4_addr() { - Some(addr) if !addr.is_unspecified() => - Some(addr), - _ => - None, - }; dhcp_repr.message_type = DhcpMessageType::Request; dhcp_repr.broadcast = false; - dhcp_repr.requested_ip = requested_ip; + dhcp_repr.requested_ip = Some(r_state.requested_ip); dhcp_repr.server_identifier = Some(r_state.server_identifier); dhcp_repr.parameter_request_list = Some(PARAMETER_REQUEST_LIST); net_trace!("DHCP send request to {} = {:?}", endpoint, dhcp_repr); From 5df2afb67f2f9235e9218989fae62c8393daed7f Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 6 Jan 2020 09:43:56 +0000 Subject: [PATCH 002/566] s/recieve/receive/ --- src/dhcp/clientv4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index feecd1bd7..9c905195a 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -198,7 +198,7 @@ impl Client { }; net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); - // once we recieve the ack, we can pass the config to the user + // once we receive the ack, we can pass the config to the user let config = if dhcp_repr.message_type == DhcpMessageType::Ack { let address = dhcp_repr.subnet_mask .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len()) From 09f14c9df91943998e16c12568b0b2a0eee46c97 Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Mon, 10 Aug 2020 22:06:06 -0300 Subject: [PATCH 003/566] Allow for ARP retry during egress --- src/iface/ethernet.rs | 2 +- src/socket/meta.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index f879efef9..a2865ffcd 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -578,7 +578,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> let mut emitted_any = false; for mut socket in sockets.iter_mut() { - if !socket.meta_mut().egress_permitted(|ip_addr| + if !socket.meta_mut().egress_permitted(timestamp, |ip_addr| self.inner.has_neighbor(&ip_addr, timestamp)) { continue } diff --git a/src/socket/meta.rs b/src/socket/meta.rs index 5ec9d74ad..e57ec3033 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -58,18 +58,21 @@ impl Meta { } } - pub(crate) fn egress_permitted(&mut self, has_neighbor: F) -> bool + pub(crate) fn egress_permitted(&mut self, timestamp: Instant, has_neighbor: F) -> bool where F: Fn(IpAddress) -> bool { match self.neighbor_state { NeighborState::Active => true, - NeighborState::Waiting { neighbor, .. } => { + NeighborState::Waiting { neighbor, silent_until } => { if has_neighbor(neighbor) { net_trace!("{}: neighbor {} discovered, unsilencing", self.handle, neighbor); self.neighbor_state = NeighborState::Active; true + } else if timestamp > silent_until { + net_trace!("{}: Retrying egress for neighbor {}", self.handle, neighbor); + true } else { false } From eba8d0eef3677a708b4584758658eb49b46e33a3 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 11 Aug 2020 16:26:53 +0000 Subject: [PATCH 004/566] Update src/socket/meta.rs --- src/socket/meta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/meta.rs b/src/socket/meta.rs index e57ec3033..9738be112 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -71,7 +71,7 @@ impl Meta { self.neighbor_state = NeighborState::Active; true } else if timestamp > silent_until { - net_trace!("{}: Retrying egress for neighbor {}", self.handle, neighbor); + net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor); true } else { false From 3bd6bc03a159b42ec14ba53c2e495d122979e801 Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Tue, 11 Aug 2020 21:37:08 -0300 Subject: [PATCH 005/566] Only limit the neighbor cache rate after sending a request packet Prior to this change, the neighbor cache would get limited even when the device failed to process our arp packet, which could put the socket in the waiting state in the next poll. This change only limits the cache rate if the device successfully consumed our packet. --- src/iface/ethernet.rs | 9 ++++----- src/iface/neighbor.rs | 9 +++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index a2865ffcd..392170af2 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1618,8 +1618,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) })?; - - Err(Error::Unaddressable) } #[cfg(feature = "proto-ipv6")] @@ -1646,12 +1644,13 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { solicit.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(), &mut Icmpv6Packet::new_unchecked(payload), &checksum_caps); })?; - - Err(Error::Unaddressable) } - _ => Err(Error::Unaddressable) + _ => () } + // The request got dispatched, limit the rate on the cache. + self.neighbor_cache.limit_rate(timestamp); + Err(Error::Unaddressable) } fn dispatch_ip(&mut self, tx_token: Tx, timestamp: Instant, diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index dd042b991..95ca7acff 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -172,18 +172,21 @@ impl<'a> Cache<'a> { None } - pub(crate) fn lookup(&mut self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { + pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { match self.lookup_pure(protocol_addr, timestamp) { Some(hardware_addr) => Answer::Found(hardware_addr), None if timestamp < self.silent_until => Answer::RateLimited, None => { - self.silent_until = timestamp + Self::SILENT_TIME; Answer::NotFound } } } + + pub(crate) fn limit_rate(&mut self, timestamp: Instant) { + self.silent_until = timestamp + Self::SILENT_TIME; + } } #[cfg(test)] @@ -273,6 +276,8 @@ mod test { let mut cache = Cache::new(&mut cache_storage[..]); assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound); + + cache.limit_rate(Instant::from_millis(0)); assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited); assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound); } From b45741675654d6fa8eb505a200c58eb729b5617e Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Thu, 13 Aug 2020 17:01:37 -0300 Subject: [PATCH 006/566] Merge lookup and lookup_pure methods --- src/iface/ethernet.rs | 8 +++--- src/iface/neighbor.rs | 67 ++++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 392170af2..e7e54d83a 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -878,7 +878,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { // Fill the neighbor cache from IP header of unicast frames. let ip_addr = IpAddress::Ipv6(ipv6_repr.src_addr); if self.in_same_network(&ip_addr) && - self.neighbor_cache.lookup_pure(&ip_addr, timestamp).is_none() { + !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp); } } @@ -1143,7 +1143,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { if flags.contains(NdiscNeighborFlags::OVERRIDE) { self.neighbor_cache.fill(ip_addr, lladdr, timestamp) } else { - if self.neighbor_cache.lookup_pure(&ip_addr, timestamp).is_none() { + if !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { self.neighbor_cache.fill(ip_addr, lladdr, timestamp) } } @@ -1543,8 +1543,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match self.route(addr, timestamp) { Ok(routed_addr) => { self.neighbor_cache - .lookup_pure(&routed_addr, timestamp) - .is_some() + .lookup(&routed_addr, timestamp) + .found() } Err(_) => false } diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 95ca7acff..71a64405c 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -28,6 +28,16 @@ pub(crate) enum Answer { RateLimited } +impl Answer { + /// Returns whether a valid address was found. + pub(crate) fn found(&self) -> bool { + match self { + Answer::Found(_) => true, + _ => false, + } + } +} + /// A neighbor cache backed by a map. /// /// # Examples @@ -154,33 +164,24 @@ impl<'a> Cache<'a> { } } - pub(crate) fn lookup_pure(&self, protocol_addr: &IpAddress, timestamp: Instant) -> - Option { + pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { if protocol_addr.is_broadcast() { - return Some(EthernetAddress::BROADCAST) + return Answer::Found(EthernetAddress::BROADCAST); } match self.storage.get(protocol_addr) { Some(&Neighbor { expires_at, hardware_addr }) => { if timestamp < expires_at { - return Some(hardware_addr) + return Answer::Found(hardware_addr) } } None => () } - None - } - - pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { - match self.lookup_pure(protocol_addr, timestamp) { - Some(hardware_addr) => - Answer::Found(hardware_addr), - None if timestamp < self.silent_until => - Answer::RateLimited, - None => { - Answer::NotFound - } + if timestamp < self.silent_until { + Answer::RateLimited + } else { + Answer::NotFound } } @@ -206,17 +207,17 @@ mod test { let mut cache_storage = [Default::default(); 3]; let mut cache = Cache::new(&mut cache_storage[..]); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), None); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2), - None); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), + false); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(0)), None); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); } #[test] @@ -225,9 +226,9 @@ mod test { let mut cache = Cache::new(&mut cache_storage[..]); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2), - None); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), + false); } #[test] @@ -236,9 +237,9 @@ mod test { let mut cache = Cache::new(&mut cache_storage[..]); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_A)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Some(HADDR_B)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B)); } #[test] @@ -251,7 +252,7 @@ mod test { cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2); assert_eq!(cache.storage.len(), 1); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Some(HADDR_C)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C)); } #[test] @@ -262,12 +263,12 @@ mod test { cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100)); cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Some(HADDR_B)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), None); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false); cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300)); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), None); - assert_eq!(cache.lookup_pure(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Some(HADDR_D)); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false); + assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D)); } #[test] From 39c41afb68ec35d35ee1c27b8c09e79b06317e75 Mon Sep 17 00:00:00 2001 From: Thales Fragoso Date: Thu, 13 Aug 2020 17:09:10 -0300 Subject: [PATCH 007/566] Match silent_until behavior of NeighborState and Cache --- src/socket/meta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/meta.rs b/src/socket/meta.rs index 9738be112..28537bfbc 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -70,7 +70,7 @@ impl Meta { self.handle, neighbor); self.neighbor_state = NeighborState::Active; true - } else if timestamp > silent_until { + } else if timestamp >= silent_until { net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor); true } else { From 7948edc020a63c1db45b7536679072ca21f6183b Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 17 Aug 2020 05:19:09 +0000 Subject: [PATCH 008/566] Fix test that relied on matching panic messages. The panic message for copy_from_slice changed in Rust 1.47. --- src/wire/ipv6.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 065b239f6..89b1196a3 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -887,7 +887,7 @@ mod test { } #[test] - #[should_panic(expected = "destination and source slices have different lengths")] + #[should_panic(expected = "length")] fn test_from_bytes_too_long() { let _ = Address::from_bytes(&[0u8; 15]); } From bdfa44270e9c59b3095b555cdf14601f7dc27794 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 17 Aug 2020 07:44:58 +0200 Subject: [PATCH 009/566] Return RST to unexpected ACK in SYN-SENT state. Before this commit, the socket got stuck in an unusable state. --- src/socket/tcp.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f6c0f97a4..8cdbf2123 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -995,6 +995,15 @@ impl<'a> TcpSocket<'a> { self.meta.handle, self.local_endpoint, self.remote_endpoint); return Err(Error::Dropped) } + // Any ACK in the SYN-SENT state must have the SYN flag set. + (State::SynSent, &TcpRepr { + control: TcpControl::None, ack_number: Some(_), .. + }) => { + net_debug!("{}:{}:{}: expecting a SYN|ACK", + self.meta.handle, self.local_endpoint, self.remote_endpoint); + self.abort(); + return Err(Error::Dropped) + } // Every acknowledgement must be for transmitted but unacknowledged data. (_, &TcpRepr { ack_number: Some(ack_number), .. }) => { let unacknowledged = self.tx_buffer.len() + control_len; @@ -2490,6 +2499,17 @@ mod test { assert_eq!(s.state, State::SynSent); } + #[test] + fn test_syn_sent_bad_ack() { + let mut s = socket_syn_sent(); + send!(s, TcpRepr { + control: TcpControl::None, + ack_number: Some(TcpSeqNumber(1)), + ..SEND_TEMPL + }, Err(Error::Dropped)); + assert_eq!(s.state, State::Closed); + } + #[test] fn test_syn_sent_close() { let mut s = socket(); From 77da1e0a37ed1d56c523efafa62b9e2c15091713 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Oct 2020 20:32:47 +0200 Subject: [PATCH 010/566] igmp: centisecs are really decisecs --- src/wire/igmp.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 49440a000..abf9354eb 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -289,22 +289,22 @@ impl Repr { fn max_resp_code_to_duration(value: u8) -> Duration { let value: u64 = value.into(); - let centisecs = if value < 128 { + let decisecs = if value < 128 { value } else { let mant = value & 0xF; let exp = (value >> 4) & 0x7; (mant | 0x10) << (exp + 3) }; - Duration::from_millis(centisecs * 100) + Duration::from_millis(decisecs * 100) } fn duration_to_max_resp_code(duration: Duration) -> u8 { - let centisecs = duration.total_millis() / 100; - if centisecs < 128 { - centisecs as u8 - } else if centisecs < 31744 { - let mut mant = centisecs >> 3; + let decisecs = duration.total_millis() / 100; + if decisecs < 128 { + decisecs as u8 + } else if decisecs < 31744 { + let mut mant = decisecs >> 3; let mut exp = 0u8; while mant > 0x1F && exp < 0x8 { mant >>= 1; From 1268fb3d8ddeb1814a6b3a49f1647d1bbf3debd8 Mon Sep 17 00:00:00 2001 From: Sam Jones Date: Wed, 19 Aug 2020 19:17:20 +0100 Subject: [PATCH 011/566] Changed Dhcpv4Client to use device checksum capabilities --- src/dhcp/clientv4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 93175262c..e7f01a699 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -130,7 +130,7 @@ impl Client { where DeviceT: for<'d> Device<'d>, { - let checksum_caps = ChecksumCapabilities::default(); // ? + let checksum_caps = iface.device().capabilities().checksum; let mut raw_socket = sockets.get::(self.raw_handle); // Process incoming From b933bc361fb5dc4590871d11382b06c238660c9f Mon Sep 17 00:00:00 2001 From: mustermeiszer Date: Fri, 23 Oct 2020 14:12:20 +0200 Subject: [PATCH 012/566] Fixes unused MTU settings at TcpSocket dispatch This commit fixes a small bug, where the TcpSocket computed the maximum size per payload in a tx_buffer without taking into account the MTU defined by the underlying network device. --- src/socket/tcp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8cdbf2123..b6f6bef0c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1558,7 +1558,8 @@ impl<'a> TcpSocket<'a> { // Extract as much data as the remote side can receive in this packet // from the transmit buffer. let offset = self.remote_last_seq - self.local_seq_no; - let size = cmp::min(self.remote_win_len, self.remote_mss); + let size = cmp::min(cmp::min(self.remote_win_len, self.remote_mss), + caps.max_transmission_unit); repr.payload = self.tx_buffer.get_allocated(offset, size); // If we've sent everything we had in the buffer, follow it with the PSH or FIN // flags, depending on whether the transmit half of the connection is open. From b7678619980d0179fd8cc5de92ab4d2ff868bd9c Mon Sep 17 00:00:00 2001 From: mustermeiszer Date: Fri, 23 Oct 2020 15:13:18 +0200 Subject: [PATCH 013/566] Taking into account TCP and IP header The MTU consists of TCP header, IP header and the payload. In the former fix, this was not taken into account. --- src/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index b6f6bef0c..b4a5f6692 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1559,7 +1559,7 @@ impl<'a> TcpSocket<'a> { // from the transmit buffer. let offset = self.remote_last_seq - self.local_seq_no; let size = cmp::min(cmp::min(self.remote_win_len, self.remote_mss), - caps.max_transmission_unit); + caps.max_transmission_unit - ip_repr.buffer_len() - repr.mss_header_len()); repr.payload = self.tx_buffer.get_allocated(offset, size); // If we've sent everything we had in the buffer, follow it with the PSH or FIN // flags, depending on whether the transmit half of the connection is open. From 65e58931532a69cb0afc761660016e6133458b32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Dec 2020 16:26:41 +0100 Subject: [PATCH 014/566] tcp: allow sending ACKs in FinWait2 state. --- src/socket/tcp.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index b4a5f6692..ddf845efa 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1574,8 +1574,9 @@ impl<'a> TcpSocket<'a> { } } - // We do not transmit anything in the FIN-WAIT-2 state. - State::FinWait2 => return Err(Error::Exhausted), + // We do not transmit data in the FIN-WAIT-2 state, but we may transmit + // ACKs for incoming data. + State::FinWait2 => {} // We do not transmit data or control flags in the CLOSING or TIME-WAIT states, // but we may retransmit an ACK. @@ -3093,6 +3094,11 @@ mod test { assert_eq!(data, b"abc"); (3, ()) }).unwrap(); + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + ..RECV_TEMPL + }]); } #[test] From 2080152b25ea3adba930aabb21d39fce178115a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Dec 2020 16:06:23 +0100 Subject: [PATCH 015/566] tcp: don't send data outside the remote window --- src/socket/tcp.rs | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index ddf845efa..fd517f1cb 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -230,8 +230,8 @@ pub struct TcpSocket<'a> { /// The sending window scaling factor advertised to remotes which support RFC 1323. /// It is zero if the window <= 64KiB and/or the remote does not support it. remote_win_shift: u8, - /// The speculative remote window size. - /// I.e. the actual remote window size minus the count of in-flight octets. + /// The remote window size, relative to local_seq_no + /// I.e. we're allowed to send octets until local_seq_no+remote_win_len remote_win_len: usize, /// The receive window scaling factor for remotes which support RFC 1323, None if unsupported. remote_win_scale: Option, @@ -1410,11 +1410,11 @@ impl<'a> TcpSocket<'a> { _ => control = TcpControl::None } - if self.remote_win_len > 0 { - self.remote_last_seq < self.local_seq_no + self.tx_buffer.len() + control.len() - } else { - false - } + self.remote_last_seq < + self.local_seq_no + core::cmp::min( + self.remote_win_len, + self.tx_buffer.len() + ) + control.len() } fn ack_to_transmit(&self) -> bool { @@ -1558,7 +1558,8 @@ impl<'a> TcpSocket<'a> { // Extract as much data as the remote side can receive in this packet // from the transmit buffer. let offset = self.remote_last_seq - self.local_seq_no; - let size = cmp::min(cmp::min(self.remote_win_len, self.remote_mss), + let win_limit = self.local_seq_no + self.remote_win_len - self.remote_last_seq; + let size = cmp::min(cmp::min(win_limit, self.remote_mss), caps.max_transmission_unit - ip_repr.buffer_len() - repr.mss_header_len()); repr.payload = self.tx_buffer.get_allocated(offset, size); // If we've sent everything we had in the buffer, follow it with the PSH or FIN @@ -2755,11 +2756,6 @@ mod test { ack_number: Some(REMOTE_SEQ + 1), payload: &data[0..16], ..RECV_TEMPL - }, TcpRepr { - seq_number: LOCAL_SEQ + 1 + 16, - ack_number: Some(REMOTE_SEQ + 1), - payload: &data[16..32], - ..RECV_TEMPL }]); } @@ -3525,7 +3521,7 @@ mod test { #[test] fn test_data_retransmit_bursts() { let mut s = socket_established(); - s.remote_win_len = 6; + s.remote_mss = 6; s.send_slice(b"abcdef012345").unwrap(); recv!(s, time 0, Ok(TcpRepr { @@ -3535,7 +3531,6 @@ mod test { payload: &b"abcdef"[..], ..RECV_TEMPL }), exact); - s.remote_win_len = 6; recv!(s, time 0, Ok(TcpRepr { control: TcpControl::Psh, seq_number: LOCAL_SEQ + 1 + 6, @@ -3543,7 +3538,6 @@ mod test { payload: &b"012345"[..], ..RECV_TEMPL }), exact); - s.remote_win_len = 6; recv!(s, time 0, Err(Error::Exhausted)); recv!(s, time 50, Err(Error::Exhausted)); @@ -3555,7 +3549,6 @@ mod test { payload: &b"abcdef"[..], ..RECV_TEMPL }), exact); - s.remote_win_len = 6; recv!(s, time 150, Ok(TcpRepr { control: TcpControl::Psh, seq_number: LOCAL_SEQ + 1 + 6, @@ -3563,7 +3556,6 @@ mod test { payload: &b"012345"[..], ..RECV_TEMPL }), exact); - s.remote_win_len = 6; recv!(s, time 200, Err(Error::Exhausted)); } @@ -3791,12 +3783,12 @@ mod test { #[test] fn test_fast_retransmit_after_triple_duplicate_ack() { let mut s = socket_established(); + s.remote_mss = 6; // Normal ACK of previously recived segment send!(s, time 0, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); @@ -3833,14 +3825,12 @@ mod test { send!(s, time 1050, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); // Second duplicate ACK send!(s, time 1055, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); // Third duplicate ACK @@ -3848,7 +3838,6 @@ mod test { send!(s, time 1060, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); @@ -3951,12 +3940,12 @@ mod test { #[test] fn test_fast_retransmit_duplicate_detection() { let mut s = socket_established(); + s.remote_mss = 6; // Normal ACK of previously recived segment send!(s, time 0, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); @@ -3964,7 +3953,6 @@ mod test { send!(s, time 0, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); @@ -3972,7 +3960,7 @@ mod test { "duplicate ACK counter is set but wound not transmit data"); // Send a long string of text divided into several packets - // because of previously recieved "window_len" + // because of small remote_mss s.send_slice(b"xxxxxxyyyyyywwwwwwzzzzzz").unwrap(); // This packet is reordered in network @@ -4005,21 +3993,18 @@ mod test { send!(s, time 1050, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); // Second duplicate ACK send!(s, time 1055, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - window_len: 6, ..SEND_TEMPL }); // Reordered packet arrives which should reset duplicate ACK count send!(s, time 1060, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1 + (6 * 3)), - window_len: 6, ..SEND_TEMPL }); @@ -4174,7 +4159,7 @@ mod test { #[test] fn test_psh_transmit() { let mut s = socket_established(); - s.remote_win_len = 6; + s.remote_mss = 6; s.send_slice(b"abcdef").unwrap(); s.send_slice(b"123456").unwrap(); recv!(s, time 0, Ok(TcpRepr { From aafc5e79cae7e3188102ca1be0a80a7c047f2108 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Tue, 22 Dec 2020 12:11:10 +0000 Subject: [PATCH 016/566] Add Github Actions for CI --- .github/workflows/clippy.yml | 25 ++++++++++++ .github/workflows/fuzz.yml | 21 ++++++++++ .github/workflows/test.yml | 79 ++++++++++++++++++++++++++++++++++++ README.md | 2 +- 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/clippy.yml create mode 100644 .github/workflows/fuzz.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 000000000..d55d697f1 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,25 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request_target: + +name: Clippy check +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + if: github.event_name == 'pull_request_target' + with: + ref: refs/pull/${{ github.event.number }}/head + - uses: actions/checkout@v2 + if: github.event_name != 'pull_request_target' + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: clippy + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml new file mode 100644 index 000000000..c09bb5d85 --- /dev/null +++ b/.github/workflows/fuzz.yml @@ -0,0 +1,21 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Fuzz + +jobs: + fuzz: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + - name: Install cargo-fuzz + run: cargo install cargo-fuzz + - name: Fuzz + run: cargo fuzz run packet_parser -- -max_len=1536 -max_total_time=30 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..a82118792 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,79 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Test + +jobs: + test: + runs-on: ubuntu-20.04 + continue-on-error: ${{ matrix.rust == 'nightly' }} + strategy: + matrix: + # Test on stable, MSRV 1.36, and nightly. + # Failure is permitted on nightly. + rust: + - stable + - 1.36.0 + - nightly + + features: + # Test default features. + - default + + # Test features chosen to be as orthogonal as possible. + - std ethernet phy-raw_socket proto-ipv6 socket-udp + - std ethernet phy-tap_interface proto-ipv6 socket-udp + - std ethernet proto-ipv4 proto-igmp socket-raw + - std ethernet proto-ipv4 socket-udp socket-tcp + - std ethernet proto-ipv4 proto-dhcpv4 socket-udp + - std ethernet proto-ipv6 socket-udp + - std ethernet proto-ipv6 socket-tcp + - std ethernet proto-ipv4 socket-icmp socket-tcp + - std ethernet proto-ipv6 socket-icmp socket-tcp + + # Test features chosen to be as aggressive as possible. + - std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + + include: + # Test alloc feature which requires nightly. + - rust: nightly + features: alloc 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 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - name: Run Tests + run: cargo test --no-default-features --features "${{ matrix.features }}" + + check: + runs-on: ubuntu-20.04 + continue-on-error: ${{ matrix.rust == 'nightly' }} + strategy: + matrix: + # Test on stable, MSRV 1.36, and nightly. + # Failure is permitted on nightly. + rust: + - stable + - 1.36.0 + - nightly + + features: + # These feature sets cannot run tests, so we only check they build. + - ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - name: Check + run: cargo check --no-default-features --features "${{ matrix.features }}" diff --git a/README.md b/README.md index bc2cecc8b..a240a1594 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.28 and later. +and compiles on stable Rust 1.36 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. From 263c4d1e44e6463e9a89c2420ae92927e86fa440 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Tue, 22 Dec 2020 15:31:16 +0000 Subject: [PATCH 017/566] Remove Travis configuration --- .travis.yml | 69 ----------------------------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1490e63c9..000000000 --- a/.travis.yml +++ /dev/null @@ -1,69 +0,0 @@ -language: rust -matrix: - include: - ### Litmus check that we work on stable/beta - # (we don't, not until slice_rotate lands) - # - rust: stable - # env: FEATURES='default' MODE='test' - # - rust: beta - # env: FEATURES='default' MODE='test' - ### Test default configurations - - rust: nightly - env: FEATURES='default' MODE='test' - ### Test select feature permutations, chosen to be as orthogonal as possible - - rust: nightly - env: FEATURES='std ethernet phy-raw_socket proto-ipv6 socket-udp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet phy-tap_interface proto-ipv6 socket-udp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv4 proto-igmp socket-raw' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv4 socket-udp socket-tcp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv4 proto-dhcpv4 socket-udp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv6 socket-udp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv6 socket-tcp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv4 socket-icmp socket-tcp' MODE='test' - - rust: nightly - env: FEATURES='std ethernet proto-ipv6 socket-icmp socket-tcp' MODE='test' - ### Test select feature permutations, chosen to be as aggressive as possible - - rust: nightly - env: FEATURES='ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp std' - MODE='test' - - rust: nightly - env: FEATURES='ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc' - MODE='test' - - rust: nightly - env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc' - MODE='test' - - rust: nightly - env: FEATURES='ethernet proto-ipv4 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp' - MODE='build' - - rust: nightly - env: MODE='fuzz run' ARGS='packet_parser -- -max_len=1536 -max_total_time=30' - - rust: nightly - env: FEATURES='default' MODE='clippy' - - rust: nightly - env: FEATURES='default' MODE='check --bench bench' - - os: osx - rust: nightly - env: FEATURES='default' MODE='build' - allow_failures: - # something's screwy with Travis (as usual) and cargo-fuzz dies with a LeakSanitizer error - # even when it's successful. Keep this in .travis.yml in case it starts working some day. - - rust: nightly - env: MODE='fuzz run' ARGS='packet_parser -- -max_len=1536 -max_total_time=30' - # Clippy sometimes fails to compile and this shouldn't gate merges - - rust: nightly - env: FEATURES='default' MODE='clippy' - # See if the bench actually breaks - - rust: nightly - env: FEATURES='default' MODE='check --bench bench' -before_script: - - if [ "$MODE" == "fuzz run" ]; then cargo install cargo-fuzz; fi - - if [ "$MODE" == "clippy" ]; then cargo install clippy; fi -script: - - cargo $MODE --no-default-features --features "$FEATURES" $ARGS From ce21bf4427669cccbab9712b8f6197d0bac21758 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Dec 2020 01:52:21 +0100 Subject: [PATCH 018/566] Remove .test_like_travis.rb --- .test_like_travis.rb | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100755 .test_like_travis.rb diff --git a/.test_like_travis.rb b/.test_like_travis.rb deleted file mode 100755 index e75d156f1..000000000 --- a/.test_like_travis.rb +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/ruby - -require 'yaml' - -travis_config = YAML.load_file('.travis.yml') -travis_config['matrix']['include'].each do |env| - ENV['RUSTUP_TOOLCHAIN'] = env['rust'] - env['env'].scan(/(\w+)=\'(.+?)\'/) do - ENV[$1] = $2 - end - travis_config['script'].each do |cmd| - $stderr.puts('+ ' + cmd.gsub(/\$(\w+)/) { ENV[$1] }) - system(cmd) - $?.success? or exit 1 - end - env['env'].scan(/(\w+)=\'(.+?)\'/) do - ENV.delete $1 - end -end From 662e8237ecf9ce20f46f94c52e61f3b5889b3e32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Dec 2020 23:57:54 +0100 Subject: [PATCH 019/566] Fix MTU of RawSocket and TapInterface. Linux's MTU is the IP MTU, while smoltcp's is the Ethernet MTU. Therefore we have to add the ethernet header size to it. --- src/phy/sys/raw_socket.rs | 6 +++++- src/phy/sys/tap_interface.rs | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 2b60bd7ce..368f62d1f 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -2,6 +2,7 @@ use std::{mem, io}; use std::os::unix::io::{RawFd, AsRawFd}; use libc; use super::*; +use crate::wire::EthernetFrame; #[derive(Debug)] pub struct RawSocketDesc { @@ -31,7 +32,10 @@ impl RawSocketDesc { } pub fn interface_mtu(&mut self) -> io::Result { - ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize) + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + let ip_mtu = ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?; + Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len()) } pub fn bind_interface(&mut self) -> io::Result<()> { diff --git a/src/phy/sys/tap_interface.rs b/src/phy/sys/tap_interface.rs index f89597ff8..29b1ffa92 100644 --- a/src/phy/sys/tap_interface.rs +++ b/src/phy/sys/tap_interface.rs @@ -2,6 +2,7 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use libc; use super::*; +use crate::wire::EthernetFrame; #[derive(Debug)] pub struct TapInterfaceDesc { @@ -42,11 +43,13 @@ impl TapInterfaceDesc { lower }; - let mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize); + let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize); unsafe { libc::close(lower); } - mtu + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + Ok(ip_mtu? + EthernetFrame::<&[u8]>::header_len()) } pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { From 9943ad38b92933b88e58aa5bedfdd705659d3d4d Mon Sep 17 00:00:00 2001 From: JOE1994 Date: Wed, 19 Aug 2020 09:44:01 -0400 Subject: [PATCH 020/566] add null terminator to c-string passed to libc API --- src/phy/sys/bpf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phy/sys/bpf.rs b/src/phy/sys/bpf.rs index 566941474..3d802424a 100644 --- a/src/phy/sys/bpf.rs +++ b/src/phy/sys/bpf.rs @@ -43,7 +43,7 @@ impl AsRawFd for BpfDevice { fn open_device() -> io::Result { unsafe { for i in 0..256 { - let dev = format!("/dev/bpf{}", i).as_ptr() as *const libc::c_char; + let dev = format!("/dev/bpf{}\0", i).as_ptr() as *const libc::c_char; match libc::open(dev, libc::O_RDWR) { -1 => continue, fd => return Ok(fd), From 3dd35d92c06fb2240fa7768160f0dfaee23df4a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 26 Dec 2020 02:04:39 +0100 Subject: [PATCH 021/566] Enable `proto-dhcpv4` feature by default. Fixes #327 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed21d0d5a..1f8f2e89b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "ethernet", "phy-raw_socket", "phy-tap_interface", - "proto-ipv4", "proto-igmp", "proto-ipv6", + "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp" ] From f20ad0e8deef64688e1d1cc10e46395e69a17619 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 26 Dec 2020 03:04:17 +0100 Subject: [PATCH 022/566] Fix seq_to_transmit incorrectly returning true when a FIN was enqueued. If there's data queued that doesn't fit into the remote window, we can't send the FIN either, so seq_to_transmit should return false. --- src/socket/tcp.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index fd517f1cb..8eaca3c68 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1401,20 +1401,23 @@ impl<'a> TcpSocket<'a> { } fn seq_to_transmit(&self) -> bool { - let control; - match self.state { - State::SynSent | State::SynReceived => - control = TcpControl::Syn, - State::FinWait1 | State::LastAck => - control = TcpControl::Fin, - _ => control = TcpControl::None - } - - self.remote_last_seq < - self.local_seq_no + core::cmp::min( - self.remote_win_len, - self.tx_buffer.len() - ) + control.len() + // We can send data if we have data that: + // - hasn't been sent before + // - fits in the remote window + let can_data = self.remote_last_seq + < self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); + + // Do we have to send a FIN? + let want_fin = matches!(self.state, State::FinWait1 | State::LastAck); + + // Can we actually send the FIN? We can send it if: + // 1. We have unsent data that fits in the remote window. + // 2. We have no unsent data. + // This condition matches only if #2, because #1 is already covered by can_data and we're ORing them. + let can_fin = + want_fin && self.remote_last_seq == self.local_seq_no + self.tx_buffer.len(); + + can_data || can_fin } fn ack_to_transmit(&self) -> bool { From c510a96012813fb453827ffdb3a88e5a63bacfdc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 26 Dec 2020 03:12:48 +0100 Subject: [PATCH 023/566] Don't use matches! macro, for Rust 1.36 support --- src/socket/tcp.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8eaca3c68..86229fd0c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1408,7 +1408,11 @@ impl<'a> TcpSocket<'a> { < self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); // Do we have to send a FIN? - let want_fin = matches!(self.state, State::FinWait1 | State::LastAck); + let want_fin = match self.state { + State::FinWait1 => true, + State::LastAck => true, + _ => false, + }; // Can we actually send the FIN? We can send it if: // 1. We have unsent data that fits in the remote window. From ccf691518604c9bc266ecce8b017f93025aa1c56 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:02:20 -0800 Subject: [PATCH 024/566] Remove explicit calls to as_ref/as_mut These were flagged by `cargo clippy`: warning: this call to `as_ref` does nothing warning: this call to `as_mut` does nothing --- examples/server.rs | 2 +- src/iface/ethernet.rs | 2 +- src/socket/raw.rs | 14 +++++++------- src/wire/ip.rs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/server.rs b/examples/server.rs index 98940cd8e..db4d14b06 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -91,7 +91,7 @@ fn main() { let client = match socket.recv() { Ok((data, endpoint)) => { debug!("udp:6969 recv data: {:?} from {}", - str::from_utf8(data.as_ref()).unwrap(), endpoint); + str::from_utf8(data).unwrap(), endpoint); Some(endpoint) } Err(_) => None diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index e7e54d83a..b80cf97ab 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1510,7 +1510,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { let tx_len = EthernetFrame::<&[u8]>::buffer_len(buffer_len); tx_token.consume(timestamp, tx_len, |tx_buffer| { debug_assert!(tx_buffer.as_ref().len() == tx_len); - let mut frame = EthernetFrame::new_unchecked(tx_buffer.as_mut()); + let mut frame = EthernetFrame::new_unchecked(tx_buffer); frame.set_src_addr(self.ethernet_addr); f(frame); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 336076c7d..15df21148 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -115,7 +115,7 @@ impl<'a, 'b> RawSocket<'a, 'b> { net_trace!("{}:{}:{}: buffer to send {} octets", self.meta.handle, self.ip_version, self.ip_protocol, packet_buf.len()); - Ok(packet_buf.as_mut()) + Ok(packet_buf) } /// Enqueue a packet to send, and fill it from a slice. @@ -165,8 +165,8 @@ impl<'a, 'b> RawSocket<'a, 'b> { 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.as_mut()[..header_len], &checksum_caps); - packet_buf.as_mut()[header_len..].copy_from_slice(payload); + ip_repr.emit(&mut packet_buf[..header_len], &checksum_caps); + packet_buf[header_len..].copy_from_slice(payload); net_trace!("{}:{}:{}: receiving {} octets", self.meta.handle, self.ip_version, self.ip_protocol, @@ -179,10 +179,10 @@ impl<'a, 'b> RawSocket<'a, 'b> { where F: FnOnce((IpRepr, &[u8])) -> Result<()> { fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8], _checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> { - match IpVersion::of_packet(buffer.as_ref())? { + match IpVersion::of_packet(buffer)? { #[cfg(feature = "proto-ipv4")] IpVersion::Ipv4 => { - let mut packet = Ipv4Packet::new_checked(buffer.as_mut())?; + let mut packet = Ipv4Packet::new_checked(buffer)?; if packet.protocol() != protocol { return Err(Error::Unaddressable) } if _checksum_caps.ipv4.tx() { packet.fill_checksum(); @@ -198,7 +198,7 @@ impl<'a, 'b> RawSocket<'a, 'b> { } #[cfg(feature = "proto-ipv6")] IpVersion::Ipv6 => { - let packet = Ipv6Packet::new_checked(buffer.as_mut())?; + let packet = Ipv6Packet::new_checked(buffer)?; if packet.next_header() != protocol { return Err(Error::Unaddressable) } let packet = Ipv6Packet::new_unchecked(&*packet.into_inner()); let ipv6_repr = Ipv6Repr::parse(&packet)?; @@ -213,7 +213,7 @@ impl<'a, 'b> RawSocket<'a, 'b> { 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.as_mut(), &checksum_caps) { + match prepare(ip_protocol, packet_buf, &checksum_caps) { Ok((ip_repr, raw_packet)) => { net_trace!("{}:{}:{}: sending {} octets", handle, ip_version, ip_protocol, diff --git a/src/wire/ip.rs b/src/wire/ip.rs index d29808685..ac5ba67ef 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -879,11 +879,11 @@ pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &m #[cfg(feature = "proto-ipv4")] Protocol::Icmp => { indent.increase(f)?; - Icmpv4Packet::<&[u8]>::pretty_print(&payload.as_ref(), f, indent) + Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent) } Protocol::Udp => { indent.increase(f)?; - match UdpPacket::<&[u8]>::new_checked(payload.as_ref()) { + match UdpPacket::<&[u8]>::new_checked(payload) { Err(err) => write!(f, "{}({})", indent, err), Ok(udp_packet) => { match UdpRepr::parse(&udp_packet, &repr.src_addr(), @@ -901,7 +901,7 @@ pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &m } Protocol::Tcp => { indent.increase(f)?; - match TcpPacket::<&[u8]>::new_checked(payload.as_ref()) { + match TcpPacket::<&[u8]>::new_checked(payload) { Err(err) => write!(f, "{}({})", indent, err), Ok(tcp_packet) => { match TcpRepr::parse(&tcp_packet, &repr.src_addr(), From 113a6c16b80bf465aa8b79e8e7cbe5335a4df56e Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 22:59:24 -0800 Subject: [PATCH 025/566] Use newline variants of write macro These were flagged by `cargo clippy`: warning: using `println!("")` warning: using `write!()` with a format string that ends in a single newline --- src/socket/tcp.rs | 2 +- src/wire/igmp.rs | 4 ++-- src/wire/pretty_print.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index fd517f1cb..7f27215a0 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1891,7 +1891,7 @@ mod test { let _ = log::set_logger(&LOGGER); log::set_max_level(log::LevelFilter::Trace); - println!(""); + println!(); } fn socket() -> TcpSocket<'static> { diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index abf9354eb..94e057210 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -363,8 +363,8 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})\n", indent, err), - Ok(packet) => write!(f, "{}{}\n", indent, packet), + Err(err) => writeln!(f, "{}({})", indent, err), + Ok(packet) => writeln!(f, "{}{}", indent, packet), } } } diff --git a/src/wire/pretty_print.rs b/src/wire/pretty_print.rs index c60c1ff62..6a65f4f4b 100644 --- a/src/wire/pretty_print.rs +++ b/src/wire/pretty_print.rs @@ -48,7 +48,7 @@ impl PrettyIndent { /// Increase indentation level. pub fn increase(&mut self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\n")?; + writeln!(f)?; self.level += 1; Ok(()) } From 5a26227909e5c4580226e5067efe4fcec7b3da05 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 22:42:32 -0800 Subject: [PATCH 026/566] Remove unnecessary returns These were flagged by `cargo clippy`: warning: unneeded `return` statement --- src/socket/set.rs | 2 +- src/wire/ndiscoption.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/set.rs b/src/socket/set.rs index b41a5383c..2fe6f24b5 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -75,7 +75,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { ManagedSlice::Owned(ref mut sockets) => { sockets.push(None); let index = sockets.len() - 1; - return put(index, &mut sockets[index], socket) + put(index, &mut sockets[index], socket) } } } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 51803318b..1c8bcbfad 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -596,7 +596,7 @@ impl> PrettyPrint for NdiscOption { Err(err) => return write!(f, "{}({})", indent, err), Ok(ndisc) => { match Repr::parse(&ndisc) { - Err(_) => return Ok(()), + Err(_) => Ok(()), Ok(repr) => { write!(f, "{}{}", indent, repr) } From ac830e8bb114a21b09570eeaebb4251af122b662 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sat, 26 Dec 2020 00:28:05 -0800 Subject: [PATCH 027/566] Dereference match expressions to clean up patterns These were flagged by `cargo clippy`: warning: you don't need to add `&` to all patterns --- src/iface/ethernet.rs | 34 +++---- src/lib.rs | 24 ++--- src/socket/icmp.rs | 6 +- src/socket/mod.rs | 4 +- src/socket/set.rs | 14 +-- src/socket/tcp.rs | 24 ++--- src/wire/arp.rs | 18 ++-- src/wire/ethernet.rs | 10 +- src/wire/icmpv4.rs | 124 +++++++++++------------ src/wire/icmpv6.rs | 110 ++++++++++---------- src/wire/igmp.rs | 28 +++--- src/wire/ip.rs | 218 ++++++++++++++++++++-------------------- src/wire/ipv6option.rs | 64 ++++++------ src/wire/ipv6routing.rs | 34 +++---- src/wire/ndisc.rs | 12 +-- src/wire/ndiscoption.rs | 40 ++++---- src/wire/tcp.rs | 22 ++-- 17 files changed, 393 insertions(+), 393 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index b80cf97ab..263a7f1a9 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -288,22 +288,22 @@ enum Packet<'a> { impl<'a> Packet<'a> { fn neighbor_addr(&self) -> Option { - match self { - &Packet::None => None, + match *self { + Packet::None => None, #[cfg(feature = "proto-ipv4")] - &Packet::Arp(_) => None, + Packet::Arp(_) => None, #[cfg(feature = "proto-ipv4")] - &Packet::Icmpv4((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), + Packet::Icmpv4((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), #[cfg(feature = "proto-igmp")] - &Packet::Igmp((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), + Packet::Igmp((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), #[cfg(feature = "proto-ipv6")] - &Packet::Icmpv6((ref ipv6_repr, _)) => Some(ipv6_repr.dst_addr.into()), + Packet::Icmpv6((ref ipv6_repr, _)) => Some(ipv6_repr.dst_addr.into()), #[cfg(feature = "socket-raw")] - &Packet::Raw((ref ip_repr, _)) => Some(ip_repr.dst_addr()), + Packet::Raw((ref ip_repr, _)) => Some(ip_repr.dst_addr()), #[cfg(feature = "socket-udp")] - &Packet::Udp((ref ip_repr, _)) => Some(ip_repr.dst_addr()), + Packet::Udp((ref ip_repr, _)) => Some(ip_repr.dst_addr()), #[cfg(feature = "socket-tcp")] - &Packet::Tcp((ref ip_repr, _)) => Some(ip_repr.dst_addr()) + Packet::Tcp((ref ip_repr, _)) => Some(ip_repr.dst_addr()) } } } @@ -723,7 +723,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool { self.ip_addrs.iter().find(|cidr| { match *cidr { - &IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK=> { + IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK=> { // Take the lower order 24 bits of the IPv6 address and // append those bits to FF02:0:0:0:0:1:FF00::/104. addr.as_bytes()[14..] == cidr.address().as_bytes()[14..] @@ -744,8 +744,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { pub fn ipv4_address(&self) -> Option { self.ip_addrs.iter() .filter_map( - |addr| match addr { - &IpCidr::Ipv4(cidr) => Some(cidr.address()), + |addr| match *addr { + IpCidr::Ipv4(cidr) => Some(cidr.address()), _ => None, }) .next() @@ -1558,24 +1558,24 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { if dst_addr.is_multicast() { let b = dst_addr.as_bytes(); let hardware_addr = - match dst_addr { - &IpAddress::Unspecified => + match *dst_addr { + IpAddress::Unspecified => None, #[cfg(feature = "proto-ipv4")] - &IpAddress::Ipv4(_addr) => + IpAddress::Ipv4(_addr) => Some(EthernetAddress::from_bytes(&[ 0x01, 0x00, 0x5e, b[1] & 0x7F, b[2], b[3], ])), #[cfg(feature = "proto-ipv6")] - &IpAddress::Ipv6(_addr) => + IpAddress::Ipv6(_addr) => Some(EthernetAddress::from_bytes(&[ 0x33, 0x33, b[12], b[13], b[14], b[15], ])), - &IpAddress::__Nonexhaustive => + IpAddress::__Nonexhaustive => unreachable!() }; match hardware_addr { diff --git a/src/lib.rs b/src/lib.rs index 4af96235b..94612dd1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,18 +166,18 @@ pub type Result = core::result::Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Error::Exhausted => write!(f, "buffer space exhausted"), - &Error::Illegal => write!(f, "illegal operation"), - &Error::Unaddressable => write!(f, "unaddressable destination"), - &Error::Finished => write!(f, "operation finished"), - &Error::Truncated => write!(f, "truncated packet"), - &Error::Checksum => write!(f, "checksum error"), - &Error::Unrecognized => write!(f, "unrecognized packet"), - &Error::Fragmented => write!(f, "fragmented packet"), - &Error::Malformed => write!(f, "malformed packet"), - &Error::Dropped => write!(f, "dropped by socket"), - &Error::__Nonexhaustive => unreachable!() + match *self { + Error::Exhausted => write!(f, "buffer space exhausted"), + Error::Illegal => write!(f, "illegal operation"), + Error::Unaddressable => write!(f, "unaddressable destination"), + Error::Finished => write!(f, "operation finished"), + Error::Truncated => write!(f, "truncated packet"), + Error::Checksum => write!(f, "checksum error"), + Error::Unrecognized => write!(f, "unrecognized packet"), + Error::Fragmented => write!(f, "fragmented packet"), + Error::Malformed => write!(f, "malformed packet"), + Error::Dropped => write!(f, "dropped by socket"), + Error::__Nonexhaustive => unreachable!() } } } diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index b06daaf14..c4364a2a5 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -316,9 +316,9 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { pub(crate) fn process(&mut self, ip_repr: &IpRepr, icmp_repr: &IcmpRepr, _cksum: &ChecksumCapabilities) -> Result<()> { - match icmp_repr { + match *icmp_repr { #[cfg(feature = "proto-ipv4")] - &IcmpRepr::Ipv4(ref icmp_repr) => { + IcmpRepr::Ipv4(ref icmp_repr) => { let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; icmp_repr.emit(&mut Icmpv4Packet::new_unchecked(packet_buf), @@ -328,7 +328,7 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { self.meta.handle, icmp_repr.buffer_len(), packet_buf.len()); }, #[cfg(feature = "proto-ipv6")] - &IcmpRepr::Ipv6(ref icmp_repr) => { + IcmpRepr::Ipv6(ref icmp_repr) => { let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; icmp_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(), diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d35724b82..531a062bc 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -69,8 +69,8 @@ pub(crate) enum PollAt { impl PollAt { #[cfg(feature = "socket-tcp")] fn is_ingress(&self) -> bool { - match self { - &PollAt::Ingress => true, + match *self { + PollAt::Ingress => true, _ => false, } } diff --git a/src/socket/set.rs b/src/socket/set.rs index 2fe6f24b5..477b8a2d7 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -139,25 +139,25 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { pub fn prune(&mut self) { for (index, item) in self.sockets.iter_mut().enumerate() { let mut may_remove = false; - if let &mut Some(Item { refs: 0, ref mut socket }) = item { - match socket { + if let Some(Item { refs: 0, ref mut socket }) = *item { + match *socket { #[cfg(feature = "socket-raw")] - &mut Socket::Raw(_) => + Socket::Raw(_) => may_remove = true, #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - &mut Socket::Icmp(_) => + Socket::Icmp(_) => may_remove = true, #[cfg(feature = "socket-udp")] - &mut Socket::Udp(_) => + Socket::Udp(_) => may_remove = true, #[cfg(feature = "socket-tcp")] - &mut Socket::Tcp(ref mut socket) => + Socket::Tcp(ref mut socket) => if socket.state() == TcpState::Closed { may_remove = true } else { socket.close() }, - &mut Socket::__Nonexhaustive(_) => unreachable!() + Socket::__Nonexhaustive(_) => unreachable!() } } if may_remove { diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 7f27215a0..6b1d0f2f8 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -34,18 +34,18 @@ pub enum State { impl fmt::Display for State { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &State::Closed => write!(f, "CLOSED"), - &State::Listen => write!(f, "LISTEN"), - &State::SynSent => write!(f, "SYN-SENT"), - &State::SynReceived => write!(f, "SYN-RECEIVED"), - &State::Established => write!(f, "ESTABLISHED"), - &State::FinWait1 => write!(f, "FIN-WAIT-1"), - &State::FinWait2 => write!(f, "FIN-WAIT-2"), - &State::CloseWait => write!(f, "CLOSE-WAIT"), - &State::Closing => write!(f, "CLOSING"), - &State::LastAck => write!(f, "LAST-ACK"), - &State::TimeWait => write!(f, "TIME-WAIT") + match *self { + State::Closed => write!(f, "CLOSED"), + State::Listen => write!(f, "LISTEN"), + State::SynSent => write!(f, "SYN-SENT"), + State::SynReceived => write!(f, "SYN-RECEIVED"), + State::Established => write!(f, "ESTABLISHED"), + State::FinWait1 => write!(f, "FIN-WAIT-1"), + State::FinWait2 => write!(f, "FIN-WAIT-2"), + State::CloseWait => write!(f, "CLOSE-WAIT"), + State::Closing => write!(f, "CLOSING"), + State::LastAck => write!(f, "LAST-ACK"), + State::TimeWait => write!(f, "TIME-WAIT") } } } diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 7e66d1d44..b96d45522 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -290,16 +290,16 @@ impl Repr { /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { - match self { - &Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end, - &Repr::__Nonexhaustive => unreachable!() + match *self { + Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end, + Repr::__Nonexhaustive => unreachable!() } } /// Emit a high-level representation into an Address Resolution Protocol packet. pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { - match self { - &Repr::EthernetIpv4 { + match *self { + Repr::EthernetIpv4 { operation, source_hardware_addr, source_protocol_addr, target_hardware_addr, target_protocol_addr @@ -314,7 +314,7 @@ impl Repr { packet.set_target_hardware_addr(target_hardware_addr.as_bytes()); packet.set_target_protocol_addr(target_protocol_addr.as_bytes()); }, - &Repr::__Nonexhaustive => unreachable!() + Repr::__Nonexhaustive => unreachable!() } } } @@ -340,8 +340,8 @@ impl> fmt::Display for Packet { impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Repr::EthernetIpv4 { + match *self { + Repr::EthernetIpv4 { operation, source_hardware_addr, source_protocol_addr, target_hardware_addr, target_protocol_addr @@ -351,7 +351,7 @@ impl fmt::Display for Repr { target_hardware_addr, target_protocol_addr, operation) }, - &Repr::__Nonexhaustive => unreachable!() + Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 6847b62e2..4b69caffc 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -14,11 +14,11 @@ enum_with_unknown! { impl fmt::Display for EtherType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &EtherType::Ipv4 => write!(f, "IPv4"), - &EtherType::Ipv6 => write!(f, "IPv6"), - &EtherType::Arp => write!(f, "ARP"), - &EtherType::Unknown(id) => write!(f, "0x{:04x}", id) + match *self { + EtherType::Ipv4 => write!(f, "IPv4"), + EtherType::Ipv6 => write!(f, "IPv6"), + EtherType::Arp => write!(f, "ARP"), + EtherType::Unknown(id) => write!(f, "0x{:04x}", id) } } } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index a066bfd8e..0d71fbc5b 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -34,18 +34,18 @@ enum_with_unknown! { impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Message::EchoReply => write!(f, "echo reply"), - &Message::DstUnreachable => write!(f, "destination unreachable"), - &Message::Redirect => write!(f, "message redirect"), - &Message::EchoRequest => write!(f, "echo request"), - &Message::RouterAdvert => write!(f, "router advertisement"), - &Message::RouterSolicit => write!(f, "router solicitation"), - &Message::TimeExceeded => write!(f, "time exceeded"), - &Message::ParamProblem => write!(f, "parameter problem"), - &Message::Timestamp => write!(f, "timestamp"), - &Message::TimestampReply => write!(f, "timestamp reply"), - &Message::Unknown(id) => write!(f, "{}", id) + match *self { + Message::EchoReply => write!(f, "echo reply"), + Message::DstUnreachable => write!(f, "destination unreachable"), + Message::Redirect => write!(f, "message redirect"), + Message::EchoRequest => write!(f, "echo request"), + Message::RouterAdvert => write!(f, "router advertisement"), + Message::RouterSolicit => write!(f, "router solicitation"), + Message::TimeExceeded => write!(f, "time exceeded"), + Message::ParamProblem => write!(f, "parameter problem"), + Message::Timestamp => write!(f, "timestamp"), + Message::TimestampReply => write!(f, "timestamp reply"), + Message::Unknown(id) => write!(f, "{}", id) } } } @@ -90,40 +90,40 @@ enum_with_unknown! { impl fmt::Display for DstUnreachable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &DstUnreachable::NetUnreachable => - write!(f, "destination network unreachable"), - &DstUnreachable::HostUnreachable => - write!(f, "destination host unreachable"), - &DstUnreachable::ProtoUnreachable => - write!(f, "destination protocol unreachable"), - &DstUnreachable::PortUnreachable => - write!(f, "destination port unreachable"), - &DstUnreachable::FragRequired => - write!(f, "fragmentation required, and DF flag set"), - &DstUnreachable::SrcRouteFailed => - write!(f, "source route failed"), - &DstUnreachable::DstNetUnknown => - write!(f, "destination network unknown"), - &DstUnreachable::DstHostUnknown => - write!(f, "destination host unknown"), - &DstUnreachable::SrcHostIsolated => - write!(f, "source host isolated"), - &DstUnreachable::NetProhibited => - write!(f, "network administratively prohibited"), - &DstUnreachable::HostProhibited => - write!(f, "host administratively prohibited"), - &DstUnreachable::NetUnreachToS => - write!(f, "network unreachable for ToS"), - &DstUnreachable::HostUnreachToS => - write!(f, "host unreachable for ToS"), - &DstUnreachable::CommProhibited => - write!(f, "communication administratively prohibited"), - &DstUnreachable::HostPrecedViol => - write!(f, "host precedence violation"), - &DstUnreachable::PrecedCutoff => - write!(f, "precedence cutoff in effect"), - &DstUnreachable::Unknown(id) => + match *self { + DstUnreachable::NetUnreachable => + write!(f, "destination network unreachable"), + DstUnreachable::HostUnreachable => + write!(f, "destination host unreachable"), + DstUnreachable::ProtoUnreachable => + write!(f, "destination protocol unreachable"), + DstUnreachable::PortUnreachable => + write!(f, "destination port unreachable"), + DstUnreachable::FragRequired => + write!(f, "fragmentation required, and DF flag set"), + DstUnreachable::SrcRouteFailed => + write!(f, "source route failed"), + DstUnreachable::DstNetUnknown => + write!(f, "destination network unknown"), + DstUnreachable::DstHostUnknown => + write!(f, "destination host unknown"), + DstUnreachable::SrcHostIsolated => + write!(f, "source host isolated"), + DstUnreachable::NetProhibited => + write!(f, "network administratively prohibited"), + DstUnreachable::HostProhibited => + write!(f, "host administratively prohibited"), + DstUnreachable::NetUnreachToS => + write!(f, "network unreachable for ToS"), + DstUnreachable::HostUnreachToS => + write!(f, "host unreachable for ToS"), + DstUnreachable::CommProhibited => + write!(f, "communication administratively prohibited"), + DstUnreachable::HostPrecedViol => + write!(f, "host precedence violation"), + DstUnreachable::PrecedCutoff => + write!(f, "precedence cutoff in effect"), + DstUnreachable::Unknown(id) => write!(f, "{}", id) } } @@ -455,8 +455,8 @@ impl<'a> Repr<'a> { pub fn emit(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { packet.set_msg_code(0); - match self { - &Repr::EchoRequest { ident, seq_no, data } => { + match *self { + Repr::EchoRequest { ident, seq_no, data } => { packet.set_msg_type(Message::EchoRequest); packet.set_msg_code(0); packet.set_echo_ident(ident); @@ -465,7 +465,7 @@ impl<'a> Repr<'a> { packet.data_mut()[..data_len].copy_from_slice(&data[..data_len]) }, - &Repr::EchoReply { ident, seq_no, data } => { + Repr::EchoReply { ident, seq_no, data } => { packet.set_msg_type(Message::EchoReply); packet.set_msg_code(0); packet.set_echo_ident(ident); @@ -474,7 +474,7 @@ impl<'a> Repr<'a> { packet.data_mut()[..data_len].copy_from_slice(&data[..data_len]) }, - &Repr::DstUnreachable { reason, header, data } => { + Repr::DstUnreachable { reason, header, data } => { packet.set_msg_type(Message::DstUnreachable); packet.set_msg_code(reason.into()); @@ -484,7 +484,7 @@ impl<'a> Repr<'a> { payload.copy_from_slice(&data[..]) } - &Repr::__Nonexhaustive => unreachable!() + Repr::__Nonexhaustive => unreachable!() } if checksum_caps.icmpv4.tx() { @@ -516,17 +516,17 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Repr::EchoRequest { ident, seq_no, data } => - write!(f, "ICMPv4 echo request id={} seq={} len={}", - ident, seq_no, data.len()), - &Repr::EchoReply { ident, seq_no, data } => - write!(f, "ICMPv4 echo reply id={} seq={} len={}", - ident, seq_no, data.len()), - &Repr::DstUnreachable { reason, .. } => - write!(f, "ICMPv4 destination unreachable ({})", - reason), - &Repr::__Nonexhaustive => unreachable!() + match *self { + Repr::EchoRequest { ident, seq_no, data } => + write!(f, "ICMPv4 echo request id={} seq={} len={}", + ident, seq_no, data.len()), + Repr::EchoReply { ident, seq_no, data } => + write!(f, "ICMPv4 echo reply id={} seq={} len={}", + ident, seq_no, data.len()), + Repr::DstUnreachable { reason, .. } => + write!(f, "ICMPv4 destination unreachable ({})", + reason), + Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index fd180630c..d2c3cbc0e 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -77,21 +77,21 @@ impl Message { impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Message::DstUnreachable => write!(f, "destination unreachable"), - &Message::PktTooBig => write!(f, "packet too big"), - &Message::TimeExceeded => write!(f, "time exceeded"), - &Message::ParamProblem => write!(f, "parameter problem"), - &Message::EchoReply => write!(f, "echo reply"), - &Message::EchoRequest => write!(f, "echo request"), - &Message::RouterSolicit => write!(f, "router solicitation"), - &Message::RouterAdvert => write!(f, "router advertisement"), - &Message::NeighborSolicit => write!(f, "neighbor solicitation"), - &Message::NeighborAdvert => write!(f, "neighbor advert"), - &Message::Redirect => write!(f, "redirect"), - &Message::MldQuery => write!(f, "multicast listener query"), - &Message::MldReport => write!(f, "multicast listener report"), - &Message::Unknown(id) => write!(f, "{}", id) + match *self { + Message::DstUnreachable => write!(f, "destination unreachable"), + Message::PktTooBig => write!(f, "packet too big"), + Message::TimeExceeded => write!(f, "time exceeded"), + Message::ParamProblem => write!(f, "parameter problem"), + Message::EchoReply => write!(f, "echo reply"), + Message::EchoRequest => write!(f, "echo request"), + Message::RouterSolicit => write!(f, "router solicitation"), + Message::RouterAdvert => write!(f, "router advertisement"), + Message::NeighborSolicit => write!(f, "neighbor solicitation"), + Message::NeighborAdvert => write!(f, "neighbor advert"), + Message::Redirect => write!(f, "redirect"), + Message::MldQuery => write!(f, "multicast listener query"), + Message::MldReport => write!(f, "multicast listener report"), + Message::Unknown(id) => write!(f, "{}", id) } } } @@ -118,22 +118,22 @@ enum_with_unknown! { impl fmt::Display for DstUnreachable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &DstUnreachable::NoRoute => - write!(f, "no route to destination"), - &DstUnreachable::AdminProhibit => - write!(f, "communication with destination administratively prohibited"), - &DstUnreachable::BeyondScope => - write!(f, "beyond scope of source address"), - &DstUnreachable::AddrUnreachable => - write!(f, "address unreachable"), - &DstUnreachable::PortUnreachable => - write!(f, "port unreachable"), - &DstUnreachable::FailedPolicy => - write!(f, "source address failed ingress/egress policy"), - &DstUnreachable::RejectRoute => - write!(f, "reject route to destination"), - &DstUnreachable::Unknown(id) => + match *self { + DstUnreachable::NoRoute => + write!(f, "no route to destination"), + DstUnreachable::AdminProhibit => + write!(f, "communication with destination administratively prohibited"), + DstUnreachable::BeyondScope => + write!(f, "beyond scope of source address"), + DstUnreachable::AddrUnreachable => + write!(f, "address unreachable"), + DstUnreachable::PortUnreachable => + write!(f, "port unreachable"), + DstUnreachable::FailedPolicy => + write!(f, "source address failed ingress/egress policy"), + DstUnreachable::RejectRoute => + write!(f, "reject route to destination"), + DstUnreachable::Unknown(id) => write!(f, "{}", id) } } @@ -153,14 +153,14 @@ enum_with_unknown! { impl fmt::Display for ParamProblem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &ParamProblem::ErroneousHdrField => - write!(f, "erroneous header field."), - &ParamProblem::UnrecognizedNxtHdr => - write!(f, "unrecognized next header type."), - &ParamProblem::UnrecognizedOption => - write!(f, "unrecognized IPv6 option."), - &ParamProblem::Unknown(id) => + match *self { + ParamProblem::ErroneousHdrField => + write!(f, "erroneous header field."), + ParamProblem::UnrecognizedNxtHdr => + write!(f, "unrecognized next header type."), + ParamProblem::UnrecognizedOption => + write!(f, "unrecognized IPv6 option."), + ParamProblem::Unknown(id) => write!(f, "{}", id) } } @@ -178,12 +178,12 @@ enum_with_unknown! { impl fmt::Display for TimeExceeded { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &TimeExceeded::HopLimitExceeded => - write!(f, "hop limit exceeded in transit"), - &TimeExceeded::FragReassemExceeded => - write!(f, "fragment reassembly time exceeded"), - &TimeExceeded::Unknown(id) => + match *self { + TimeExceeded::HopLimitExceeded => + write!(f, "hop limit exceeded in transit"), + TimeExceeded::FragReassemExceeded => + write!(f, "fragment reassembly time exceeded"), + TimeExceeded::Unknown(id) => write!(f, "{}", id) } } @@ -667,15 +667,15 @@ impl<'a> Repr<'a> { payload.copy_from_slice(&data[..]); } - match self { - &Repr::DstUnreachable { reason, header, data } => { + match *self { + Repr::DstUnreachable { reason, header, data } => { packet.set_msg_type(Message::DstUnreachable); packet.set_msg_code(reason.into()); emit_contained_packet(packet.payload_mut(), header, &data); }, - &Repr::PktTooBig { mtu, header, data } => { + Repr::PktTooBig { mtu, header, data } => { packet.set_msg_type(Message::PktTooBig); packet.set_msg_code(0); packet.set_pkt_too_big_mtu(mtu); @@ -683,14 +683,14 @@ impl<'a> Repr<'a> { emit_contained_packet(packet.payload_mut(), header, &data); }, - &Repr::TimeExceeded { reason, header, data } => { + Repr::TimeExceeded { reason, header, data } => { packet.set_msg_type(Message::TimeExceeded); packet.set_msg_code(reason.into()); emit_contained_packet(packet.payload_mut(), header, &data); }, - &Repr::ParamProblem { reason, pointer, header, data } => { + Repr::ParamProblem { reason, pointer, header, data } => { packet.set_msg_type(Message::ParamProblem); packet.set_msg_code(reason.into()); packet.set_param_problem_ptr(pointer); @@ -698,7 +698,7 @@ impl<'a> Repr<'a> { emit_contained_packet(packet.payload_mut(), header, &data); }, - &Repr::EchoRequest { ident, seq_no, data } => { + Repr::EchoRequest { ident, seq_no, data } => { packet.set_msg_type(Message::EchoRequest); packet.set_msg_code(0); packet.set_echo_ident(ident); @@ -707,7 +707,7 @@ impl<'a> Repr<'a> { packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) }, - &Repr::EchoReply { ident, seq_no, data } => { + Repr::EchoReply { ident, seq_no, data } => { packet.set_msg_type(Message::EchoReply); packet.set_msg_code(0); packet.set_echo_ident(ident); @@ -717,15 +717,15 @@ impl<'a> Repr<'a> { }, #[cfg(feature = "ethernet")] - &Repr::Ndisc(ndisc) => { + Repr::Ndisc(ndisc) => { ndisc.emit(packet) }, - &Repr::Mld(mld) => { + Repr::Mld(mld) => { mld.emit(packet) }, - &Repr::__Nonexhaustive => unreachable!(), + Repr::__Nonexhaustive => unreachable!(), } if checksum_caps.icmpv6.tx() { diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 94e057210..64ad6577b 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -38,12 +38,12 @@ mod field { impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Message::MembershipQuery => write!(f, "membership query"), - &Message::MembershipReportV2 => write!(f, "version 2 membership report"), - &Message::LeaveGroup => write!(f, "leave group"), - &Message::MembershipReportV1 => write!(f, "version 1 membership report"), - &Message::Unknown(id) => write!(f, "{}", id), + match *self { + Message::MembershipQuery => write!(f, "membership query"), + Message::MembershipReportV2 => write!(f, "version 2 membership report"), + Message::LeaveGroup => write!(f, "leave group"), + Message::MembershipReportV1 => write!(f, "version 1 membership report"), + Message::Unknown(id) => write!(f, "{}", id), } } } @@ -251,8 +251,8 @@ impl Repr { pub fn emit(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { - match self { - &Repr::MembershipQuery { + match *self { + Repr::MembershipQuery { max_resp_time, group_addr, version @@ -266,7 +266,7 @@ impl Repr { } packet.set_group_address(group_addr); } - &Repr::MembershipReport { + Repr::MembershipReport { group_addr, version, } => { @@ -277,7 +277,7 @@ impl Repr { packet.set_max_resp_code(0); packet.set_group_address(group_addr); } - &Repr::LeaveGroup { group_addr } => { + Repr::LeaveGroup { group_addr } => { packet.set_msg_type(Message::LeaveGroup); packet.set_group_address(group_addr); } @@ -327,8 +327,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl<'a> fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Repr::MembershipQuery { + match *self { + Repr::MembershipQuery { max_resp_time, group_addr, version, @@ -339,7 +339,7 @@ impl<'a> fmt::Display for Repr { group_addr, version) } - &Repr::MembershipReport { + Repr::MembershipReport { group_addr, version, } => { @@ -348,7 +348,7 @@ impl<'a> fmt::Display for Repr { group_addr, version) } - &Repr::LeaveGroup { group_addr } => { + Repr::LeaveGroup { group_addr } => { write!(f, "IGMP leave group group_addr={})", group_addr) } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index ac5ba67ef..bdc843c41 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -38,13 +38,13 @@ impl Version { impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Version::Unspecified => write!(f, "IPv?"), + match *self { + Version::Unspecified => write!(f, "IPv?"), #[cfg(feature = "proto-ipv4")] - &Version::Ipv4 => write!(f, "IPv4"), + Version::Ipv4 => write!(f, "IPv4"), #[cfg(feature = "proto-ipv6")] - &Version::Ipv6 => write!(f, "IPv6"), - &Version::__Nonexhaustive => unreachable!() + Version::Ipv6 => write!(f, "IPv6"), + Version::__Nonexhaustive => unreachable!() } } } @@ -67,18 +67,18 @@ enum_with_unknown! { impl fmt::Display for Protocol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Protocol::HopByHop => write!(f, "Hop-by-Hop"), - &Protocol::Icmp => write!(f, "ICMP"), - &Protocol::Igmp => write!(f, "IGMP"), - &Protocol::Tcp => write!(f, "TCP"), - &Protocol::Udp => write!(f, "UDP"), - &Protocol::Ipv6Route => write!(f, "IPv6-Route"), - &Protocol::Ipv6Frag => write!(f, "IPv6-Frag"), - &Protocol::Icmpv6 => write!(f, "ICMPv6"), - &Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"), - &Protocol::Ipv6Opts => write!(f, "IPv6-Opts"), - &Protocol::Unknown(id) => write!(f, "0x{:02x}", id) + match *self { + Protocol::HopByHop => write!(f, "Hop-by-Hop"), + Protocol::Icmp => write!(f, "ICMP"), + Protocol::Igmp => write!(f, "IGMP"), + Protocol::Tcp => write!(f, "TCP"), + Protocol::Udp => write!(f, "UDP"), + Protocol::Ipv6Route => write!(f, "IPv6-Route"), + Protocol::Ipv6Frag => write!(f, "IPv6-Frag"), + Protocol::Icmpv6 => write!(f, "ICMPv6"), + Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"), + Protocol::Ipv6Opts => write!(f, "IPv6-Opts"), + Protocol::Unknown(id) => write!(f, "0x{:02x}", id) } } } @@ -115,73 +115,73 @@ impl Address { /// Return an address as a sequence of octets, in big-endian. pub fn as_bytes(&self) -> &[u8] { - match self { - &Address::Unspecified => &[], + match *self { + Address::Unspecified => &[], #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(ref addr) => addr.as_bytes(), + Address::Ipv4(ref addr) => addr.as_bytes(), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(ref addr) => addr.as_bytes(), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(ref addr) => addr.as_bytes(), + Address::__Nonexhaustive => unreachable!() } } /// Query whether the address is a valid unicast address. pub fn is_unicast(&self) -> bool { - match self { - &Address::Unspecified => false, + match *self { + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => addr.is_unicast(), + Address::Ipv4(addr) => addr.is_unicast(), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(addr) => addr.is_unicast(), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(addr) => addr.is_unicast(), + Address::__Nonexhaustive => unreachable!() } } /// Query whether the address is a valid multicast address. pub fn is_multicast(&self) -> bool { - match self { - &Address::Unspecified => false, + match *self { + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => addr.is_multicast(), + Address::Ipv4(addr) => addr.is_multicast(), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(addr) => addr.is_multicast(), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(addr) => addr.is_multicast(), + Address::__Nonexhaustive => unreachable!() } } /// Query whether the address is the broadcast address. pub fn is_broadcast(&self) -> bool { - match self { - &Address::Unspecified => false, + match *self { + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => addr.is_broadcast(), + Address::Ipv4(addr) => addr.is_broadcast(), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(_) => false, - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(_) => false, + Address::__Nonexhaustive => unreachable!() } } /// Query whether the address falls into the "unspecified" range. pub fn is_unspecified(&self) -> bool { - match self { - &Address::Unspecified => true, + match *self { + Address::Unspecified => true, #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => addr.is_unspecified(), + Address::Ipv4(addr) => addr.is_unspecified(), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(addr) => addr.is_unspecified(), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(addr) => addr.is_unspecified(), + Address::__Nonexhaustive => unreachable!() } } /// Return an unspecified address that has the same IP version as `self`. pub fn to_unspecified(&self) -> Address { - match self { - &Address::Unspecified => Address::Unspecified, + match *self { + Address::Unspecified => Address::Unspecified, #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), + Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), + Address::__Nonexhaustive => unreachable!() } } @@ -260,13 +260,13 @@ impl From for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Address::Unspecified => write!(f, "*"), + match *self { + Address::Unspecified => write!(f, "*"), #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => write!(f, "{}", addr), + Address::Ipv4(addr) => write!(f, "{}", addr), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(addr) => write!(f, "{}", addr), - &Address::__Nonexhaustive => unreachable!() + Address::Ipv6(addr) => write!(f, "{}", addr), + Address::__Nonexhaustive => unreachable!() } } } @@ -304,23 +304,23 @@ impl Cidr { /// Return the IP address of this CIDR block. pub fn address(&self) -> Address { - match self { + match *self { #[cfg(feature = "proto-ipv4")] - &Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), + Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), #[cfg(feature = "proto-ipv6")] - &Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()), - &Cidr::__Nonexhaustive => unreachable!() + Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()), + Cidr::__Nonexhaustive => unreachable!() } } /// Return the prefix length of this CIDR block. pub fn prefix_len(&self) -> u8 { - match self { + match *self { #[cfg(feature = "proto-ipv4")] - &Cidr::Ipv4(cidr) => cidr.prefix_len(), + Cidr::Ipv4(cidr) => cidr.prefix_len(), #[cfg(feature = "proto-ipv6")] - &Cidr::Ipv6(cidr) => cidr.prefix_len(), - &Cidr::__Nonexhaustive => unreachable!() + Cidr::Ipv6(cidr) => cidr.prefix_len(), + Cidr::__Nonexhaustive => unreachable!() } } @@ -383,12 +383,12 @@ impl From for Cidr { impl fmt::Display for Cidr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { + match *self { #[cfg(feature = "proto-ipv4")] - &Cidr::Ipv4(cidr) => write!(f, "{}", cidr), + Cidr::Ipv4(cidr) => write!(f, "{}", cidr), #[cfg(feature = "proto-ipv6")] - &Cidr::Ipv6(cidr) => write!(f, "{}", cidr), - &Cidr::__Nonexhaustive => unreachable!() + Cidr::Ipv6(cidr) => write!(f, "{}", cidr), + Cidr::__Nonexhaustive => unreachable!() } } } @@ -504,88 +504,88 @@ impl From for Repr { impl Repr { /// Return the protocol version. pub fn version(&self) -> Version { - match self { - &Repr::Unspecified { .. } => Version::Unspecified, + match *self { + Repr::Unspecified { .. } => Version::Unspecified, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(_) => Version::Ipv4, + Repr::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(_) => Version::Ipv6, - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(_) => Version::Ipv6, + Repr::__Nonexhaustive => unreachable!() } } /// Return the source address. pub fn src_addr(&self) -> Address { - match self { - &Repr::Unspecified { src_addr, .. } => src_addr, + match *self { + Repr::Unspecified { src_addr, .. } => src_addr, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), + Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr), - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr), + Repr::__Nonexhaustive => unreachable!() } } /// Return the destination address. pub fn dst_addr(&self) -> Address { - match self { - &Repr::Unspecified { dst_addr, .. } => dst_addr, + match *self { + Repr::Unspecified { dst_addr, .. } => dst_addr, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), + Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr), - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr), + Repr::__Nonexhaustive => unreachable!() } } /// Return the protocol. pub fn protocol(&self) -> Protocol { - match self { - &Repr::Unspecified { protocol, .. } => protocol, + match *self { + Repr::Unspecified { protocol, .. } => protocol, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => repr.protocol, + Repr::Ipv4(repr) => repr.protocol, #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => repr.next_header, - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(repr) => repr.next_header, + Repr::__Nonexhaustive => unreachable!() } } /// Return the payload length. pub fn payload_len(&self) -> usize { - match self { - &Repr::Unspecified { payload_len, .. } => payload_len, + match *self { + Repr::Unspecified { payload_len, .. } => payload_len, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => repr.payload_len, + Repr::Ipv4(repr) => repr.payload_len, #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => repr.payload_len, - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(repr) => repr.payload_len, + Repr::__Nonexhaustive => unreachable!() } } /// Set the payload length. pub fn set_payload_len(&mut self, length: usize) { - match self { - &mut Repr::Unspecified { ref mut payload_len, .. } => + match *self { + Repr::Unspecified { ref mut payload_len, .. } => *payload_len = length, #[cfg(feature = "proto-ipv4")] - &mut Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) => + Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) => *payload_len = length, #[cfg(feature = "proto-ipv6")] - &mut Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) => + Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) => *payload_len = length, - &mut Repr::__Nonexhaustive => unreachable!() + Repr::__Nonexhaustive => unreachable!() } } /// Return the TTL value. pub fn hop_limit(&self) -> u8 { - match self { - &Repr::Unspecified { hop_limit, .. } => hop_limit, + match *self { + Repr::Unspecified { hop_limit, .. } => hop_limit, #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, + Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit, - &Repr::__Nonexhaustive => unreachable!() + Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit, + Repr::__Nonexhaustive => unreachable!() } } @@ -722,16 +722,16 @@ impl Repr { /// # Panics /// This function panics if invoked on an unspecified representation. pub fn buffer_len(&self) -> usize { - match self { - &Repr::Unspecified { .. } => + match *self { + Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => + Repr::Ipv4(repr) => repr.buffer_len(), #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => + Repr::Ipv6(repr) => repr.buffer_len(), - &Repr::__Nonexhaustive => + Repr::__Nonexhaustive => unreachable!() } } @@ -741,16 +741,16 @@ impl Repr { /// # Panics /// This function panics if invoked on an unspecified representation. pub fn emit + AsMut<[u8]>>(&self, buffer: T, _checksum_caps: &ChecksumCapabilities) { - match self { - &Repr::Unspecified { .. } => + match *self { + Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(repr) => + Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), &_checksum_caps), #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(repr) => + Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), - &Repr::__Nonexhaustive => + Repr::__Nonexhaustive => unreachable!() } } diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 26ba587b2..1cf4dee41 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -13,10 +13,10 @@ enum_with_unknown! { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Type::Pad1 => write!(f, "Pad1"), - &Type::PadN => write!(f, "PadN"), - &Type::Unknown(id) => write!(f, "{}", id) + match *self { + Type::Pad1 => write!(f, "Pad1"), + Type::PadN => write!(f, "PadN"), + Type::Unknown(id) => write!(f, "{}", id) } } } @@ -39,12 +39,12 @@ enum_with_unknown! { impl fmt::Display for FailureType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &FailureType::Skip => write!(f, "skip"), - &FailureType::Discard => write!(f, "discard"), - &FailureType::DiscardSendAll => write!(f, "discard and send error"), - &FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"), - &FailureType::Unknown(id) => write!(f, "Unknown({})", id), + match *self { + FailureType::Skip => write!(f, "skip"), + FailureType::Discard => write!(f, "discard"), + FailureType::DiscardSendAll => write!(f, "discard and send error"), + FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"), + FailureType::Unknown(id) => write!(f, "Unknown({})", id), } } } @@ -247,23 +247,23 @@ impl<'a> Repr<'a> { /// Return the length of a header that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { - match self { - &Repr::Pad1 => 1, - &Repr::PadN(length) => - field::DATA(length).end, - &Repr::Unknown{ length, .. } => - field::DATA(length).end, - - &Repr::__Nonexhaustive => unreachable!() + match *self { + Repr::Pad1 => 1, + Repr::PadN(length) => + field::DATA(length).end, + Repr::Unknown{ length, .. } => + field::DATA(length).end, + + Repr::__Nonexhaustive => unreachable!() } } /// Emit a high-level representation into an IPv6 Extension Header Option. pub fn emit + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) { - match self { - &Repr::Pad1 => + match *self { + Repr::Pad1 => opt.set_option_type(Type::Pad1), - &Repr::PadN(len) => { + Repr::PadN(len) => { opt.set_option_type(Type::PadN); opt.set_data_len(len); // Ensure all padding bytes are set to zero. @@ -271,13 +271,13 @@ impl<'a> Repr<'a> { *x = 0 } } - &Repr::Unknown{ type_, length, data } => { + Repr::Unknown{ type_, length, data } => { opt.set_option_type(type_); opt.set_data_len(length); opt.data_mut().copy_from_slice(&data[..length as usize]); } - &Repr::__Nonexhaustive => unreachable!() + Repr::__Nonexhaustive => unreachable!() } } } @@ -351,15 +351,15 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IPv6 Option ")?; - match self { - &Repr::Pad1 => - write!(f, "{} ", Type::Pad1), - &Repr::PadN(len) => - write!(f, "{} length={} ", Type::PadN, len), - &Repr::Unknown{ type_, length, .. } => - write!(f, "{} length={} ", type_, length), - - &Repr::__Nonexhaustive => unreachable!() + match *self { + Repr::Pad1 => + write!(f, "{} ", Type::Pad1), + Repr::PadN(len) => + write!(f, "{} length={} ", Type::PadN, len), + Repr::Unknown{ type_, length, .. } => + write!(f, "{} length={} ", type_, length), + + Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index befbd2c8a..7956b4773 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -36,15 +36,15 @@ enum_with_unknown! { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Type::Type0 => write!(f, "Type0"), - &Type::Nimrod => write!(f, "Nimrod"), - &Type::Type2 => write!(f, "Type2"), - &Type::Rpl => write!(f, "Rpl"), - &Type::Experiment1 => write!(f, "Experiment1"), - &Type::Experiment2 => write!(f, "Experiment2"), - &Type::Reserved => write!(f, "Reserved"), - &Type::Unknown(id) => write!(f, "{}", id) + match *self { + Type::Type0 => write!(f, "Type0"), + Type::Nimrod => write!(f, "Nimrod"), + Type::Type2 => write!(f, "Type2"), + Type::Rpl => write!(f, "Rpl"), + Type::Experiment1 => write!(f, "Experiment1"), + Type::Experiment2 => write!(f, "Experiment2"), + Type::Reserved => write!(f, "Reserved"), + Type::Unknown(id) => write!(f, "{}", id) } } } @@ -465,8 +465,8 @@ impl<'a> Repr<'a> { /// Emit a high-level representation into an IPv6 Routing Header. pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { - match self { - &Repr::Type2 { next_header, length, segments_left, home_address } => { + match *self { + Repr::Type2 { next_header, length, segments_left, home_address } => { header.set_next_header(next_header); header.set_header_len(length); header.set_routing_type(Type::Type2); @@ -474,7 +474,7 @@ impl<'a> Repr<'a> { header.clear_reserved(); header.set_home_address(home_address); } - &Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => { + Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => { header.set_next_header(next_header); header.set_header_len(length); header.set_routing_type(Type::Rpl); @@ -486,24 +486,24 @@ impl<'a> Repr<'a> { header.set_addresses(addresses); } - &Repr::__Nonexhaustive => unreachable!(), + Repr::__Nonexhaustive => unreachable!(), } } } impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Repr::Type2 { next_header, length, segments_left, home_address } => { + match *self { + Repr::Type2 { next_header, length, segments_left, home_address } => { write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", next_header, length, Type::Type2, segments_left, home_address) } - &Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => { + Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => { write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) } - &Repr::__Nonexhaustive => unreachable!(), + Repr::__Nonexhaustive => unreachable!(), } } } diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 4f3e5c458..afaf0cee9 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -370,8 +370,8 @@ impl<'a> Repr<'a> { pub fn emit(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { - match self { - &Repr::RouterSolicit { lladdr } => { + match *self { + Repr::RouterSolicit { lladdr } => { packet.set_msg_type(Message::RouterSolicit); packet.set_msg_code(0); packet.clear_reserved(); @@ -381,7 +381,7 @@ impl<'a> Repr<'a> { } }, - &Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time, + Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time, retrans_time, lladdr, mtu, prefix_info } => { packet.set_msg_type(Message::RouterAdvert); packet.set_msg_code(0); @@ -410,7 +410,7 @@ impl<'a> Repr<'a> { } }, - &Repr::NeighborSolicit { target_addr, lladdr } => { + Repr::NeighborSolicit { target_addr, lladdr } => { packet.set_msg_type(Message::NeighborSolicit); packet.set_msg_code(0); packet.clear_reserved(); @@ -422,7 +422,7 @@ impl<'a> Repr<'a> { } }, - &Repr::NeighborAdvert { flags, target_addr, lladdr } => { + Repr::NeighborAdvert { flags, target_addr, lladdr } => { packet.set_msg_type(Message::NeighborAdvert); packet.set_msg_code(0); packet.clear_reserved(); @@ -435,7 +435,7 @@ impl<'a> Repr<'a> { } }, - &Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => { + Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => { packet.set_msg_type(Message::Redirect); packet.set_msg_code(0); packet.clear_reserved(); diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 1c8bcbfad..0175f44f1 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -24,12 +24,12 @@ enum_with_unknown! { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - &Type::SourceLinkLayerAddr => write!(f, "source link-layer address"), - &Type::TargetLinkLayerAddr => write!(f, "target link-layer address"), - &Type::PrefixInformation => write!(f, "prefix information"), - &Type::RedirectedHeader => write!(f, "redirected header"), - &Type::Mtu => write!(f, "mtu"), - &Type::Unknown(id) => write!(f, "{}", id) + Type::SourceLinkLayerAddr => write!(f, "source link-layer address"), + Type::TargetLinkLayerAddr => write!(f, "target link-layer address"), + Type::PrefixInformation => write!(f, "prefix information"), + Type::RedirectedHeader => write!(f, "redirected header"), + Type::Mtu => write!(f, "mtu"), + Type::Unknown(id) => write!(f, "{}", id) } } } @@ -504,18 +504,18 @@ impl<'a> Repr<'a> { /// Emit a high-level representation into an NDISC Option. pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { - match self { - &Repr::SourceLinkLayerAddr(addr) => { + match *self { + Repr::SourceLinkLayerAddr(addr) => { opt.set_option_type(Type::SourceLinkLayerAddr); opt.set_data_len(1); opt.set_link_layer_addr(addr); }, - &Repr::TargetLinkLayerAddr(addr) => { + Repr::TargetLinkLayerAddr(addr) => { opt.set_option_type(Type::TargetLinkLayerAddr); opt.set_data_len(1); opt.set_link_layer_addr(addr); }, - &Repr::PrefixInformation(PrefixInformation { + Repr::PrefixInformation(PrefixInformation { prefix_len, flags, valid_lifetime, preferred_lifetime, prefix }) => { @@ -528,7 +528,7 @@ impl<'a> Repr<'a> { opt.set_preferred_lifetime(preferred_lifetime); opt.set_prefix(prefix); }, - &Repr::RedirectedHeader(RedirectedHeader { + Repr::RedirectedHeader(RedirectedHeader { header, data }) => { let data_len = data.len() / 8; @@ -541,12 +541,12 @@ impl<'a> Repr<'a> { let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; payload.copy_from_slice(&data[..data_len]); } - &Repr::Mtu(mtu) => { + Repr::Mtu(mtu) => { opt.set_option_type(Type::Mtu); opt.set_data_len(1); opt.set_mtu(mtu); } - &Repr::Unknown { type_: id, length, data } => { + Repr::Unknown { type_: id, length, data } => { opt.set_option_type(Type::Unknown(id)); opt.set_data_len(length); opt.data_mut().copy_from_slice(data); @@ -558,29 +558,29 @@ impl<'a> Repr<'a> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "NDISC Option: ")?; - match self { - &Repr::SourceLinkLayerAddr(addr) => { + match *self { + Repr::SourceLinkLayerAddr(addr) => { write!(f, "SourceLinkLayer addr={}", addr) }, - &Repr::TargetLinkLayerAddr(addr) => { + Repr::TargetLinkLayerAddr(addr) => { write!(f, "TargetLinkLayer addr={}", addr) }, - &Repr::PrefixInformation(PrefixInformation { + Repr::PrefixInformation(PrefixInformation { prefix, prefix_len, .. }) => { write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) }, - &Repr::RedirectedHeader(RedirectedHeader { + Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { write!(f, "RedirectedHeader header={}", header) }, - &Repr::Mtu(mtu) => { + Repr::Mtu(mtu) => { write!(f, "MTU mtu={}", mtu) }, - &Repr::Unknown { type_: id, length, .. } => { + Repr::Unknown { type_: id, length, .. } => { write!(f, "Unknown({}) length={}", id, length) } } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 8242b366d..3f51ef9d1 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -645,28 +645,28 @@ impl<'a> TcpOption<'a> { } pub fn buffer_len(&self) -> usize { - match self { - &TcpOption::EndOfList => 1, - &TcpOption::NoOperation => 1, - &TcpOption::MaxSegmentSize(_) => 4, - &TcpOption::WindowScale(_) => 3, - &TcpOption::SackPermitted => 2, - &TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2, - &TcpOption::Unknown { data, .. } => 2 + data.len() + match *self { + TcpOption::EndOfList => 1, + TcpOption::NoOperation => 1, + TcpOption::MaxSegmentSize(_) => 4, + TcpOption::WindowScale(_) => 3, + TcpOption::SackPermitted => 2, + TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2, + TcpOption::Unknown { data, .. } => 2 + data.len() } } pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] { let length; - match self { - &TcpOption::EndOfList => { + match *self { + TcpOption::EndOfList => { length = 1; // There may be padding space which also should be initialized. for p in buffer.iter_mut() { *p = field::OPT_END; } } - &TcpOption::NoOperation => { + TcpOption::NoOperation => { length = 1; buffer[0] = field::OPT_NOP; } From 23bb12a8566f7da46855c02f3f4460529f5c4098 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Oct 2020 13:22:46 +0200 Subject: [PATCH 028/566] Async/await waker support. --- .github/workflows/test.yml | 4 +- Cargo.toml | 4 +- src/socket/icmp.rs | 69 ++++++++++++++++++++++++++++++++-- src/socket/mod.rs | 5 +++ src/socket/raw.rs | 58 ++++++++++++++++++++++++++++- src/socket/tcp.rs | 76 +++++++++++++++++++++++++++++++++++++- src/socket/udp.rs | 69 ++++++++++++++++++++++++++++++++-- src/socket/waker.rs | 33 +++++++++++++++++ 8 files changed, 307 insertions(+), 11 deletions(-) create mode 100644 src/socket/waker.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a82118792..498e606cf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,7 @@ jobs: - std ethernet proto-ipv6 socket-icmp socket-tcp # Test features chosen to be as aggressive as possible. - - std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + - std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async include: # Test alloc feature which requires nightly. @@ -66,7 +66,7 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp + - ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 1f8f2e89b..63a9c047f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,12 +42,14 @@ ethernet = [] "socket-udp" = [] "socket-tcp" = [] "socket-icmp" = [] +"async" = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "ethernet", "phy-raw_socket", "phy-tap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp" + "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", + "async" ] # experimental; do not use; no guarantees provided that this feature will be kept diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index c4364a2a5..72bb8ab9e 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -5,6 +5,10 @@ use phy::{ChecksumCapabilities, DeviceCapabilities}; use socket::{Socket, SocketMeta, SocketHandle, PollAt}; use storage::{PacketBuffer, PacketMetadata}; use wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; +#[cfg(feature = "async")] +use socket::WakerRegistration; +#[cfg(feature = "async")] +use core::task::Waker; #[cfg(feature = "proto-ipv4")] use wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr}; @@ -61,7 +65,11 @@ pub struct IcmpSocket<'a, 'b: 'a> { /// The endpoint this socket is communicating with endpoint: Endpoint, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. - hop_limit: Option + hop_limit: Option, + #[cfg(feature = "async")] + rx_waker: WakerRegistration, + #[cfg(feature = "async")] + tx_waker: WakerRegistration, } impl<'a, 'b> IcmpSocket<'a, 'b> { @@ -73,10 +81,49 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { rx_buffer: rx_buffer, tx_buffer: tx_buffer, endpoint: Endpoint::default(), - hop_limit: None + hop_limit: None, + #[cfg(feature = "async")] + rx_waker: WakerRegistration::new(), + #[cfg(feature = "async")] + tx_waker: WakerRegistration::new(), } } + /// Register a waker for receive operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `recv` method calls, such as receiving data, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_recv_waker(&mut self, waker: &Waker) { + self.rx_waker.register(waker) + } + + /// Register a waker for send operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `send` method calls, such as space becoming available in the transmit + /// buffer, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_send_waker(&mut self, waker: &Waker) { + self.tx_waker.register(waker) + } + /// Return the socket handle. #[inline] pub fn handle(&self) -> SocketHandle { @@ -174,6 +221,13 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { if self.is_open() { return Err(Error::Illegal) } self.endpoint = endpoint; + + #[cfg(feature = "async")] + { + self.rx_waker.wake(); + self.tx_waker.wake(); + } + Ok(()) } @@ -339,6 +393,10 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { self.meta.handle, icmp_repr.buffer_len(), packet_buf.len()); }, } + + #[cfg(feature = "async")] + self.rx_waker.wake(); + Ok(()) } @@ -380,7 +438,12 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { }, _ => Err(Error::Unaddressable) } - }) + })?; + + #[cfg(feature = "async")] + self.tx_waker.wake(); + + Ok(()) } pub(crate) fn poll_at(&self) -> PollAt { diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 531a062bc..d9bf454ba 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -26,7 +26,12 @@ mod tcp; mod set; mod ref_; +#[cfg(feature = "async")] +mod waker; + pub(crate) use self::meta::Meta as SocketMeta; +#[cfg(feature = "async")] +pub(crate) use self::waker::WakerRegistration; #[cfg(feature = "socket-raw")] pub use self::raw::{RawPacketMetadata, diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 15df21148..6eee47ce4 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -9,6 +9,10 @@ use wire::{IpVersion, IpRepr, IpProtocol}; use wire::{Ipv4Repr, Ipv4Packet}; #[cfg(feature = "proto-ipv6")] use wire::{Ipv6Repr, Ipv6Packet}; +#[cfg(feature = "async")] +use socket::WakerRegistration; +#[cfg(feature = "async")] +use core::task::Waker; /// A UDP packet metadata. pub type RawPacketMetadata = PacketMetadata<()>; @@ -27,6 +31,10 @@ pub struct RawSocket<'a, 'b: 'a> { ip_protocol: IpProtocol, rx_buffer: RawSocketBuffer<'a, 'b>, tx_buffer: RawSocketBuffer<'a, 'b>, + #[cfg(feature = "async")] + rx_waker: WakerRegistration, + #[cfg(feature = "async")] + tx_waker: WakerRegistration, } impl<'a, 'b> RawSocket<'a, 'b> { @@ -41,9 +49,48 @@ impl<'a, 'b> RawSocket<'a, 'b> { ip_protocol, rx_buffer, tx_buffer, + #[cfg(feature = "async")] + rx_waker: WakerRegistration::new(), + #[cfg(feature = "async")] + tx_waker: WakerRegistration::new(), } } + /// Register a waker for receive operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `recv` method calls, such as receiving data, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_recv_waker(&mut self, waker: &Waker) { + self.rx_waker.register(waker) + } + + /// Register a waker for send operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `send` method calls, such as space becoming available in the transmit + /// buffer, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_send_waker(&mut self, waker: &Waker) { + self.tx_waker.register(waker) + } + /// Return the socket handle. #[inline] pub fn handle(&self) -> SocketHandle { @@ -171,6 +218,10 @@ impl<'a, 'b> RawSocket<'a, 'b> { net_trace!("{}:{}:{}: receiving {} octets", self.meta.handle, self.ip_version, self.ip_protocol, packet_buf.len()); + + #[cfg(feature = "async")] + self.rx_waker.wake(); + Ok(()) } @@ -228,7 +279,12 @@ impl<'a, 'b> RawSocket<'a, 'b> { Ok(()) } } - }) + })?; + + #[cfg(feature = "async")] + self.tx_waker.wake(); + + Ok(()) } pub(crate) fn poll_at(&self) -> PollAt { diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 23961d455..7b66ca067 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -10,6 +10,10 @@ use time::{Duration, Instant}; use socket::{Socket, SocketMeta, SocketHandle, PollAt}; use storage::{Assembler, RingBuffer}; use wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl}; +#[cfg(feature = "async")] +use socket::WakerRegistration; +#[cfg(feature = "async")] +use core::task::Waker; /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -248,6 +252,12 @@ pub struct TcpSocket<'a> { /// The number of packets recived directly after /// each other which have the same ACK number. local_rx_dup_acks: u8, + + #[cfg(feature = "async")] + rx_waker: WakerRegistration, + #[cfg(feature = "async")] + tx_waker: WakerRegistration, + } const DEFAULT_MSS: usize = 536; @@ -298,9 +308,49 @@ impl<'a> TcpSocket<'a> { local_rx_last_ack: None, local_rx_last_seq: None, local_rx_dup_acks: 0, + + #[cfg(feature = "async")] + rx_waker: WakerRegistration::new(), + #[cfg(feature = "async")] + tx_waker: WakerRegistration::new(), } } + /// Register a waker for receive operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `recv` method calls, such as receiving data, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_recv_waker(&mut self, waker: &Waker) { + self.rx_waker.register(waker) + } + + /// Register a waker for send operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `send` method calls, such as space becoming available in the transmit + /// buffer, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_send_waker(&mut self, waker: &Waker) { + self.tx_waker.register(waker) + } + /// Return the socket handle. #[inline] pub fn handle(&self) -> SocketHandle { @@ -438,6 +488,12 @@ impl<'a> TcpSocket<'a> { self.remote_win_shift = rx_cap_log2.saturating_sub(16) as u8; self.remote_mss = DEFAULT_MSS; self.remote_last_ts = None; + + #[cfg(feature = "async")] + { + self.rx_waker.wake(); + self.tx_waker.wake(); + } } /// Start listening on the given endpoint. @@ -825,7 +881,17 @@ impl<'a> TcpSocket<'a> { self.state, state); } } - self.state = state + + self.state = state; + + #[cfg(feature = "async")] + { + // Wake all tasks waiting. Even if we haven't received/sent data, this + // is needed because return values of functions may change depending on the state. + // For example, a pending read has to fail with an error if the socket is closed. + self.rx_waker.wake(); + self.tx_waker.wake(); + } } pub(crate) fn reply(ip_repr: &IpRepr, repr: &TcpRepr) -> (IpRepr, TcpRepr<'static>) { @@ -1283,6 +1349,10 @@ impl<'a> TcpSocket<'a> { self.meta.handle, self.local_endpoint, self.remote_endpoint, ack_len, self.tx_buffer.len() - ack_len); self.tx_buffer.dequeue_allocated(ack_len); + + // There's new room available in tx_buffer, wake the waiting task if any. + #[cfg(feature = "async")] + self.tx_waker.wake(); } if let Some(ack_number) = repr.ack_number { @@ -1367,6 +1437,10 @@ impl<'a> TcpSocket<'a> { self.meta.handle, self.local_endpoint, self.remote_endpoint, contig_len, self.rx_buffer.len() + contig_len); self.rx_buffer.enqueue_unallocated(contig_len); + + // There's new data in rx_buffer, notify waiting task if any. + #[cfg(feature = "async")] + self.rx_waker.wake(); } if !self.assembler.is_empty() { diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 7818c0fcd..10996923d 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -4,6 +4,10 @@ use {Error, Result}; use socket::{Socket, SocketMeta, SocketHandle, PollAt}; use storage::{PacketBuffer, PacketMetadata}; use wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr}; +#[cfg(feature = "async")] +use socket::WakerRegistration; +#[cfg(feature = "async")] +use core::task::Waker; /// A UDP packet metadata. pub type UdpPacketMetadata = PacketMetadata; @@ -22,7 +26,11 @@ pub struct UdpSocket<'a, 'b: 'a> { rx_buffer: UdpSocketBuffer<'a, 'b>, tx_buffer: UdpSocketBuffer<'a, 'b>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. - hop_limit: Option + hop_limit: Option, + #[cfg(feature = "async")] + rx_waker: WakerRegistration, + #[cfg(feature = "async")] + tx_waker: WakerRegistration, } impl<'a, 'b> UdpSocket<'a, 'b> { @@ -34,10 +42,49 @@ impl<'a, 'b> UdpSocket<'a, 'b> { endpoint: IpEndpoint::default(), rx_buffer: rx_buffer, tx_buffer: tx_buffer, - hop_limit: None + hop_limit: None, + #[cfg(feature = "async")] + rx_waker: WakerRegistration::new(), + #[cfg(feature = "async")] + tx_waker: WakerRegistration::new(), } } + /// Register a waker for receive operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `recv` method calls, such as receiving data, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_recv_waker(&mut self, waker: &Waker) { + self.rx_waker.register(waker) + } + + /// Register a waker for send operations. + /// + /// The waker is woken on state changes that might affect the return value + /// of `send` method calls, such as space becoming available in the transmit + /// buffer, or the socket closing. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has + /// necessarily changed. + #[cfg(feature = "async")] + pub fn register_send_waker(&mut self, waker: &Waker) { + self.tx_waker.register(waker) + } + /// Return the socket handle. #[inline] pub fn handle(&self) -> SocketHandle { @@ -89,6 +136,13 @@ impl<'a, 'b> UdpSocket<'a, 'b> { if self.is_open() { return Err(Error::Illegal) } self.endpoint = endpoint; + + #[cfg(feature = "async")] + { + self.rx_waker.wake(); + self.tx_waker.wake(); + } + Ok(()) } @@ -234,6 +288,10 @@ impl<'a, 'b> UdpSocket<'a, 'b> { net_trace!("{}:{}:{}: receiving {} octets", self.meta.handle, self.endpoint, endpoint, size); + + #[cfg(feature = "async")] + self.rx_waker.wake(); + Ok(()) } @@ -261,7 +319,12 @@ impl<'a, 'b> UdpSocket<'a, 'b> { hop_limit: hop_limit, }; emit((ip_repr, repr)) - }) + })?; + + #[cfg(feature = "async")] + self.tx_waker.wake(); + + Ok(()) } pub(crate) fn poll_at(&self) -> PollAt { diff --git a/src/socket/waker.rs b/src/socket/waker.rs new file mode 100644 index 000000000..4dfb44b4e --- /dev/null +++ b/src/socket/waker.rs @@ -0,0 +1,33 @@ +use core::task::Waker; + +/// Utility struct to register and wake a waker. +#[derive(Debug)] +pub struct WakerRegistration { + waker: Option, +} + +impl WakerRegistration { + pub const fn new() -> Self { + Self { waker: None } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + // In all other cases + // - we have no waker registered + // - we have a waker registered but it's for a different task. + // then clone the new waker and store it + _ => self.waker = Some(w.clone()), + } + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + self.waker.take().map(|w| w.wake()); + } +} \ No newline at end of file From 1e40b934bf4a6c29998efb1addf7d5b8ea91d8d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Dec 2020 00:11:30 +0100 Subject: [PATCH 029/566] Update to Rust 2018. Fixes #271 --- CODE_STYLE.md | 4 +- Cargo.toml | 1 + benches/bench.rs | 3 - examples/benchmark.rs | 10 +-- examples/client.rs | 8 +-- examples/dhcp_client.rs | 6 -- examples/httpclient.rs | 10 +-- examples/loopback.rs | 12 +--- examples/multicast.rs | 9 +-- examples/ping.rs | 13 ++-- examples/server.rs | 8 +-- examples/tcpdump.rs | 2 - examples/utils.rs | 2 +- src/dhcp/clientv4.rs | 14 ++--- src/iface/ethernet.rs | 116 +++++++++++++++++------------------ src/iface/neighbor.rs | 6 +- src/iface/route.rs | 10 +-- src/lib.rs | 16 +---- src/macros.rs | 16 ++--- src/parsers.rs | 32 +++++----- src/phy/fault_injector.rs | 6 +- src/phy/fuzz_injector.rs | 6 +- src/phy/loopback.rs | 6 +- src/phy/mod.rs | 4 +- src/phy/pcap_writer.rs | 6 +- src/phy/raw_socket.rs | 6 +- src/phy/sys/mod.rs | 2 +- src/phy/tap_interface.rs | 6 +- src/phy/tracer.rs | 8 +-- src/socket/icmp.rs | 32 +++++----- src/socket/meta.rs | 6 +- src/socket/mod.rs | 2 +- src/socket/raw.rs | 29 ++++----- src/socket/ref_.rs | 8 +-- src/socket/set.rs | 4 +- src/socket/tcp.rs | 24 ++++---- src/socket/udp.rs | 22 +++---- src/storage/packet_buffer.rs | 4 +- src/storage/ring_buffer.rs | 4 +- src/wire/arp.rs | 8 +-- src/wire/dhcpv4.rs | 10 +-- src/wire/ethernet.rs | 6 +- src/wire/icmp.rs | 4 +- src/wire/icmpv4.rs | 12 ++-- src/wire/icmpv6.rs | 18 +++--- src/wire/igmp.rs | 12 ++-- src/wire/ip.rs | 20 +++--- src/wire/ipv4.rs | 12 ++-- src/wire/ipv6.rs | 25 ++++---- src/wire/ipv6fragment.rs | 4 +- src/wire/ipv6hopbyhop.rs | 6 +- src/wire/ipv6option.rs | 4 +- src/wire/ipv6routing.rs | 8 +-- src/wire/mld.rs | 12 ++-- src/wire/ndisc.rs | 21 ++++--- src/wire/ndiscoption.rs | 17 ++--- src/wire/tcp.rs | 14 ++--- src/wire/udp.rs | 14 ++--- utils/packet2pcap.rs | 3 - 59 files changed, 324 insertions(+), 389 deletions(-) diff --git a/CODE_STYLE.md b/CODE_STYLE.md index 902e20f44..4cf2ca2a3 100644 --- a/CODE_STYLE.md +++ b/CODE_STYLE.md @@ -16,8 +16,8 @@ to most specific, but it's not very important. ```rust use core::cell::RefCell; -use {Error, Result}; -use phy::{self, DeviceCapabilities, Device}; +use crate::{Error, Result}; +use crate::phy::{self, DeviceCapabilities, Device}; ``` ## Wrapping function calls diff --git a/Cargo.toml b/Cargo.toml index 63a9c047f..c6f1130bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "smoltcp" version = "0.6.0" +edition = "2018" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." documentation = "https://docs.rs/smoltcp/" diff --git a/benches/bench.rs b/benches/bench.rs index 4aed8ce22..b1ddc8528 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,8 +1,5 @@ #![feature(test)] -extern crate test; -extern crate smoltcp; - mod wire { use test; #[cfg(feature = "proto-ipv6")] diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 3e78ac234..92d8c460b 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -1,11 +1,3 @@ -#[cfg(feature = "log")] -#[macro_use] -extern crate log; -#[cfg(feature = "log")] -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; - mod utils; use std::cmp; @@ -15,6 +7,8 @@ use std::thread; use std::io::{Read, Write}; use std::net::TcpStream; use std::os::unix::io::AsRawFd; +use log::debug; + use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; diff --git a/examples/client.rs b/examples/client.rs index a9aaf653d..58340aa08 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,14 +1,10 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; - mod utils; use std::str::{self, FromStr}; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; +use log::debug; + use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 0695c40e1..a751e59f3 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -1,9 +1,3 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; - mod utils; use std::collections::BTreeMap; diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 7ac9ef71d..d2d8f402b 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -1,17 +1,11 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate rand; -extern crate url; -extern crate smoltcp; - mod utils; use std::str::{self, FromStr}; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use url::Url; +use log::debug; + use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; diff --git a/examples/loopback.rs b/examples/loopback.rs index afc33fa20..a291aa4b3 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -1,21 +1,13 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(unused_mut)] -#[cfg(feature = "std")] -use std as core; -#[macro_use] -extern crate log; -extern crate smoltcp; -#[cfg(feature = "std")] -extern crate env_logger; -#[cfg(feature = "std")] -extern crate getopts; - #[cfg(feature = "std")] #[allow(dead_code)] mod utils; use core::str; +use log::{info, debug, error}; + use smoltcp::phy::Loopback; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; diff --git a/examples/multicast.rs b/examples/multicast.rs index 81a606f44..92f1876cd 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -1,14 +1,9 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; -extern crate byteorder; - mod utils; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; +use log::debug; + use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address, Ipv4Packet, IgmpPacket, IgmpRepr}; diff --git a/examples/ping.rs b/examples/ping.rs index bdbf96e6c..411e450c4 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -1,16 +1,13 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; -extern crate byteorder; - mod utils; use std::str::FromStr; use std::collections::BTreeMap; use std::cmp; use std::os::unix::io::AsRawFd; +use std::collections::HashMap; +use log::debug; +use byteorder::{ByteOrder, NetworkEndian}; + use smoltcp::time::{Duration, Instant}; use smoltcp::phy::Device; use smoltcp::phy::wait as phy_wait; @@ -19,8 +16,6 @@ use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Icmpv4Repr, Icmpv4Packet}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; -use std::collections::HashMap; -use byteorder::{ByteOrder, NetworkEndian}; macro_rules! send_icmp_ping { ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, diff --git a/examples/server.rs b/examples/server.rs index db4d14b06..ae53b6c80 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,15 +1,11 @@ -#[macro_use] -extern crate log; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; - mod utils; use std::str; use std::collections::BTreeMap; use std::fmt::Write; use std::os::unix::io::AsRawFd; +use log::debug; + use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; diff --git a/examples/tcpdump.rs b/examples/tcpdump.rs index 166ac1e50..5ca8ec6de 100644 --- a/examples/tcpdump.rs +++ b/examples/tcpdump.rs @@ -1,5 +1,3 @@ -extern crate smoltcp; - use std::env; use std::os::unix::io::AsRawFd; use smoltcp::phy::wait as phy_wait; diff --git a/examples/utils.rs b/examples/utils.rs index f6b94e05a..09d43d477 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -9,7 +9,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use std::env; use std::process; #[cfg(feature = "log")] -use log::{Level, LevelFilter}; +use log::{Level, LevelFilter, trace}; #[cfg(feature = "log")] use env_logger::Builder; use getopts::{Options, Matches}; diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index e7f01a699..0828d1f88 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -1,13 +1,13 @@ -use {Result, Error}; -use wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, +use crate::{Result, Error}; +use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, Ipv4Cidr, Ipv4Address, Ipv4Packet, Ipv4Repr, UdpPacket, UdpRepr, DhcpPacket, DhcpRepr, DhcpMessageType}; -use wire::dhcpv4::field as dhcpv4_field; -use socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; -use phy::{Device, ChecksumCapabilities}; -use iface::EthernetInterface as Interface; -use time::{Instant, Duration}; +use crate::wire::dhcpv4::field as dhcpv4_field; +use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; +use crate::phy::{Device, ChecksumCapabilities}; +use crate::iface::EthernetInterface as Interface; +use crate::time::{Instant, Duration}; use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT}; const DISCOVER_TIMEOUT: u64 = 10; diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 263a7f1a9..71e609417 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -7,50 +7,50 @@ use managed::{ManagedSlice, ManagedMap}; #[cfg(not(feature = "proto-igmp"))] use core::marker::PhantomData; -use {Error, Result}; -use phy::{Device, DeviceCapabilities, RxToken, TxToken}; -use time::{Duration, Instant}; -use wire::pretty_print::PrettyPrinter; -use wire::{EthernetAddress, EthernetProtocol, EthernetFrame}; -use wire::{IpAddress, IpProtocol, IpRepr, IpCidr}; +use crate::{Error, Result}; +use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken}; +use crate::time::{Duration, Instant}; +use crate::wire::pretty_print::PrettyPrinter; +use crate::wire::{EthernetAddress, EthernetProtocol, EthernetFrame}; +use crate::wire::{IpAddress, IpProtocol, IpRepr, IpCidr}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, IPV6_MIN_MTU}; +use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, IPV6_MIN_MTU}; #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; +use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; #[cfg(feature = "proto-ipv4")] -use wire::{ArpPacket, ArpRepr, ArpOperation}; +use crate::wire::{ArpPacket, ArpRepr, ArpOperation}; #[cfg(feature = "proto-ipv4")] -use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable}; +use crate::wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable}; #[cfg(feature = "proto-igmp")] -use wire::{IgmpPacket, IgmpRepr, IgmpVersion}; +use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; #[cfg(feature = "proto-ipv6")] -use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; +use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use wire::IcmpRepr; +use crate::wire::IcmpRepr; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6HopByHopHeader, Ipv6HopByHopRepr}; +use crate::wire::{Ipv6HopByHopHeader, Ipv6HopByHopRepr}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6OptionRepr, Ipv6OptionFailureType}; +use crate::wire::{Ipv6OptionRepr, Ipv6OptionFailureType}; #[cfg(feature = "proto-ipv6")] -use wire::{NdiscNeighborFlags, NdiscRepr}; +use crate::wire::{NdiscNeighborFlags, NdiscRepr}; #[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))] -use wire::Icmpv6DstUnreachable; +use crate::wire::Icmpv6DstUnreachable; #[cfg(feature = "socket-udp")] -use wire::{UdpPacket, UdpRepr}; +use crate::wire::{UdpPacket, UdpRepr}; #[cfg(feature = "socket-tcp")] -use wire::{TcpPacket, TcpRepr, TcpControl}; +use crate::wire::{TcpPacket, TcpRepr, TcpControl}; -use socket::{Socket, SocketSet, AnySocket, PollAt}; +use crate::socket::{Socket, SocketSet, AnySocket, PollAt}; #[cfg(feature = "socket-raw")] -use socket::RawSocket; +use crate::socket::RawSocket; #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use socket::IcmpSocket; +use crate::socket::IcmpSocket; #[cfg(feature = "socket-udp")] -use socket::UdpSocket; +use crate::socket::UdpSocket; #[cfg(feature = "socket-tcp")] -use socket::TcpSocket; -use super::{NeighborCache, NeighborAnswer}; -use super::Routes; +use crate::socket::TcpSocket; +use crate::iface::{NeighborCache, NeighborAnswer}; +use crate::iface::Routes; /// An Ethernet network interface. /// @@ -1722,37 +1722,37 @@ mod test { #[cfg(feature = "proto-igmp")] use std::vec::Vec; use std::collections::BTreeMap; - use {Result, Error}; + use crate::{Result, Error}; use super::InterfaceBuilder; - use iface::{NeighborCache, EthernetInterface}; - use phy::{self, Loopback, ChecksumCapabilities}; + use crate::iface::{NeighborCache, EthernetInterface}; + use crate::phy::{self, Loopback, ChecksumCapabilities}; #[cfg(feature = "proto-igmp")] - use phy::{Device, RxToken, TxToken}; - use time::Instant; - use socket::SocketSet; + use crate::phy::{Device, RxToken, TxToken}; + use crate::time::Instant; + use crate::socket::SocketSet; #[cfg(feature = "proto-ipv4")] - use wire::{ArpOperation, ArpPacket, ArpRepr}; - use wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; - use wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; + use crate::wire::{ArpOperation, ArpPacket, ArpRepr}; + use crate::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; + use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; #[cfg(feature = "proto-ipv4")] - use wire::{Ipv4Address, Ipv4Repr}; + use crate::wire::{Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-igmp")] - use wire::Ipv4Packet; + use crate::wire::Ipv4Packet; #[cfg(feature = "proto-ipv4")] - use wire::{Icmpv4Repr, Icmpv4DstUnreachable}; + use crate::wire::{Icmpv4Repr, Icmpv4DstUnreachable}; #[cfg(feature = "proto-igmp")] - use wire::{IgmpPacket, IgmpRepr, IgmpVersion}; + use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; #[cfg(all(feature = "socket-udp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - use wire::{UdpPacket, UdpRepr}; + use crate::wire::{UdpPacket, UdpRepr}; #[cfg(feature = "proto-ipv6")] - use wire::{Ipv6Address, Ipv6Repr}; + use crate::wire::{Ipv6Address, Ipv6Repr}; #[cfg(feature = "proto-ipv6")] - use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; + use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; #[cfg(feature = "proto-ipv6")] - use wire::{NdiscNeighborFlags, NdiscRepr}; + use crate::wire::{NdiscNeighborFlags, NdiscRepr}; #[cfg(feature = "proto-ipv6")] - use wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr}; + use crate::wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr}; use super::Packet; @@ -2003,8 +2003,8 @@ mod test { #[test] #[cfg(feature = "socket-udp")] fn test_handle_udp_broadcast() { - use socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; - use wire::IpEndpoint; + use crate::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; + use crate::wire::IpEndpoint; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; @@ -2075,7 +2075,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv4")] fn test_handle_ipv4_broadcast() { - use wire::{Ipv4Packet, Icmpv4Repr, Icmpv4Packet}; + use crate::wire::{Ipv4Packet, Icmpv4Repr, Icmpv4Packet}; let (mut iface, mut socket_set) = create_loopback(); @@ -2134,11 +2134,11 @@ mod test { #[cfg(feature = "socket-udp")] fn test_icmp_reply_size() { #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - use wire::IPV4_MIN_MTU as MIN_MTU; + use crate::wire::IPV4_MIN_MTU as MIN_MTU; #[cfg(feature = "proto-ipv6")] - use wire::Icmpv6DstUnreachable; + use crate::wire::Icmpv6DstUnreachable; #[cfg(feature = "proto-ipv6")] - use wire::IPV6_MIN_MTU as MIN_MTU; + use crate::wire::IPV6_MIN_MTU as MIN_MTU; #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] const MAX_PAYLOAD_LEN: usize = 528; @@ -2371,8 +2371,8 @@ mod test { #[test] #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] fn test_icmpv4_socket() { - use socket::{IcmpSocket, IcmpEndpoint, IcmpSocketBuffer, IcmpPacketMetadata}; - use wire::Icmpv4Packet; + use crate::socket::{IcmpSocket, IcmpEndpoint, IcmpSocketBuffer, IcmpPacketMetadata}; + use crate::wire::Icmpv4Packet; let (iface, mut socket_set) = create_loopback(); @@ -2613,8 +2613,8 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_no_reply() { - use socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; - use wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + use crate::socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; + use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let (mut iface, mut socket_set) = create_loopback(); @@ -2669,8 +2669,8 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_truncated_packet() { - use socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; - use wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + use crate::socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; + use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let (mut iface, mut socket_set) = create_loopback(); @@ -2730,9 +2730,9 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] fn test_raw_socket_with_udp_socket() { - use socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata, + use crate::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata, RawSocket, RawSocketBuffer, RawPacketMetadata}; - use wire::{IpVersion, IpEndpoint, Ipv4Packet, UdpPacket, UdpRepr}; + use crate::wire::{IpVersion, IpEndpoint, Ipv4Packet, UdpPacket, UdpRepr}; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 71a64405c..8f2193a8a 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -3,8 +3,8 @@ use managed::ManagedMap; -use wire::{EthernetAddress, IpAddress}; -use time::{Duration, Instant}; +use crate::wire::{EthernetAddress, IpAddress}; +use crate::time::{Duration, Instant}; /// A cached neighbor. /// @@ -194,7 +194,7 @@ impl<'a> Cache<'a> { mod test { use super::*; use std::collections::BTreeMap; - use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4}; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4}; const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]); diff --git a/src/iface/route.rs b/src/iface/route.rs index ad3e6d4e0..2ddc5fd6c 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -1,13 +1,13 @@ use managed::ManagedMap; -use time::Instant; +use crate::time::Instant; use core::ops::Bound; -use {Error, Result}; -use wire::{IpCidr, IpAddress}; +use crate::{Error, Result}; +use crate::wire::{IpCidr, IpAddress}; #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Address, Ipv4Cidr}; +use crate::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6Address, Ipv6Cidr}; +use crate::wire::{Ipv6Address, Ipv6Cidr}; /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index 94612dd1a..a25dae5ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ -#![cfg_attr(feature = "alloc", feature(alloc))] -#![no_std] +#![cfg_attr(not(any(test, feature = "std")), no_std)] #![deny(unsafe_code)] #![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "ethernet"), deny(unused))] @@ -91,21 +90,8 @@ compile_error!("at least one socket needs to be enabled"); */ // FIXME(dlrobertson): clippy fails with this lint #![cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))] -#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))] -#[macro_use] -extern crate bitflags; -extern crate byteorder; -extern crate managed; -#[cfg(any(test, feature = "std"))] -#[macro_use] -extern crate std; -#[cfg(any(feature = "phy-raw_socket", feature = "phy-tap_interface"))] -extern crate libc; #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "log")] -#[macro_use(trace, debug)] -extern crate log; use core::fmt; diff --git a/src/macros.rs b/src/macros.rs index eb51d03a3..2c1da7f64 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,18 +1,14 @@ + #[cfg(feature = "log")] -#[macro_use] -mod log { - macro_rules! net_log { - (trace, $($arg:expr),*) => { trace!($($arg),*); }; - (debug, $($arg:expr),*) => { debug!($($arg),*); }; - } +macro_rules! net_log { + (trace, $($arg:expr),*) => { log::trace!($($arg),*); }; + (debug, $($arg:expr),*) => { log::debug!($($arg),*); }; } #[cfg(not(feature = "log"))] #[macro_use] -mod log { - macro_rules! net_log { - ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* } - } +macro_rules! net_log { + ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* } } macro_rules! net_trace { diff --git a/src/parsers.rs b/src/parsers.rs index e6452633b..4c6b93682 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -4,12 +4,12 @@ use core::str::FromStr; use core::result; #[cfg(feature = "ethernet")] -use wire::EthernetAddress; -use wire::{IpAddress, IpCidr, IpEndpoint}; +use crate::wire::EthernetAddress; +use crate::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Address, Ipv4Cidr}; +use crate::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6Address, Ipv6Cidr}; +use crate::wire::{Ipv6Address, Ipv6Cidr}; type Result = result::Result; @@ -44,7 +44,7 @@ impl<'a> Parser<'a> { } } - fn try(&mut self, f: F) -> Option + fn try_do(&mut self, f: F) -> Option where F: FnOnce(&mut Parser<'a>) -> Result { let pos = self.pos; match f(self) { @@ -103,7 +103,7 @@ impl<'a> Parser<'a> { hex: bool) -> Result { let mut value = self.accept_digit(hex)? as u32; for _ in 1..max_digits { - match self.try(|p| p.accept_digit(hex)) { + match self.try_do(|p| p.accept_digit(hex)) { Some(digit) => { value *= if hex { 16 } else { 10 }; value += digit as u32; @@ -132,10 +132,10 @@ impl<'a> Parser<'a> { #[cfg(feature = "ethernet")] fn accept_mac(&mut self) -> Result { - if let Some(mac) = self.try(|p| p.accept_mac_joined_with(b'-')) { + if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) { return Ok(mac) } - if let Some(mac) = self.try(|p| p.accept_mac_joined_with(b':')) { + if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) { return Ok(mac) } Err(()) @@ -157,7 +157,7 @@ impl<'a> Parser<'a> { fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]), (head_idx, tail_idx): (&mut usize, &mut usize), mut use_tail: bool, is_cidr: bool) -> Result<()> { - let double_colon = match self.try(|p| p.accept_str(b"::")) { + let double_colon = match self.try_do(|p| p.accept_str(b"::")) { Some(_) if !use_tail && *head_idx < 7 => { // Found a double colon. Start filling out the // tail and set the double colon flag in case @@ -180,14 +180,14 @@ impl<'a> Parser<'a> { } }; - match self.try(|p| p.accept_number(4, 0x10000, true)) { + match self.try_do(|p| p.accept_number(4, 0x10000, true)) { Some(part) if !use_tail && *head_idx < 8 => { // Valid u16 to be added to the address head[*head_idx] = part as u16; *head_idx += 1; if *head_idx == 6 && head[0..*head_idx] == [0, 0, 0, 0, 0, 0xffff] { - self.try(|p| { + self.try_do(|p| { p.accept_char(b':')?; p.accept_ipv4_mapped_ipv6_part(head, head_idx) }); @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] { - self.try(|p| { + self.try_do(|p| { p.accept_char(b':')?; p.accept_ipv4_mapped_ipv6_part(tail, tail_idx) }); @@ -287,13 +287,13 @@ impl<'a> Parser<'a> { fn accept_ip(&mut self) -> Result { #[cfg(feature = "proto-ipv4")] - match self.try(|p| p.accept_ipv4()) { + match self.try_do(|p| p.accept_ipv4()) { Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)), None => () } #[cfg(feature = "proto-ipv6")] - match self.try(|p| p.accept_ipv6(false)) { + match self.try_do(|p| p.accept_ipv6(false)) { Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)), None => () } @@ -333,13 +333,13 @@ impl<'a> Parser<'a> { fn accept_ip_endpoint(&mut self) -> Result { #[cfg(feature = "proto-ipv4")] - match self.try(|p| p.accept_ipv4_endpoint()) { + match self.try_do(|p| p.accept_ipv4_endpoint()) { Some(ipv4) => return Ok(ipv4), None => () } #[cfg(feature = "proto-ipv6")] - match self.try(|p| p.accept_ipv6_endpoint()) { + match self.try_do(|p| p.accept_ipv6_endpoint()) { Some(ipv6) => return Ok(ipv6), None => () } diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index c5e2c4b96..5f79fec1a 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; -use {Error, Result}; -use phy::{self, DeviceCapabilities, Device}; -use time::{Duration, Instant}; +use crate::{Error, Result}; +use crate::phy::{self, DeviceCapabilities, Device}; +use crate::time::{Duration, Instant}; // We use our own RNG to stay compatible with #![no_std]. // The use of the RNG below has a slight bias, but it doesn't matter. diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 83a6e4251..9f11b1772 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -1,6 +1,6 @@ -use Result; -use phy::{self, DeviceCapabilities, Device}; -use time::Instant; +use crate::Result; +use crate::phy::{self, DeviceCapabilities, Device}; +use crate::time::Instant; // This could be fixed once associated consts are stable. const MTU: usize = 1536; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index 7db99aaa3..b4e1f522c 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -9,9 +9,9 @@ use alloc::collections::VecDeque; #[cfg(all(feature = "alloc", feature = "rust-1_28"))] use alloc::VecDeque; -use Result; -use phy::{self, Device, DeviceCapabilities}; -use time::Instant; +use crate::Result; +use crate::phy::{self, Device, DeviceCapabilities}; +use crate::time::Instant; /// A loopback device. #[derive(Debug)] diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 47146b432..8ef699141 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -84,8 +84,8 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { ``` */ -use Result; -use time::Instant; +use crate::Result; +use crate::time::Instant; #[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] mod sys; diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 53d7559c5..cb4b9ece2 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -4,9 +4,9 @@ use std::cell::RefCell; use std::io::Write; use byteorder::{ByteOrder, NativeEndian}; -use Result; -use phy::{self, DeviceCapabilities, Device}; -use time::Instant; +use crate::Result; +use crate::phy::{self, DeviceCapabilities, Device}; +use crate::time::Instant; enum_with_unknown! { /// Captured packet header type. diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 231d3d171..3764551cf 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -4,9 +4,9 @@ use std::rc::Rc; use std::io; use std::os::unix::io::{RawFd, AsRawFd}; -use Result; -use phy::{self, sys, DeviceCapabilities, Device}; -use time::Instant; +use crate::Result; +use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::time::Instant; /// A socket that captures or transmits the complete frame. #[derive(Debug)] diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 508eb1abf..cfc9a88fb 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -3,7 +3,7 @@ use libc; use std::{mem, ptr, io}; use std::os::unix::io::RawFd; -use time::Duration; +use crate::time::Duration; #[cfg(target_os = "linux")] #[path = "linux.rs"] diff --git a/src/phy/tap_interface.rs b/src/phy/tap_interface.rs index dfdfb27b4..95202ab9e 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tap_interface.rs @@ -4,9 +4,9 @@ use std::rc::Rc; use std::io; use std::os::unix::io::{RawFd, AsRawFd}; -use Result; -use phy::{self, sys, DeviceCapabilities, Device}; -use time::Instant; +use crate::Result; +use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::time::Instant; /// A virtual Ethernet interface. #[derive(Debug)] diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index b1598adbd..22a2288f1 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -1,7 +1,7 @@ -use Result; -use wire::pretty_print::{PrettyPrint, PrettyPrinter}; -use phy::{self, DeviceCapabilities, Device}; -use time::Instant; +use crate::Result; +use crate::wire::pretty_print::{PrettyPrint, PrettyPrinter}; +use crate::phy::{self, DeviceCapabilities, Device}; +use crate::time::Instant; /// A tracer device. /// diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 72bb8ab9e..2afd8b87a 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -1,21 +1,21 @@ use core::cmp; - -use {Error, Result}; -use phy::{ChecksumCapabilities, DeviceCapabilities}; -use socket::{Socket, SocketMeta, SocketHandle, PollAt}; -use storage::{PacketBuffer, PacketMetadata}; -use wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; -#[cfg(feature = "async")] -use socket::WakerRegistration; #[cfg(feature = "async")] use core::task::Waker; +use crate::{Error, Result}; +use crate::phy::{ChecksumCapabilities, DeviceCapabilities}; +use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; +use crate::storage::{PacketBuffer, PacketMetadata}; +#[cfg(feature = "async")] +use crate::socket::WakerRegistration; + #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr}; +use crate::wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr}; -use wire::IcmpRepr; -use wire::{UdpPacket, UdpRepr}; +use crate::wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr}; +use crate::wire::IcmpRepr; +use crate::wire::{UdpPacket, UdpRepr}; +use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; /// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for /// more details. @@ -463,8 +463,8 @@ impl<'a, 'b> Into> for IcmpSocket<'a, 'b> { #[cfg(test)] mod tests_common { - pub use phy::DeviceCapabilities; - pub use wire::IpAddress; + pub use crate::phy::DeviceCapabilities; + pub use crate::wire::IpAddress; pub use super::*; pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static, 'static> { @@ -489,7 +489,7 @@ mod tests_common { mod test_ipv4 { use super::tests_common::*; - use wire::Icmpv4DstUnreachable; + use crate::wire::Icmpv4DstUnreachable; const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); @@ -687,7 +687,7 @@ mod test_ipv4 { mod test_ipv6 { use super::tests_common::*; - use wire::Icmpv6DstUnreachable; + use crate::wire::Icmpv6DstUnreachable; const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); diff --git a/src/socket/meta.rs b/src/socket/meta.rs index 28537bfbc..b4e9fbb26 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -1,6 +1,6 @@ -use wire::IpAddress; -use super::{SocketHandle, PollAt}; -use time::{Duration, Instant}; +use crate::wire::IpAddress; +use crate::socket::{SocketHandle, PollAt}; +use crate::time::{Duration, Instant}; /// Neighbor dependency. /// diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d9bf454ba..24afb5a02 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -12,7 +12,7 @@ size for a buffer, allocate it, and let the networking stack use it. */ use core::marker::PhantomData; -use time::Instant; +use crate::time::Instant; mod meta; #[cfg(feature = "socket-raw")] diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 6eee47ce4..56b66d513 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -1,18 +1,19 @@ use core::cmp::min; +#[cfg(feature = "async")] +use core::task::Waker; + +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; +use crate::storage::{PacketBuffer, PacketMetadata}; +#[cfg(feature = "async")] +use crate::socket::WakerRegistration; -use {Error, Result}; -use phy::ChecksumCapabilities; -use socket::{Socket, SocketMeta, SocketHandle, PollAt}; -use storage::{PacketBuffer, PacketMetadata}; -use wire::{IpVersion, IpRepr, IpProtocol}; +use crate::wire::{IpVersion, IpRepr, IpProtocol}; #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Repr, Ipv4Packet}; +use crate::wire::{Ipv4Repr, Ipv4Packet}; #[cfg(feature = "proto-ipv6")] -use wire::{Ipv6Repr, Ipv6Packet}; -#[cfg(feature = "async")] -use socket::WakerRegistration; -#[cfg(feature = "async")] -use core::task::Waker; +use crate::wire::{Ipv6Repr, Ipv6Packet}; /// A UDP packet metadata. pub type RawPacketMetadata = PacketMetadata<()>; @@ -304,11 +305,11 @@ impl<'a, 'b> Into> for RawSocket<'a, 'b> { #[cfg(test)] mod test { - use wire::IpRepr; + use crate::wire::IpRepr; #[cfg(feature = "proto-ipv4")] - use wire::{Ipv4Address, Ipv4Repr}; + use crate::wire::{Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] - use wire::{Ipv6Address, Ipv6Repr}; + use crate::wire::{Ipv6Address, Ipv6Repr}; use super::*; fn buffer(packets: usize) -> RawSocketBuffer<'static, 'static> { diff --git a/src/socket/ref_.rs b/src/socket/ref_.rs index db3205c84..7d57ccf0d 100644 --- a/src/socket/ref_.rs +++ b/src/socket/ref_.rs @@ -1,13 +1,13 @@ use core::ops::{Deref, DerefMut}; #[cfg(feature = "socket-raw")] -use socket::RawSocket; +use crate::socket::RawSocket; #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use socket::IcmpSocket; +use crate::socket::IcmpSocket; #[cfg(feature = "socket-udp")] -use socket::UdpSocket; +use crate::socket::UdpSocket; #[cfg(feature = "socket-tcp")] -use socket::TcpSocket; +use crate::socket::TcpSocket; /// A trait for tracking a socket usage session. /// diff --git a/src/socket/set.rs b/src/socket/set.rs index 477b8a2d7..fd6d53826 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -1,9 +1,9 @@ use core::{fmt, slice}; use managed::ManagedSlice; -use super::{Socket, SocketRef, AnySocket}; +use crate::socket::{Socket, SocketRef, AnySocket}; #[cfg(feature = "socket-tcp")] -use super::TcpState; +use crate::socket::TcpState; /// An item of a socket set. /// diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 7b66ca067..d50042814 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -3,18 +3,18 @@ // a new feature. use core::{cmp, fmt, mem}; - -use {Error, Result}; -use phy::DeviceCapabilities; -use time::{Duration, Instant}; -use socket::{Socket, SocketMeta, SocketHandle, PollAt}; -use storage::{Assembler, RingBuffer}; -use wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl}; -#[cfg(feature = "async")] -use socket::WakerRegistration; #[cfg(feature = "async")] use core::task::Waker; +use crate::{Error, Result}; +use crate::phy::DeviceCapabilities; +use crate::time::{Duration, Instant}; +use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; +use crate::storage::{Assembler, RingBuffer}; +#[cfg(feature = "async")] +use crate::socket::WakerRegistration; +use crate::wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl}; + /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -1802,8 +1802,8 @@ impl<'a> fmt::Write for TcpSocket<'a> { mod test { use core::i32; use std::vec::Vec; - use wire::{IpAddress, IpRepr, IpCidr}; - use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; + use crate::wire::{IpAddress, IpRepr, IpCidr}; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; use super::*; // =========================================================================================// @@ -1950,8 +1950,6 @@ mod test { #[cfg(feature = "log")] fn init_logger() { - extern crate log; - struct Logger; static LOGGER: Logger = Logger; diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 10996923d..caf8cb4cc 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -1,14 +1,14 @@ use core::cmp::min; - -use {Error, Result}; -use socket::{Socket, SocketMeta, SocketHandle, PollAt}; -use storage::{PacketBuffer, PacketMetadata}; -use wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr}; -#[cfg(feature = "async")] -use socket::WakerRegistration; #[cfg(feature = "async")] use core::task::Waker; +use crate::{Error, Result}; +use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; +use crate::storage::{PacketBuffer, PacketMetadata}; +use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr}; +#[cfg(feature = "async")] +use crate::socket::WakerRegistration; + /// A UDP packet metadata. pub type UdpPacketMetadata = PacketMetadata; @@ -344,12 +344,12 @@ impl<'a, 'b> Into> for UdpSocket<'a, 'b> { #[cfg(test)] mod test { - use wire::{IpAddress, IpRepr, UdpRepr}; + use crate::wire::{IpAddress, IpRepr, UdpRepr}; #[cfg(feature = "proto-ipv4")] - use wire::Ipv4Repr; + use crate::wire::Ipv4Repr; #[cfg(feature = "proto-ipv6")] - use wire::Ipv6Repr; - use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; + use crate::wire::Ipv6Repr; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; use super::*; fn buffer(packets: usize) -> UdpSocketBuffer<'static, 'static> { diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 3bec8f1ac..ab9159617 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -1,7 +1,7 @@ use managed::ManagedSlice; -use {Error, Result}; -use super::RingBuffer; +use crate::{Error, Result}; +use crate::storage::RingBuffer; /// Size and header of a packet. #[derive(Debug, Clone, Copy)] diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index cf8244ffc..9b70b0298 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -4,8 +4,8 @@ use core::cmp; use managed::ManagedSlice; -use {Error, Result}; -use super::Resettable; +use crate::{Error, Result}; +use crate::storage::Resettable; /// A ring buffer. /// diff --git a/src/wire/arp.rs b/src/wire/arp.rs index b96d45522..150adbf7c 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -1,7 +1,7 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; +use crate::{Error, Result}; pub use super::EthernetProtocol as Protocol; @@ -29,7 +29,7 @@ pub struct Packet> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; pub const HTYPE: Field = 0..2; pub const PTYPE: Field = 2..4; @@ -248,7 +248,7 @@ impl> AsRef<[u8]> for Packet { } } -use super::{EthernetAddress, Ipv4Address}; +use crate::wire::{EthernetAddress, Ipv4Address}; /// A high-level representation of an Address Resolution Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -356,7 +356,7 @@ impl fmt::Display for Repr { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 7062c9c54..af51f57f8 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -2,9 +2,9 @@ use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use super::{EthernetAddress, Ipv4Address}; -use super::arp::Hardware; +use crate::{Error, Result}; +use crate::wire::{EthernetAddress, Ipv4Address}; +use crate::wire::arp::Hardware; const DHCP_MAGIC_NUMBER: u32 = 0x63825363; @@ -199,7 +199,7 @@ pub(crate) mod field { #![allow(non_snake_case)] #![allow(unused)] - use wire::field::*; + use crate::wire::field::*; pub const OP: usize = 0; pub const HTYPE: usize = 1; @@ -836,7 +836,7 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use wire::Ipv4Address; + use crate::wire::Ipv4Address; use super::*; const MAGIC_COOKIE: u32 = 0x63825363; diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 4b69caffc..449454a9e 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -1,7 +1,7 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; +use crate::{Error, Result}; enum_with_unknown! { /// Ethernet protocol type. @@ -83,7 +83,7 @@ pub struct Frame> { } mod field { - use wire::field::*; + use crate::wire::field::*; pub const DESTINATION: Field = 0..6; pub const SOURCE: Field = 6..12; @@ -209,7 +209,7 @@ impl> fmt::Display for Frame { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Frame { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, diff --git a/src/wire/icmp.rs b/src/wire/icmp.rs index 684ea5e29..cf8400c0a 100644 --- a/src/wire/icmp.rs +++ b/src/wire/icmp.rs @@ -1,7 +1,7 @@ #[cfg(feature = "proto-ipv4")] -use super::icmpv4; +use crate::wire::icmpv4; #[cfg(feature = "proto-ipv6")] -use super::icmpv6; +use crate::wire::icmpv6; #[derive(Clone, PartialEq, Eq, Debug)] pub enum Repr<'a> { diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 0d71fbc5b..fae318a52 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -1,10 +1,10 @@ use core::{cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use phy::ChecksumCapabilities; -use super::ip::checksum; -use super::{Ipv4Packet, Ipv4Repr}; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::wire::ip::checksum; +use crate::wire::{Ipv4Packet, Ipv4Repr}; enum_with_unknown! { /// Internet protocol control message type. @@ -172,7 +172,7 @@ pub struct Packet> { } mod field { - use wire::field::*; + use crate::wire::field::*; pub const TYPE: usize = 0; pub const CODE: usize = 1; @@ -531,7 +531,7 @@ impl<'a> fmt::Display for Repr<'a> { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index d2c3cbc0e..c44ae5d6b 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -1,13 +1,13 @@ use core::{cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use phy::ChecksumCapabilities; -use super::ip::checksum; -use super::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; -use super::MldRepr; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::wire::ip::checksum; +use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; +use crate::wire::MldRepr; #[cfg(feature = "ethernet")] -use super::NdiscRepr; +use crate::wire::NdiscRepr; enum_with_unknown! { /// Internet protocol control message type. @@ -197,7 +197,7 @@ pub struct Packet> { // Ranges and constants describing key boundaries in the ICMPv6 header. pub(super) mod field { - use wire::field::*; + use crate::wire::field::*; // ICMPv6: See https://tools.ietf.org/html/rfc4443 pub const TYPE: usize = 0; @@ -739,8 +739,8 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use wire::{Ipv6Address, Ipv6Repr, IpProtocol}; - use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; + use crate::wire::{Ipv6Address, Ipv6Repr, IpProtocol}; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; use super::*; static ECHO_PACKET_BYTES: [u8; 12] = diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 64ad6577b..8fac53534 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -1,11 +1,11 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use super::ip::checksum; -use time::Duration; +use crate::{Error, Result}; +use crate::wire::ip::checksum; +use crate::time::Duration; -use wire::Ipv4Address; +use crate::wire::Ipv4Address; enum_with_unknown! { /// Internet Group Management Protocol v1/v2 message version/type. @@ -28,7 +28,7 @@ pub struct Packet> { } mod field { - use wire::field::*; + use crate::wire::field::*; pub const TYPE: usize = 0; pub const MAX_RESP_CODE: usize = 1; @@ -355,7 +355,7 @@ impl<'a> fmt::Display for Repr { } } -use super::pretty_print::{PrettyIndent, PrettyPrint}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, diff --git a/src/wire/ip.rs b/src/wire/ip.rs index bdc843c41..d003a1516 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -1,12 +1,12 @@ use core::fmt; use core::convert::From; -use {Error, Result}; -use phy::ChecksumCapabilities; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; #[cfg(feature = "proto-ipv4")] -use super::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr}; +use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] -use super::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; +use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] @@ -862,16 +862,16 @@ pub mod checksum { } } -use super::pretty_print::PrettyIndent; +use crate::wire::pretty_print::PrettyIndent; pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &mut PrettyIndent, ip_repr: T, payload: &[u8]) -> fmt::Result { #[cfg(feature = "proto-ipv4")] - use wire::Icmpv4Packet; + use crate::wire::Icmpv4Packet; #[cfg(feature = "proto-ipv4")] use super::pretty_print::PrettyPrint; - use wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr}; - use wire::ip::checksum::format_checksum; + use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr}; + use crate::wire::ip::checksum::format_checksum; let checksum_caps = ChecksumCapabilities::ignored(); let repr = ip_repr.into(); @@ -953,9 +953,9 @@ pub(crate) mod test { use super::*; - use wire::{IpAddress, IpProtocol,IpCidr}; + use crate::wire::{IpAddress, IpProtocol,IpCidr}; #[cfg(feature = "proto-ipv4")] - use wire::{Ipv4Address, Ipv4Repr}; + use crate::wire::{Ipv4Address, Ipv4Repr}; macro_rules! generate_common_tests { ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path, diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index c2360cb9c..aab7e610a 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -1,9 +1,9 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use phy::ChecksumCapabilities; -use super::ip::{checksum, pretty_print_ip_payload}; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::wire::ip::{checksum, pretty_print_ip_payload}; pub use super::IpProtocol as Protocol; @@ -231,7 +231,7 @@ pub struct Packet> { } mod field { - use wire::field::*; + use crate::wire::field::*; pub const VER_IHL: usize = 0; pub const DSCP_ECN: usize = 1; @@ -662,12 +662,12 @@ impl fmt::Display for Repr { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, indent: &mut PrettyIndent) -> fmt::Result { - use wire::ip::checksum::format_checksum; + use crate::wire::ip::checksum::format_checksum; let checksum_caps = ChecksumCapabilities::ignored(); diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 89b1196a3..8d93cd814 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -3,9 +3,12 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; +use crate::{Error, Result}; +use crate::wire::ip::pretty_print_ip_payload; +#[cfg(feature = "proto-ipv4")] +use crate::wire::ipv4; + pub use super::IpProtocol as Protocol; -use super::ip::pretty_print_ip_payload; /// Minimum MTU required of all links supporting IPv6. See [RFC 8200 § 5]. /// @@ -144,9 +147,9 @@ impl Address { #[cfg(feature = "proto-ipv4")] /// Convert an IPv4 mapped IPv6 address to an IPv4 address. - pub fn as_ipv4(&self) -> Option<::wire::ipv4::Address> { + pub fn as_ipv4(&self) -> Option { if self.is_ipv4_mapped() { - Some(::wire::ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15])) + Some(ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15])) } else { None } @@ -254,8 +257,8 @@ impl fmt::Display for Address { #[cfg(feature = "proto-ipv4")] /// Convert the given IPv4 address into a IPv4-mapped IPv6 address -impl From<::wire::ipv4::Address> for Address { - fn from(address: ::wire::ipv4::Address) -> Self { +impl From for Address { + fn from(address: ipv4::Address) -> Self { let octets = address.0; Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3]]) @@ -356,7 +359,7 @@ pub struct Packet> { // // See https://tools.ietf.org/html/rfc2460#section-3 for details. mod field { - use wire::field::*; + use crate::wire::field::*; // 4-bit version number, 8-bit traffic class, and the // 20-bit flow label. pub const VER_TC_FLOW: Field = 0..4; @@ -647,7 +650,7 @@ impl fmt::Display for Repr { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; // TODO: This is very similar to the implementation for IPv4. Make // a way to have less copy and pasted code here. @@ -673,13 +676,13 @@ impl> PrettyPrint for Packet { #[cfg(test)] mod test { - use Error; + use crate::Error; use super::{Address, Cidr}; use super::{Packet, Protocol, Repr}; - use wire::pretty_print::{PrettyPrinter}; + use crate::wire::pretty_print::{PrettyPrinter}; #[cfg(feature = "proto-ipv4")] - use wire::ipv4::Address as Ipv4Address; + use crate::wire::ipv4::Address as Ipv4Address; static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index b5b641fe2..93a29b014 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -1,5 +1,5 @@ use core::fmt; -use {Error, Result}; +use crate::{Error, Result}; use byteorder::{ByteOrder, NetworkEndian}; @@ -21,7 +21,7 @@ pub struct Header> { // // See https://tools.ietf.org/html/rfc8200#section-4.5 for details. mod field { - use wire::field::*; + use crate::wire::field::*; // 8-bit identifier of the header immediately following this header. pub const NXT_HDR: usize = 0; diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 3eb115fe9..6327ceaed 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -1,7 +1,7 @@ use core::fmt; -use {Error, Result}; +use crate::{Error, Result}; -use super::ipv6option::Ipv6OptionsIterator; +use crate::wire::ipv6option::Ipv6OptionsIterator; pub use super::IpProtocol as Protocol; /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. @@ -27,7 +27,7 @@ pub struct Header> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; // Minimum size of the header. pub const MIN_HEADER_SIZE: usize = 8; diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 1cf4dee41..d96969c99 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -1,5 +1,5 @@ use core::fmt; -use {Error, Result}; +use crate::{Error, Result}; enum_with_unknown! { /// IPv6 Extension Header Option Type @@ -73,7 +73,7 @@ pub struct Ipv6Option> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; // 8-bit identifier of the type of option. pub const TYPE: usize = 0; diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 7956b4773..d66793e4d 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -1,8 +1,8 @@ use core::fmt; -use {Error, Result}; +use crate::{Error, Result}; -use super::IpProtocol as Protocol; -use super::Ipv6Address as Address; +use crate::wire::IpProtocol as Protocol; +use crate::wire::Ipv6Address as Address; enum_with_unknown! { /// IPv6 Extension Routing Header Routing Type @@ -72,7 +72,7 @@ pub struct Header> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; // Minimum size of the header. pub const MIN_HEADER_SIZE: usize = 4; diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 926829399..1abbba9f9 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -6,9 +6,9 @@ use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use super::icmpv6::{field, Message, Packet}; -use super::Ipv6Address; +use crate::{Error, Result}; +use crate::wire::icmpv6::{field, Message, Packet}; +use crate::wire::Ipv6Address; enum_with_unknown! { /// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for @@ -383,9 +383,9 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use phy::ChecksumCapabilities; - use wire::Icmpv6Repr; - use wire::icmpv6::Message; + use crate::phy::ChecksumCapabilities; + use crate::wire::Icmpv6Repr; + use crate::wire::icmpv6::Message; use super::*; static QUERY_PACKET_BYTES: [u8; 44] = diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index afaf0cee9..20544b255 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -1,12 +1,13 @@ use byteorder::{ByteOrder, NetworkEndian}; +use bitflags::bitflags; -use {Error, Result}; -use super::icmpv6::{field, Message, Packet}; -use wire::{EthernetAddress, Ipv6Repr, Ipv6Packet}; -use wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; -use wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; -use time::Duration; -use super::Ipv6Address; +use crate::{Error, Result}; +use crate::wire::icmpv6::{field, Message, Packet}; +use crate::wire::{EthernetAddress, Ipv6Repr, Ipv6Packet}; +use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; +use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; +use crate::time::Duration; +use crate::wire::Ipv6Address; bitflags! { pub struct RouterFlags: u8 { @@ -462,10 +463,10 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use phy::ChecksumCapabilities; + use crate::phy::ChecksumCapabilities; use super::*; - use wire::Icmpv6Repr; - use wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; + use crate::wire::Icmpv6Repr; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; static ROUTER_ADVERT_BYTES: [u8; 24] = [0x86, 0x00, 0xa9, 0xde, diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 0175f44f1..88b22f027 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -1,9 +1,10 @@ use core::fmt; use byteorder::{NetworkEndian, ByteOrder}; +use bitflags::bitflags; -use {Error, Result}; -use time::Duration; -use wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; +use crate::{Error, Result}; +use crate::time::Duration; +use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; enum_with_unknown! { /// NDISC Option Type @@ -61,7 +62,7 @@ pub struct NdiscOption> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; // 8-bit identifier of the type of option. pub const TYPE: usize = 0; @@ -587,7 +588,7 @@ impl<'a> fmt::Display for Repr<'a> { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for NdiscOption { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, @@ -608,9 +609,9 @@ impl> PrettyPrint for NdiscOption { #[cfg(test)] mod test { - use Error; - use time::Duration; - use wire::{EthernetAddress, Ipv6Address}; + use crate::Error; + use crate::time::Duration; + use crate::wire::{EthernetAddress, Ipv6Address}; use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr}; static PREFIX_OPT_BYTES: [u8; 32] = [ diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 3f51ef9d1..205e4f0f2 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -1,10 +1,10 @@ use core::{i32, ops, cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use phy::ChecksumCapabilities; -use super::{IpProtocol, IpAddress}; -use super::ip::checksum; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::wire::{IpProtocol, IpAddress}; +use crate::wire::ip::checksum; /// A TCP sequence number. /// @@ -74,7 +74,7 @@ pub struct Packet> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; pub const SRC_PORT: Field = 0..2; pub const DST_PORT: Field = 2..4; @@ -1009,7 +1009,7 @@ impl<'a> fmt::Display for Repr<'a> { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, @@ -1024,7 +1024,7 @@ impl> PrettyPrint for Packet { #[cfg(test)] mod test { #[cfg(feature = "proto-ipv4")] - use wire::Ipv4Address; + use crate::wire::Ipv4Address; use super::*; #[cfg(feature = "proto-ipv4")] diff --git a/src/wire/udp.rs b/src/wire/udp.rs index b309f6309..44bb578bf 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -1,10 +1,10 @@ use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; -use {Error, Result}; -use phy::ChecksumCapabilities; -use super::{IpProtocol, IpAddress}; -use super::ip::checksum; +use crate::{Error, Result}; +use crate::phy::ChecksumCapabilities; +use crate::wire::{IpProtocol, IpAddress}; +use crate::wire::ip::checksum; /// A read/write wrapper around an User Datagram Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] @@ -15,7 +15,7 @@ pub struct Packet> { mod field { #![allow(non_snake_case)] - use wire::field::*; + use crate::wire::field::*; pub const SRC_PORT: Field = 0..2; pub const DST_PORT: Field = 2..4; @@ -272,7 +272,7 @@ impl<'a> fmt::Display for Repr<'a> { } } -use super::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; impl> PrettyPrint for Packet { fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, @@ -287,7 +287,7 @@ impl> PrettyPrint for Packet { #[cfg(test)] mod test { #[cfg(feature = "proto-ipv4")] - use wire::Ipv4Address; + use crate::wire::Ipv4Address; use super::*; #[cfg(feature = "proto-ipv4")] diff --git a/utils/packet2pcap.rs b/utils/packet2pcap.rs index b82118678..89b990636 100644 --- a/utils/packet2pcap.rs +++ b/utils/packet2pcap.rs @@ -1,6 +1,3 @@ -extern crate smoltcp; -extern crate getopts; - use std::cell::RefCell; use std::io::{self, Read, Write}; use std::path::Path; From 2219af77b295c49de1fb79f07c0da7a90b695f1d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Dec 2020 18:29:48 +0100 Subject: [PATCH 030/566] tcp: fix racey simultaneous close not sending FIN. --- src/socket/tcp.rs | 85 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 11 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index d50042814..564e0d0b4 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1484,6 +1484,7 @@ impl<'a> TcpSocket<'a> { // Do we have to send a FIN? let want_fin = match self.state { State::FinWait1 => true, + State::Closing => true, State::LastAck => true, _ => false, }; @@ -1633,9 +1634,8 @@ impl<'a> TcpSocket<'a> { } // We transmit data in all states where we may have data in the buffer, - // or the transmit half of the connection is still open: - // the ESTABLISHED, FIN-WAIT-1, CLOSE-WAIT and LAST-ACK states. - State::Established | State::FinWait1 | State::CloseWait | State::LastAck => { + // or the transmit half of the connection is still open. + State::Established | State::FinWait1 | State::Closing | State::CloseWait | State::LastAck => { // Extract as much data as the remote side can receive in this packet // from the transmit buffer. let offset = self.remote_last_seq - self.local_seq_no; @@ -1647,7 +1647,7 @@ impl<'a> TcpSocket<'a> { // flags, depending on whether the transmit half of the connection is open. if offset + repr.payload.len() == self.tx_buffer.len() { match self.state { - State::FinWait1 | State::LastAck => + State::FinWait1 | State::LastAck | State::Closing => repr.control = TcpControl::Fin, State::Established | State::CloseWait if repr.payload.len() > 0 => repr.control = TcpControl::Psh, @@ -1656,13 +1656,8 @@ impl<'a> TcpSocket<'a> { } } - // We do not transmit data in the FIN-WAIT-2 state, but we may transmit - // ACKs for incoming data. - State::FinWait2 => {} - - // We do not transmit data or control flags in the CLOSING or TIME-WAIT states, - // but we may retransmit an ACK. - State::Closing | State::TimeWait => () + // In FIN-WAIT-2 and TIME-WAIT states we may only transmit ACKs for incoming data or FIN + State::FinWait2 | State::TimeWait => {} } // There might be more than one reason to send a packet. E.g. the keep-alive timer @@ -3489,6 +3484,74 @@ mod test { }]); } + #[test] + fn test_simultaneous_close_raced() { + let mut s = socket_established(); + s.close(); + assert_eq!(s.state, State::FinWait1); + + // Socket receives FIN before it has a chance to send its own FIN + send!(s, TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::Closing); + + // FIN + ack-of-FIN + recv!(s, [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }]); + assert_eq!(s.state, State::Closing); + + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::TimeWait); + recv!(s, []); + } + + #[test] + fn test_simultaneous_close_raced_with_data() { + let mut s = socket_established(); + s.send_slice(b"abcdef").unwrap(); + s.close(); + assert_eq!(s.state, State::FinWait1); + + // Socket receives FIN before it has a chance to send its own data+FIN + send!(s, TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::Closing); + + // data + FIN + ack-of-FIN + recv!(s, [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }]); + assert_eq!(s.state, State::Closing); + + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::TimeWait); + recv!(s, []); + } + #[test] fn test_fin_with_data() { let mut s = socket_established(); From df6704ce9c9544ab0c6076a5d8d9fc23932ae15d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 19 Apr 2020 02:59:56 +0200 Subject: [PATCH 031/566] Remove "None" variant from Packet, use Option instead. --- src/iface/ethernet.rs | 151 +++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 71e609417..f4d2bbe81 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -269,7 +269,6 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> #[derive(Debug, PartialEq)] enum Packet<'a> { - None, #[cfg(feature = "proto-ipv4")] Arp(ArpRepr), #[cfg(feature = "proto-ipv4")] @@ -289,7 +288,6 @@ enum Packet<'a> { impl<'a> Packet<'a> { fn neighbor_addr(&self) -> Option { match *self { - Packet::None => None, #[cfg(feature = "proto-ipv4")] Packet::Arp(_) => None, #[cfg(feature = "proto-ipv4")] @@ -562,10 +560,15 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> err }).and_then(|response| { processed_any = true; - inner.dispatch(tx_token, timestamp, response).map_err(|err| { - net_debug!("cannot dispatch response packet: {}", err); - err - }) + match response { + Some(packet) => { + inner.dispatch(tx_token, timestamp, packet).map_err(|err| { + net_debug!("cannot dispatch response packet: {}", err); + err + }) + } + None => Ok(()) + } }) })?; } @@ -768,7 +771,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ethernet<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) -> - Result> + Result>> { let eth_frame = EthernetFrame::new_checked(frame)?; @@ -777,7 +780,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { !eth_frame.dst_addr().is_multicast() && eth_frame.dst_addr() != self.ethernet_addr { - return Ok(Packet::None) + return Ok(None) } match eth_frame.ethertype() { @@ -798,7 +801,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn process_arp<'frame, T: AsRef<[u8]>> (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result> + Result>> { let arp_packet = ArpPacket::new_checked(eth_frame.payload())?; let arp_repr = ArpRepr::parse(&arp_packet)?; @@ -821,15 +824,15 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { - Ok(Packet::Arp(ArpRepr::EthernetIpv4 { + Ok(Some(Packet::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: self.ethernet_addr, source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr - })) + }))) } else { - Ok(Packet::None) + Ok(None) } } @@ -863,7 +866,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ipv6<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result> + Result>> { let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; @@ -900,7 +903,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_nxt_hdr<'frame> (&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) - -> Result> + -> Result>> { match nxt_hdr { IpProtocol::Icmpv6 => @@ -919,7 +922,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "socket-raw")] _ if handled_by_raw_socket => - Ok(Packet::None), + Ok(None), _ => { // Send back as much of the original payload as we can. @@ -941,7 +944,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ipv4<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result> + Result>> { let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -975,12 +978,12 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { // 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 { - return Ok(Packet::None); + return Ok(None); } else if match self.routes.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), timestamp) { Some(router_addr) => !self.has_ip_addr(router_addr), None => true, } { - return Ok(Packet::None); + return Ok(None); } } @@ -1001,7 +1004,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { self.process_tcp(sockets, timestamp, ip_repr, ip_payload), _ if handled_by_raw_socket => - Ok(Packet::None), + Ok(None), _ => { // Send back as much of the original payload as we can. @@ -1024,7 +1027,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { /// 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, - ip_payload: &'frame [u8]) -> Result> { + ip_payload: &'frame [u8]) -> Result>> { let igmp_packet = IgmpPacket::new_checked(ip_payload)?; let igmp_repr = IgmpRepr::parse(&igmp_packet)?; @@ -1068,12 +1071,12 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { IgmpRepr::LeaveGroup{ .. } => (), } - Ok(Packet::None) + Ok(None) } #[cfg(feature = "proto-ipv6")] fn process_icmpv6<'frame>(&mut self, _sockets: &mut SocketSet, timestamp: Instant, - ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result> + ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -1114,18 +1117,18 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } // Ignore any echo replies. - Icmpv6Repr::EchoReply { .. } => Ok(Packet::None), + Icmpv6Repr::EchoReply { .. } => Ok(None), // Forward any NDISC packets to the ndisc packet handler Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(timestamp, ipv6_repr, repr), - _ => Ok(Packet::None) + _ => Ok(None) }, // Don't report an error if a packet with unknown type // has been handled by an ICMP socket #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => Ok(Packet::None), + _ if handled_by_icmp_socket => Ok(None), // FIXME: do something correct here? _ => Err(Error::Unrecognized), @@ -1134,8 +1137,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv6")] fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, - repr: NdiscRepr<'frame>) -> Result> { - let packet = match repr { + repr: NdiscRepr<'frame>) -> Result>> { + match repr { NdiscRepr::NeighborAdvert { lladdr, target_addr, flags } => { let ip_addr = ip_repr.src_addr.into(); match lladdr { @@ -1150,7 +1153,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { }, _ => (), } - Ok(Packet::None) + Ok(None) } NdiscRepr::NeighborSolicit { target_addr, lladdr, .. } => { match lladdr { @@ -1172,20 +1175,19 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { hop_limit: 0xff, payload_len: advert.buffer_len() }; - Ok(Packet::Icmpv6((ip_repr, advert))) + Ok(Some(Packet::Icmpv6((ip_repr, advert)))) } else { - Ok(Packet::None) + Ok(None) } } - _ => Ok(Packet::None) - }; - packet + _ => Ok(None) + } } #[cfg(feature = "proto-ipv6")] fn process_hopbyhop<'frame>(&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, - ip_payload: &'frame [u8]) -> Result> + ip_payload: &'frame [u8]) -> Result>> { let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?; let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?; @@ -1197,7 +1199,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match Ipv6OptionFailureType::from(type_) { Ipv6OptionFailureType::Skip => (), Ipv6OptionFailureType::Discard => { - return Ok(Packet::None); + return Ok(None); }, _ => { // FIXME(dlrobertson): Send an ICMPv6 parameter problem message @@ -1215,7 +1217,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr, - ip_payload: &'frame [u8]) -> Result> + ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -1254,12 +1256,12 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { }, // Ignore any echo replies. - Icmpv4Repr::EchoReply { .. } => Ok(Packet::None), + Icmpv4Repr::EchoReply { .. } => Ok(None), // Don't report an error if a packet with unknown type // has been handled by an ICMP socket #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => Ok(Packet::None), + _ if handled_by_icmp_socket => Ok(None), // FIXME: do something correct here? _ => Err(Error::Unrecognized), @@ -1269,11 +1271,11 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn icmpv4_reply<'frame, 'icmp: 'frame> (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) -> - Packet<'frame> + Option> { if !ipv4_repr.src_addr.is_unicast() { // Do not send ICMP replies to non-unicast sources - Packet::None + None } else if ipv4_repr.dst_addr.is_unicast() { // Reply as normal when src_addr and dst_addr are both unicast let ipv4_reply_repr = Ipv4Repr { @@ -1283,7 +1285,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Packet::Icmpv4((ipv4_reply_repr, icmp_repr)) + Some(Packet::Icmpv4((ipv4_reply_repr, icmp_repr))) } else if ipv4_repr.dst_addr.is_broadcast() { // Only reply to broadcasts for echo replies and not other ICMP messages match icmp_repr { @@ -1296,21 +1298,21 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Packet::Icmpv4((ipv4_reply_repr, icmp_repr)) + Some(Packet::Icmpv4((ipv4_reply_repr, icmp_repr))) }, - None => Packet::None, + None => None, }, - _ => Packet::None, + _ => None, } } else { - Packet::None + None } } #[cfg(feature = "proto-ipv6")] fn icmpv6_reply<'frame, 'icmp: 'frame> (&self, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>) -> - Packet<'frame> + Option> { if ipv6_repr.dst_addr.is_unicast() { let ipv6_reply_repr = Ipv6Repr { @@ -1320,17 +1322,17 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Packet::Icmpv6((ipv6_reply_repr, icmp_repr)) + Some(Packet::Icmpv6((ipv6_reply_repr, icmp_repr))) } else { // Do not send any ICMP replies to a broadcast destination address. - Packet::None + None } } #[cfg(feature = "socket-udp")] fn process_udp<'frame>(&self, sockets: &mut SocketSet, ip_repr: IpRepr, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) -> - Result> + Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_packet = UdpPacket::new_checked(ip_payload)?; @@ -1342,7 +1344,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match udp_socket.process(&ip_repr, &udp_repr) { // The packet is valid and handled by socket. - Ok(()) => return Ok(Packet::None), + Ok(()) => return Ok(None), // The packet is malformed, or the socket buffer is full. Err(e) => return Err(e) } @@ -1352,10 +1354,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match ip_repr { #[cfg(feature = "proto-ipv4")] IpRepr::Ipv4(_) if handled_by_raw_socket => - Ok(Packet::None), + Ok(None), #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(_) if handled_by_raw_socket => - Ok(Packet::None), + Ok(None), #[cfg(feature = "proto-ipv4")] IpRepr::Ipv4(ipv4_repr) => { let payload_len = icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, @@ -1386,7 +1388,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "socket-tcp")] fn process_tcp<'frame>(&self, sockets: &mut SocketSet, timestamp: Instant, ip_repr: IpRepr, ip_payload: &'frame [u8]) -> - Result> + Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let tcp_packet = TcpPacket::new_checked(ip_payload)?; @@ -1398,7 +1400,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match tcp_socket.process(timestamp, &ip_repr, &tcp_repr) { // The packet is valid and handled by socket. - Ok(reply) => return Ok(reply.map_or(Packet::None, Packet::Tcp)), + Ok(reply) => return Ok(reply.map(Packet::Tcp)), // The packet is malformed, or doesn't match the socket state, // or the socket buffer is full. Err(e) => return Err(e) @@ -1407,10 +1409,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { if tcp_repr.control == TcpControl::Rst { // Never reply to a TCP RST packet with another TCP RST packet. - Ok(Packet::None) + Ok(None) } else { // The packet wasn't handled by a socket, send a TCP RST packet. - Ok(Packet::Tcp(TcpSocket::rst_reply(&ip_repr, &tcp_repr))) + Ok(Some(Packet::Tcp(TcpSocket::rst_reply(&ip_repr, &tcp_repr)))) } } @@ -1499,7 +1501,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { &checksum_caps); }) } - Packet::None => Ok(()) } } @@ -1852,10 +1853,10 @@ mod test { // broadcast address #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(Packet::None)); + Ok(None)); #[cfg(feature = "proto-ipv6")] assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame), - Ok(Packet::None)); + Ok(None)); } #[test] @@ -1913,7 +1914,7 @@ 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), - Ok(expected_repr)); + Ok(Some(expected_repr))); } #[test] @@ -1978,7 +1979,7 @@ 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), - Ok(expected_repr)); + Ok(Some(expected_repr))); let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), @@ -1997,7 +1998,7 @@ mod test { // 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, - false, packet_broadcast.into_inner()), Ok(Packet::None)); + false, packet_broadcast.into_inner()), Ok(None)); } #[test] @@ -2061,7 +2062,7 @@ mod test { // Packet should be handled by bound UDP socket assert_eq!(iface.inner.process_udp(&mut socket_set, ip_repr, false, packet.into_inner()), - Ok(Packet::None)); + Ok(None)); { // Make sure the payload to the UDP packet processed by process_udp is @@ -2127,7 +2128,7 @@ mod test { let expected_packet = Packet::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(expected_packet)); + Ok(Some(expected_packet))); } #[test] @@ -2219,10 +2220,10 @@ mod test { // 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), - Ok(Packet::Icmpv4((expected_ip_repr, expected_icmp_repr)))); + Ok(Some(Packet::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), - Ok(Packet::Icmpv6((expected_ip_repr, expected_icmp_repr)))); + Ok(Some(Packet::Icmpv6((expected_ip_repr, expected_icmp_repr))))); } #[test] @@ -2256,13 +2257,13 @@ mod test { // 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()), - Ok(Packet::Arp(ArpRepr::EthernetIpv4 { + Ok(Some(Packet::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, target_hardware_addr: remote_hw_addr, target_protocol_addr: remote_ip_addr - }))); + })))); // Ensure the address of the requestor was entered in the cache assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0), @@ -2322,7 +2323,7 @@ mod test { // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement assert_eq!(iface.inner.process_ethernet(&mut socket_set, Instant::from_millis(0), frame.into_inner()), - Ok(Packet::Icmpv6((ipv6_expected, icmpv6_expected)))); + Ok(Some(Packet::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), @@ -2359,7 +2360,7 @@ mod test { // 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()), - Ok(Packet::None)); + 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), @@ -2423,7 +2424,7 @@ mod test { ..ipv4_repr }; assert_eq!(iface.inner.process_icmpv4(&mut socket_set, ip_repr, icmp_data), - Ok(Packet::Icmpv4((ipv4_reply, echo_reply)))); + Ok(Some(Packet::Icmpv4((ipv4_reply, echo_reply))))); { let mut socket = socket_set.get::(socket_handle); @@ -2513,7 +2514,7 @@ mod test { // 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), - Ok(Packet::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))); + Ok(Some(Packet::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); // Ensure the address of the requestor was entered in the cache assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0), @@ -2663,7 +2664,7 @@ mod test { }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(Packet::None)); + Ok(None)); } #[test] @@ -2722,7 +2723,7 @@ mod test { // because the packet could not be handled we should send an Icmp message assert!(match frame { - Ok(Packet::Icmpv4(_)) => true, + Ok(Some(Packet::Icmpv4(_))) => true, _ => false, }); } @@ -2795,7 +2796,7 @@ mod test { }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(Packet::None)); + Ok(None)); { // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP From 961c7007ede03f83f0383a8d3c9c98c27b4e19ee Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 4 Jun 2020 02:34:34 +0200 Subject: [PATCH 032/566] Split Packet into EthernetPacket and IpPacket. Functions that only deal with IP packets take/return IpPacket's. IpPacket's are wrapped into EthernetPacket's as late as possible. This will later allow generalizing Interface to handle both Ethernet and pure-IP mediums. --- src/iface/ethernet.rs | 278 ++++++++++++++++++++---------------------- 1 file changed, 132 insertions(+), 146 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index f4d2bbe81..0a9194a91 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -268,9 +268,15 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> } #[derive(Debug, PartialEq)] -enum Packet<'a> { +enum EthernetPacket<'a> { #[cfg(feature = "proto-ipv4")] Arp(ArpRepr), + Ip(IpPacket<'a>), +} + + +#[derive(Debug, PartialEq)] +pub(crate) enum IpPacket<'a> { #[cfg(feature = "proto-ipv4")] Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)), #[cfg(feature = "proto-igmp")] @@ -285,23 +291,67 @@ enum Packet<'a> { Tcp((IpRepr, TcpRepr<'a>)) } -impl<'a> Packet<'a> { - fn neighbor_addr(&self) -> Option { - match *self { +impl<'a> IpPacket<'a> { + pub(crate) fn ip_repr(&self) -> IpRepr { + match self { #[cfg(feature = "proto-ipv4")] - Packet::Arp(_) => None, + IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(ipv4_repr.clone()), + #[cfg(feature = "proto-igmp")] + IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(ipv4_repr.clone()), + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(ipv6_repr.clone()), + #[cfg(feature = "socket-raw")] + IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), + #[cfg(feature = "socket-udp")] + IpPacket::Udp((ip_repr, _)) => ip_repr.clone(), + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), + } + } + + pub(crate) fn emit_payload(&self, _ip_repr: IpRepr, payload: &mut [u8], caps: &DeviceCapabilities) { + match self { #[cfg(feature = "proto-ipv4")] - Packet::Icmpv4((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), + IpPacket::Icmpv4((_, icmpv4_repr)) => + icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum), #[cfg(feature = "proto-igmp")] - Packet::Igmp((ref ipv4_repr, _)) => Some(ipv4_repr.dst_addr.into()), + IpPacket::Igmp((_, igmp_repr)) => + igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)), #[cfg(feature = "proto-ipv6")] - Packet::Icmpv6((ref ipv6_repr, _)) => Some(ipv6_repr.dst_addr.into()), + IpPacket::Icmpv6((_, icmpv6_repr)) => + icmpv6_repr.emit(&_ip_repr.src_addr(), &_ip_repr.dst_addr(), + &mut Icmpv6Packet::new_unchecked(payload), &caps.checksum), #[cfg(feature = "socket-raw")] - Packet::Raw((ref ip_repr, _)) => Some(ip_repr.dst_addr()), + IpPacket::Raw((_, raw_packet)) => + payload.copy_from_slice(raw_packet), #[cfg(feature = "socket-udp")] - Packet::Udp((ref ip_repr, _)) => Some(ip_repr.dst_addr()), + IpPacket::Udp((_, udp_repr)) => + udp_repr.emit(&mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), &_ip_repr.dst_addr(), &caps.checksum), #[cfg(feature = "socket-tcp")] - Packet::Tcp((ref ip_repr, _)) => Some(ip_repr.dst_addr()) + IpPacket::Tcp((_, mut tcp_repr)) => { + // This is a terrible hack to make TCP performance more acceptable on systems + // where the TCP buffers are significantly larger than network buffers, + // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window) + // together with four 1500 B Ethernet receive buffers. If left untreated, + // this would result in our peer pushing our window and sever packet loss. + // + // I'm really not happy about this "solution" but I don't know what else to do. + if let Some(max_burst_size) = caps.max_burst_size { + let mut max_segment_size = caps.max_transmission_unit; + max_segment_size -= _ip_repr.buffer_len(); + max_segment_size -= tcp_repr.header_len(); + + let max_window_size = max_burst_size * max_segment_size; + if tcp_repr.window_len as usize > max_window_size { + tcp_repr.window_len = max_window_size as u16; + } + } + + tcp_repr.emit(&mut TcpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), &_ip_repr.dst_addr(), + &caps.checksum); + } } } } @@ -384,7 +434,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, pkt)?; + self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; Ok(true) } else { Ok(false) @@ -410,7 +460,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, pkt)?; + self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; Ok(true) } else { Ok(false) @@ -593,7 +643,8 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> macro_rules! respond { ($response:expr) => ({ let response = $response; - neighbor_addr = response.neighbor_addr(); + neighbor_addr = Some(response.ip_repr().dst_addr()); + let response = EthernetPacket::Ip(response); let tx_token = device.transmit().ok_or(Error::Exhausted)?; device_result = inner.dispatch(tx_token, timestamp, response); device_result @@ -605,28 +656,28 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> #[cfg(feature = "socket-raw")] Socket::Raw(ref mut socket) => socket.dispatch(&caps.checksum, |response| - respond!(Packet::Raw(response))), + respond!(IpPacket::Raw(response))), #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] Socket::Icmp(ref mut socket) => socket.dispatch(&caps, |response| { match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => - respond!(Packet::Icmpv4((ipv4_repr, icmpv4_repr))), + respond!(IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))), #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => - respond!(Packet::Icmpv6((ipv6_repr, icmpv6_repr))), + respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))), _ => Err(Error::Unaddressable) } }), #[cfg(feature = "socket-udp")] Socket::Udp(ref mut socket) => socket.dispatch(|response| - respond!(Packet::Udp(response))), + respond!(IpPacket::Udp(response))), #[cfg(feature = "socket-tcp")] Socket::Tcp(ref mut socket) => socket.dispatch(timestamp, &caps, |response| - respond!(Packet::Tcp(response))), + respond!(IpPacket::Tcp(response))), Socket::__Nonexhaustive(_) => unreachable!() }; @@ -663,7 +714,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> 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(tx_token, timestamp, pkt)?; + self.inner.dispatch(tx_token, timestamp, EthernetPacket::Ip(pkt))?; } self.inner.igmp_report_state = IgmpReportState::Inactive; @@ -681,7 +732,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, 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(tx_token, timestamp, pkt)?; + self.inner.dispatch(tx_token, timestamp, EthernetPacket::Ip(pkt))?; } let next_timeout = (timeout + interval).max(timestamp); @@ -771,7 +822,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ethernet<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) -> - Result>> + Result>> { let eth_frame = EthernetFrame::new_checked(frame)?; @@ -789,10 +840,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { self.process_arp(timestamp, ð_frame), #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => - self.process_ipv4(sockets, timestamp, ð_frame), + self.process_ipv4(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => - self.process_ipv6(sockets, timestamp, ð_frame), + self.process_ipv6(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), // Drop all other traffic. _ => Err(Error::Unrecognized), } @@ -801,7 +852,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn process_arp<'frame, T: AsRef<[u8]>> (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result>> + Result>> { let arp_packet = ArpPacket::new_checked(eth_frame.payload())?; let arp_repr = ArpRepr::parse(&arp_packet)?; @@ -824,7 +875,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { - Ok(Some(Packet::Arp(ArpRepr::EthernetIpv4 { + Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: self.ethernet_addr, source_protocol_addr: target_protocol_addr, @@ -866,7 +917,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ipv6<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result>> + Result>> { let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; @@ -903,7 +954,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_nxt_hdr<'frame> (&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) - -> Result>> + -> Result>> { match nxt_hdr { IpProtocol::Icmpv6 => @@ -944,7 +995,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn process_ipv4<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result>> + Result>> { let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -1027,7 +1078,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { /// 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, - ip_payload: &'frame [u8]) -> Result>> { + ip_payload: &'frame [u8]) -> Result>> { let igmp_packet = IgmpPacket::new_checked(ip_payload)?; let igmp_repr = IgmpRepr::parse(&igmp_packet)?; @@ -1076,7 +1127,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv6")] fn process_icmpv6<'frame>(&mut self, _sockets: &mut SocketSet, timestamp: Instant, - ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> + ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -1137,7 +1188,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv6")] fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, - repr: NdiscRepr<'frame>) -> Result>> { + repr: NdiscRepr<'frame>) -> Result>> { match repr { NdiscRepr::NeighborAdvert { lladdr, target_addr, flags } => { let ip_addr = ip_repr.src_addr.into(); @@ -1175,7 +1226,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { hop_limit: 0xff, payload_len: advert.buffer_len() }; - Ok(Some(Packet::Icmpv6((ip_repr, advert)))) + Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) } else { Ok(None) } @@ -1187,7 +1238,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv6")] fn process_hopbyhop<'frame>(&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, - ip_payload: &'frame [u8]) -> Result>> + ip_payload: &'frame [u8]) -> Result>> { let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?; let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?; @@ -1217,7 +1268,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr, - ip_payload: &'frame [u8]) -> Result>> + ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?; let checksum_caps = self.device_capabilities.checksum.clone(); @@ -1271,7 +1322,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv4")] fn icmpv4_reply<'frame, 'icmp: 'frame> (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) -> - Option> + Option> { if !ipv4_repr.src_addr.is_unicast() { // Do not send ICMP replies to non-unicast sources @@ -1285,7 +1336,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Some(Packet::Icmpv4((ipv4_reply_repr, icmp_repr))) + Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) } else if ipv4_repr.dst_addr.is_broadcast() { // Only reply to broadcasts for echo replies and not other ICMP messages match icmp_repr { @@ -1298,7 +1349,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Some(Packet::Icmpv4((ipv4_reply_repr, icmp_repr))) + Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) }, None => None, }, @@ -1312,7 +1363,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "proto-ipv6")] fn icmpv6_reply<'frame, 'icmp: 'frame> (&self, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>) -> - Option> + Option> { if ipv6_repr.dst_addr.is_unicast() { let ipv6_reply_repr = Ipv6Repr { @@ -1322,7 +1373,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { payload_len: icmp_repr.buffer_len(), hop_limit: 64 }; - Some(Packet::Icmpv6((ipv6_reply_repr, icmp_repr))) + Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) } else { // Do not send any ICMP replies to a broadcast destination address. None @@ -1332,7 +1383,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "socket-udp")] fn process_udp<'frame>(&self, sockets: &mut SocketSet, ip_repr: IpRepr, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) -> - Result>> + Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_packet = UdpPacket::new_checked(ip_payload)?; @@ -1388,7 +1439,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { #[cfg(feature = "socket-tcp")] fn process_tcp<'frame>(&self, sockets: &mut SocketSet, timestamp: Instant, ip_repr: IpRepr, ip_payload: &'frame [u8]) -> - Result>> + Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let tcp_packet = TcpPacket::new_checked(ip_payload)?; @@ -1400,7 +1451,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match tcp_socket.process(timestamp, &ip_repr, &tcp_repr) { // The packet is valid and handled by socket. - Ok(reply) => return Ok(reply.map(Packet::Tcp)), + Ok(reply) => return Ok(reply.map(|x| IpPacket::Tcp(x))), // The packet is malformed, or doesn't match the socket state, // or the socket buffer is full. Err(e) => return Err(e) @@ -1412,18 +1463,17 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { Ok(None) } else { // The packet wasn't handled by a socket, send a TCP RST packet. - Ok(Some(Packet::Tcp(TcpSocket::rst_reply(&ip_repr, &tcp_repr)))) + Ok(Some(IpPacket::Tcp(TcpSocket::rst_reply(&ip_repr, &tcp_repr)))) } } fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, - packet: Packet) -> Result<()> + packet: EthernetPacket) -> Result<()> where Tx: TxToken { - let checksum_caps = self.device_capabilities.checksum.clone(); match packet { #[cfg(feature = "proto-ipv4")] - Packet::Arp(arp_repr) => { + EthernetPacket::Arp(arp_repr) => { let dst_hardware_addr = match arp_repr { ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr, @@ -1438,69 +1488,9 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { arp_repr.emit(&mut packet); }) }, - #[cfg(feature = "proto-ipv4")] - Packet::Icmpv4((ipv4_repr, icmpv4_repr)) => { - self.dispatch_ip(tx_token, timestamp, IpRepr::Ipv4(ipv4_repr), - |_ip_repr, payload| { - icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &checksum_caps); - }) - } - #[cfg(feature = "proto-igmp")] - Packet::Igmp((ipv4_repr, igmp_repr)) => { - self.dispatch_ip(tx_token, timestamp, IpRepr::Ipv4(ipv4_repr), |_ip_repr, payload| { - igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)); - }) - } - #[cfg(feature = "proto-ipv6")] - Packet::Icmpv6((ipv6_repr, icmpv6_repr)) => { - self.dispatch_ip(tx_token, timestamp, IpRepr::Ipv6(ipv6_repr), - |ip_repr, payload| { - icmpv6_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(payload), &checksum_caps); - }) - } - #[cfg(feature = "socket-raw")] - Packet::Raw((ip_repr, raw_packet)) => { - self.dispatch_ip(tx_token, timestamp, ip_repr, |_ip_repr, payload| { - payload.copy_from_slice(raw_packet); - }) - } - #[cfg(feature = "socket-udp")] - Packet::Udp((ip_repr, udp_repr)) => { - self.dispatch_ip(tx_token, timestamp, ip_repr, |ip_repr, payload| { - udp_repr.emit(&mut UdpPacket::new_unchecked(payload), - &ip_repr.src_addr(), &ip_repr.dst_addr(), - &checksum_caps); - }) - } - #[cfg(feature = "socket-tcp")] - Packet::Tcp((ip_repr, mut tcp_repr)) => { - let caps = self.device_capabilities.clone(); - self.dispatch_ip(tx_token, timestamp, ip_repr, |ip_repr, payload| { - // This is a terrible hack to make TCP performance more acceptable on systems - // where the TCP buffers are significantly larger than network buffers, - // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window) - // together with four 1500 B Ethernet receive buffers. If left untreated, - // this would result in our peer pushing our window and sever packet loss. - // - // I'm really not happy about this "solution" but I don't know what else to do. - if let Some(max_burst_size) = caps.max_burst_size { - let mut max_segment_size = caps.max_transmission_unit; - max_segment_size -= EthernetFrame::<&[u8]>::header_len(); - max_segment_size -= ip_repr.buffer_len(); - max_segment_size -= tcp_repr.header_len(); - - let max_window_size = max_burst_size * max_segment_size; - if tcp_repr.window_len as usize > max_window_size { - tcp_repr.window_len = max_window_size as u16; - } - } - - tcp_repr.emit(&mut TcpPacket::new_unchecked(payload), - &ip_repr.src_addr(), &ip_repr.dst_addr(), - &checksum_caps); - }) - } + EthernetPacket::Ip(packet) => { + self.dispatch_ip(tx_token, timestamp, packet) + }, } } @@ -1626,25 +1616,23 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { net_debug!("address {} not in neighbor cache, sending Neighbor Solicitation", dst_addr); - let checksum_caps = self.device_capabilities.checksum.clone(); - let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: src_addr, lladdr: Some(self.ethernet_addr), }); - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_addr, - dst_addr: dst_addr.solicited_node(), - next_header: IpProtocol::Icmpv6, - payload_len: solicit.buffer_len(), - hop_limit: 0xff - }); + let packet = IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: src_addr, + dst_addr: dst_addr.solicited_node(), + next_header: IpProtocol::Icmpv6, + payload_len: solicit.buffer_len(), + hop_limit: 0xff + }, + solicit, + )); - self.dispatch_ip(tx_token, timestamp, ip_repr, |ip_repr, payload| { - solicit.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(payload), &checksum_caps); - })?; + self.dispatch_ip(tx_token, timestamp, packet)?; } _ => () @@ -1654,12 +1642,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { Err(Error::Unaddressable) } - fn dispatch_ip(&mut self, tx_token: Tx, timestamp: Instant, - ip_repr: IpRepr, f: F) -> Result<()> - where Tx: TxToken, F: FnOnce(IpRepr, &mut [u8]) - { - let ip_repr = ip_repr.lower(&self.ip_addrs)?; - let checksum_caps = self.device_capabilities.checksum.clone(); + fn dispatch_ip(&mut self, tx_token: Tx, timestamp: Instant, + packet: IpPacket) -> Result<()> { + let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; + let caps = self.device_capabilities.clone(); let (dst_hardware_addr, tx_token) = self.lookup_hardware_addr(tx_token, timestamp, @@ -1675,21 +1661,21 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { _ => return } - ip_repr.emit(frame.payload_mut(), &checksum_caps); + ip_repr.emit(frame.payload_mut(), &caps.checksum); let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - f(ip_repr, payload) + packet.emit_payload(ip_repr, payload, &caps); }) } #[cfg(feature = "proto-igmp")] - fn igmp_report_packet<'any>(&self, version: IgmpVersion, group_addr: Ipv4Address) -> Option> { + fn igmp_report_packet<'any>(&self, version: IgmpVersion, group_addr: Ipv4Address) -> Option> { let iface_addr = self.ipv4_address()?; let igmp_repr = IgmpRepr::MembershipReport { group_addr, version, }; - let pkt = Packet::Igmp((Ipv4Repr { + let pkt = IpPacket::Igmp((Ipv4Repr { src_addr: iface_addr, // Send to the group being reported dst_addr: group_addr, @@ -1703,10 +1689,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } #[cfg(feature = "proto-igmp")] - fn igmp_leave_packet<'any>(&self, group_addr: Ipv4Address) -> Option> { + fn igmp_leave_packet<'any>(&self, group_addr: Ipv4Address) -> Option> { self.ipv4_address().map(|iface_addr| { let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; - let pkt = Packet::Igmp((Ipv4Repr { + let pkt = IpPacket::Igmp((Ipv4Repr { src_addr: iface_addr, dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, protocol: IpProtocol::Igmp, @@ -1755,7 +1741,7 @@ mod test { #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr}; - use super::Packet; + use super::{EthernetPacket, IpPacket}; fn create_loopback<'a, 'b, 'c>() -> (EthernetInterface<'static, 'b, 'c, Loopback>, SocketSet<'static, 'a, 'b>) { @@ -1900,7 +1886,7 @@ mod test { data: &NO_BYTES }; - let expected_repr = Packet::Icmpv4(( + let expected_repr = IpPacket::Icmpv4(( Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), @@ -1965,7 +1951,7 @@ mod test { }, data: &data }; - let expected_repr = Packet::Icmpv4(( + let expected_repr = IpPacket::Icmpv4(( Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), @@ -2125,7 +2111,7 @@ mod test { hop_limit: 64, payload_len: expected_icmpv4_repr.buffer_len(), }; - let expected_packet = Packet::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); + 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), Ok(Some(expected_packet))); @@ -2220,10 +2206,10 @@ mod test { // 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), - Ok(Some(Packet::Icmpv4((expected_ip_repr, expected_icmp_repr))))); + 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), - Ok(Some(Packet::Icmpv6((expected_ip_repr, expected_icmp_repr))))); + Ok(Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))))); } #[test] @@ -2257,7 +2243,7 @@ mod test { // 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()), - Ok(Some(Packet::Arp(ArpRepr::EthernetIpv4 { + Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, @@ -2323,7 +2309,7 @@ mod test { // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement assert_eq!(iface.inner.process_ethernet(&mut socket_set, Instant::from_millis(0), frame.into_inner()), - Ok(Some(Packet::Icmpv6((ipv6_expected, icmpv6_expected))))); + 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), @@ -2424,7 +2410,7 @@ mod test { ..ipv4_repr }; assert_eq!(iface.inner.process_icmpv4(&mut socket_set, ip_repr, icmp_data), - Ok(Some(Packet::Icmpv4((ipv4_reply, echo_reply))))); + Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))))); { let mut socket = socket_set.get::(socket_handle); @@ -2514,7 +2500,7 @@ mod test { // 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), - Ok(Some(Packet::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); + Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); // Ensure the address of the requestor was entered in the cache assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0), @@ -2723,7 +2709,7 @@ mod test { // because the packet could not be handled we should send an Icmp message assert!(match frame { - Ok(Some(Packet::Icmpv4(_))) => true, + Ok(Some(IpPacket::Icmpv4(_))) => true, _ => false, }); } From 323dfe2ab6dd18b2f09614d836005d9158a57263 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:22:59 -0800 Subject: [PATCH 033/566] Use is_empty instead of length comparison These were flagged by `cargo clippy`: warning: length comparison to zero --- examples/client.rs | 4 ++-- examples/server.rs | 8 ++++---- src/socket/tcp.rs | 12 ++++++------ src/wire/dhcpv4.rs | 2 +- src/wire/ndisc.rs | 6 +++--- src/wire/tcp.rs | 12 ++++++------ 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 58340aa08..66f5fb7ca 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -77,7 +77,7 @@ fn main() { if socket.may_recv() { let data = socket.recv(|data| { let mut data = data.to_owned(); - if data.len() > 0 { + if !data.is_empty() { debug!("recv data: {:?}", str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); data = data.split(|&b| b == b'\n').collect::>().concat(); @@ -86,7 +86,7 @@ fn main() { } (data.len(), data) }).unwrap(); - if socket.can_send() && data.len() > 0 { + if socket.can_send() && !data.is_empty() { debug!("send data: {:?}", str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); socket.send_slice(&data[..]).unwrap(); diff --git a/examples/server.rs b/examples/server.rs index ae53b6c80..572c84ae3 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -133,7 +133,7 @@ fn main() { let data = socket.recv(|buffer| { let recvd_len = buffer.len(); let mut data = buffer.to_owned(); - if data.len() > 0 { + if !data.is_empty() { debug!("tcp:6970 recv data: {:?}", str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); data = data.split(|&b| b == b'\n').collect::>().concat(); @@ -142,7 +142,7 @@ fn main() { } (recvd_len, data) }).unwrap(); - if socket.can_send() && data.len() > 0 { + if socket.can_send() && !data.is_empty() { debug!("tcp:6970 send data: {:?}", str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); socket.send_slice(&data[..]).unwrap(); @@ -164,7 +164,7 @@ fn main() { if socket.may_recv() { socket.recv(|buffer| { - if buffer.len() > 0 { + if !buffer.is_empty() { debug!("tcp:6971 recv {:?} octets", buffer.len()); } (buffer.len(), ()) @@ -183,7 +183,7 @@ fn main() { if socket.may_send() { socket.send(|data| { - if data.len() > 0 { + if !data.is_empty() { debug!("tcp:6972 send {:?} octets", data.len()); for (i, b) in data.iter_mut().enumerate() { *b = (i % 256) as u8; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index d50042814..569a64a62 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -685,7 +685,7 @@ impl<'a> TcpSocket<'a> { // we still can receive indefinitely. State::FinWait1 | State::FinWait2 => true, // If we have something in the receive buffer, we can receive that. - _ if self.rx_buffer.len() > 0 => true, + _ if !self.rx_buffer.is_empty() => true, _ => false } } @@ -833,7 +833,7 @@ impl<'a> TcpSocket<'a> { self.recv_error_check()?; let buffer = self.rx_buffer.get_allocated(0, size); - if buffer.len() > 0 { + if !buffer.is_empty() { #[cfg(any(test, feature = "verbose"))] net_trace!("{}:{}:{}: rx buffer: peeking at {} octets", self.meta.handle, self.local_endpoint, self.remote_endpoint, @@ -1368,7 +1368,7 @@ impl<'a> TcpSocket<'a> { // Increment duplicate ACK count and set for retransmit if we just recived // the third duplicate ACK Some(ref last_rx_ack) if - repr.payload.len() == 0 && + repr.payload.is_empty() && *last_rx_ack == ack_number && ack_number < self.remote_last_seq => { // Increment duplicate ACK count @@ -1649,7 +1649,7 @@ impl<'a> TcpSocket<'a> { match self.state { State::FinWait1 | State::LastAck => repr.control = TcpControl::Fin, - State::Established | State::CloseWait if repr.payload.len() > 0 => + State::Established | State::CloseWait if !repr.payload.is_empty() => repr.control = TcpControl::Psh, _ => () } @@ -1682,12 +1682,12 @@ impl<'a> TcpSocket<'a> { if is_keep_alive { net_trace!("{}:{}:{}: sending a keep-alive", self.meta.handle, self.local_endpoint, self.remote_endpoint); - } else if repr.payload.len() > 0 { + } else if !repr.payload.is_empty() { net_trace!("{}:{}:{}: tx buffer: sending {} octets at offset {}", self.meta.handle, self.local_endpoint, self.remote_endpoint, repr.payload.len(), self.remote_last_seq - self.local_seq_no); } - if repr.control != TcpControl::None || repr.payload.len() == 0 { + if repr.control != TcpControl::None || repr.payload.is_empty() { let flags = match (repr.control, repr.ack_number) { (TcpControl::Syn, None) => "SYN", diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index af51f57f8..d5c6c06f8 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -727,7 +727,7 @@ impl<'a> Repr<'a> { let mut max_size = None; let mut options = packet.options()?; - while options.len() > 0 { + while !options.is_empty() { let (next_options, option) = DhcpOption::parse(options)?; match option { DhcpOption::EndOfList => break, diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 20544b255..775f54816 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -228,7 +228,7 @@ impl<'a> Repr<'a> { where T: AsRef<[u8]> + ?Sized { match packet.msg_type() { Message::RouterSolicit => { - let lladdr = if packet.payload().len() > 0 { + let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), @@ -263,7 +263,7 @@ impl<'a> Repr<'a> { }) }, Message::NeighborSolicit => { - let lladdr = if packet.payload().len() > 0 { + let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), @@ -277,7 +277,7 @@ impl<'a> Repr<'a> { }) }, Message::NeighborAdvert => { - let lladdr = if packet.payload().len() > 0 { + let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 205e4f0f2..18feb17e8 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -294,7 +294,7 @@ impl> Packet { pub fn selective_ack_permitted(&self) -> Result { let data = self.buffer.as_ref(); let mut options = &data[field::OPTIONS(self.header_len())]; - while options.len() > 0 { + while !options.is_empty() { let (next_options, option) = TcpOption::parse(options)?; match option { TcpOption::SackPermitted => { @@ -315,7 +315,7 @@ impl> Packet { ) -> Result<[Option<(u32, u32)>; 3]> { let data = self.buffer.as_ref(); let mut options = &data[field::OPTIONS(self.header_len())]; - while options.len() > 0 { + while !options.is_empty() { let (next_options, option) = TcpOption::parse(options)?; match option { TcpOption::SackRange(slice) => { @@ -789,7 +789,7 @@ impl<'a> Repr<'a> { let mut options = packet.options(); let mut sack_permitted = false; let mut sack_ranges = [None, None, None]; - while options.len() > 0 { + while !options.is_empty() { let (next_options, option) = TcpOption::parse(options)?; match option { TcpOption::EndOfList => break, @@ -905,7 +905,7 @@ impl<'a> Repr<'a> { let tmp = options; options = TcpOption::SackRange(self.sack_ranges).emit(tmp); } - if options.len() > 0 { + if !options.is_empty() { TcpOption::EndOfList.emit(options); } } @@ -929,7 +929,7 @@ impl<'a> Repr<'a> { /// Return whether the segment has no flags set (except PSH) and no data. pub fn is_empty(&self) -> bool { match self.control { - _ if self.payload.len() != 0 => false, + _ if !self.payload.is_empty() => false, Control::Syn | Control::Fin | Control::Rst => false, Control::None | Control::Psh => true } @@ -959,7 +959,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { write!(f, " len={}", self.payload().len())?; let mut options = self.options(); - while options.len() > 0 { + while !options.is_empty() { let (next_options, option) = match TcpOption::parse(options) { Ok(res) => res, From 38a5008541f59aee6949cc19259eaad7a60e23c3 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:35:44 -0800 Subject: [PATCH 034/566] Prefer if-let syntax over single-pattern match These were flagged by `cargo clippy`: warning: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` This also silences a few cases where the match couldn't be replaced with an if because of the following error: error: attributes are not yet allowed on `if` expressions Once we increase the minimum Rust version to 1.43, these can be updated as well. --- src/iface/ethernet.rs | 9 ++------- src/iface/neighbor.rs | 10 ++++------ src/parsers.rs | 6 ++++++ src/socket/tcp.rs | 13 ++++--------- src/wire/tcp.rs | 14 ++++---------- 5 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 71e609417..af2efd722 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1578,13 +1578,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { IpAddress::__Nonexhaustive => unreachable!() }; - match hardware_addr { - Some(hardware_addr) => - // Destination is multicast - return Ok((hardware_addr, tx_token)), - None => - // Continue - (), + if let Some(hardware_addr) = hardware_addr { + return Ok((hardware_addr, tx_token)) } } diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 8f2193a8a..83863b7c0 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -169,13 +169,11 @@ impl<'a> Cache<'a> { return Answer::Found(EthernetAddress::BROADCAST); } - match self.storage.get(protocol_addr) { - Some(&Neighbor { expires_at, hardware_addr }) => { - if timestamp < expires_at { - return Answer::Found(hardware_addr) - } + if let Some(&Neighbor { expires_at, hardware_addr }) = + self.storage.get(protocol_addr) { + if timestamp < expires_at { + return Answer::Found(hardware_addr) } - None => () } if timestamp < self.silent_until { diff --git a/src/parsers.rs b/src/parsers.rs index 4c6b93682..0400147a7 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -287,12 +287,14 @@ impl<'a> Parser<'a> { fn accept_ip(&mut self) -> Result { #[cfg(feature = "proto-ipv4")] + #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv4()) { Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)), None => () } #[cfg(feature = "proto-ipv6")] + #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv6(false)) { Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)), None => () @@ -333,12 +335,14 @@ impl<'a> Parser<'a> { fn accept_ip_endpoint(&mut self) -> Result { #[cfg(feature = "proto-ipv4")] + #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv4_endpoint()) { Some(ipv4) => return Ok(ipv4), None => () } #[cfg(feature = "proto-ipv6")] + #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv6_endpoint()) { Some(ipv6) => return Ok(ipv6), None => () @@ -424,12 +428,14 @@ impl FromStr for IpCidr { /// Parse a string representation of an IP CIDR. fn from_str(s: &str) -> Result { #[cfg(feature = "proto-ipv4")] + #[allow(clippy::single_match)] match Ipv4Cidr::from_str(s) { Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)), Err(_) => () } #[cfg(feature = "proto-ipv6")] + #[allow(clippy::single_match)] match Ipv6Cidr::from_str(s) { Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)), Err(_) => () diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 569a64a62..4ac071a63 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -127,21 +127,16 @@ impl Timer { } fn set_keep_alive(&mut self) { - match *self { - Timer::Idle { ref mut keep_alive_at } - if keep_alive_at.is_none() => { + if let Timer::Idle { ref mut keep_alive_at } = *self { + if keep_alive_at.is_none() { *keep_alive_at = Some(Instant::from_millis(0)) } - _ => () } } fn rewind_keep_alive(&mut self, timestamp: Instant, interval: Option) { - match self { - &mut Timer::Idle { ref mut keep_alive_at } => { - *keep_alive_at = interval.map(|interval| timestamp + interval) - } - _ => () + if let Timer::Idle { ref mut keep_alive_at } = *self { + *keep_alive_at = interval.map(|interval| timestamp + interval) } } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 18feb17e8..f4acd9e5d 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -296,11 +296,8 @@ impl> Packet { let mut options = &data[field::OPTIONS(self.header_len())]; while !options.is_empty() { let (next_options, option) = TcpOption::parse(options)?; - match option { - TcpOption::SackPermitted => { - return Ok(true); - }, - _ => {}, + if option == TcpOption::SackPermitted { + return Ok(true); } options = next_options; } @@ -317,11 +314,8 @@ impl> Packet { let mut options = &data[field::OPTIONS(self.header_len())]; while !options.is_empty() { let (next_options, option) = TcpOption::parse(options)?; - match option { - TcpOption::SackRange(slice) => { - return Ok(slice); - }, - _ => {}, + if let TcpOption::SackRange(slice) = option { + return Ok(slice); } options = next_options; } From 63bf644992d894ce50311493af5e90662f2b82c4 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 10:44:09 -0800 Subject: [PATCH 035/566] Dereference match expressions to clean up patterns These were flagged by `cargo clippy`: warning: you don't need to add `&` to all patterns This should have happened in ac830e8b, but I guess I missed it. --- src/wire/dhcpv4.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index d5c6c06f8..fbc09e132 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -135,50 +135,50 @@ impl<'a> DhcpOption<'a> { pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] { let skip_length; - match self { - &DhcpOption::EndOfList => { + match *self { + DhcpOption::EndOfList => { skip_length = 1; buffer[0] = field::OPT_END; } - &DhcpOption::Pad => { + DhcpOption::Pad => { skip_length = 1; buffer[0] = field::OPT_PAD; } _ => { skip_length = self.buffer_len(); buffer[1] = (skip_length - 2) as u8; - match self { - &DhcpOption::EndOfList | &DhcpOption::Pad => unreachable!(), - &DhcpOption::MessageType(value) => { + match *self { + DhcpOption::EndOfList | DhcpOption::Pad => unreachable!(), + DhcpOption::MessageType(value) => { buffer[0] = field::OPT_DHCP_MESSAGE_TYPE; buffer[2] = value.into(); } - &DhcpOption::ClientIdentifier(eth_addr) => { + DhcpOption::ClientIdentifier(eth_addr) => { buffer[0] = field::OPT_CLIENT_ID; buffer[2] = u16::from(Hardware::Ethernet) as u8; buffer[3..9].copy_from_slice(eth_addr.as_bytes()); } - &DhcpOption::RequestedIp(ip) => { + DhcpOption::RequestedIp(ip) => { buffer[0] = field::OPT_REQUESTED_IP; buffer[2..6].copy_from_slice(ip.as_bytes()); } - &DhcpOption::ServerIdentifier(ip) => { + DhcpOption::ServerIdentifier(ip) => { buffer[0] = field::OPT_SERVER_IDENTIFIER; buffer[2..6].copy_from_slice(ip.as_bytes()); } - &DhcpOption::Router(ip) => { + DhcpOption::Router(ip) => { buffer[0] = field::OPT_ROUTER; buffer[2..6].copy_from_slice(ip.as_bytes()); } - &DhcpOption::SubnetMask(mask) => { + DhcpOption::SubnetMask(mask) => { buffer[0] = field::OPT_SUBNET_MASK; buffer[2..6].copy_from_slice(mask.as_bytes()); } - &DhcpOption::MaximumDhcpMessageSize(size) => { + DhcpOption::MaximumDhcpMessageSize(size) => { buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE; buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]); } - &DhcpOption::Other { kind, data: provided } => { + DhcpOption::Other { kind, data: provided } => { buffer[0] = kind; buffer[2..skip_length].copy_from_slice(provided); } From 86e778c4467df58c5209c0b5224108bd56bc996d Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 10:53:49 -0800 Subject: [PATCH 036/566] Silence warning about non-exhaustive pattern These were flagged by `cargo clippy`: warning: this seems like a manual implementation of the non-exhaustive pattern The non_exhaustive attribute isn't available until we increase the minimum Rust version to 1.40, so we should just silence these warnings in the meantime. --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a25dae5ae..99a5a52b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,8 @@ compile_error!("at least one socket needs to be enabled"); */ // FIXME(dlrobertson): clippy fails with this lint -#![cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))] +#![allow(clippy::if_same_then_else)] +#![allow(clippy::manual_non_exhaustive)] #[cfg(feature = "alloc")] extern crate alloc; From 5c335f8fe70d6cabb2e83d1c010f6b482cbb1daa Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 11:13:40 -0800 Subject: [PATCH 037/566] Silence warning about is_empty method These were flagged by `cargo clippy`: warning: item has a public `len` method but no corresponding `is_empty` method --- src/wire/tcp.rs | 1 + src/wire/udp.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index f4acd9e5d..79235509c 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -712,6 +712,7 @@ pub enum Control { Rst } +#[allow(clippy::len_without_is_empty)] impl Control { /// Return the length of a control flag, in terms of sequence space. pub fn len(self) -> usize { diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 44bb578bf..1ce43b8e1 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -27,6 +27,7 @@ mod field { } } +#[allow(clippy::len_without_is_empty)] impl> Packet { /// Imbue a raw octet buffer with UDP packet structure. pub fn new_unchecked(buffer: T) -> Packet { From 48323b598042117c1db46ee0388ca80c18400bd8 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 14:28:31 -0800 Subject: [PATCH 038/566] Silence warning about matches macro These were flagged by `cargo clippy`: warning: match expression looks like `matches!` macro The matches! macro isn't stabilized until Rust 1.42. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 99a5a52b6..6e67af288 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ compile_error!("at least one socket needs to be enabled"); */ // FIXME(dlrobertson): clippy fails with this lint #![allow(clippy::if_same_then_else)] #![allow(clippy::manual_non_exhaustive)] +#![allow(clippy::match_like_matches_macro)] #[cfg(feature = "alloc")] extern crate alloc; From 57029d6e5d2bb9a7bc4275ca152bff9c2f1b4c33 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:23:53 -0800 Subject: [PATCH 039/566] Prefer assignment operators These were flagged by `cargo clippy`: warning: manual implementation of an assign operation --- src/storage/assembler.rs | 2 +- src/wire/icmpv6.rs | 2 +- src/wire/ipv6fragment.rs | 2 +- src/wire/ipv6routing.rs | 2 +- src/wire/mld.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index d5685a760..0a75fc96f 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -275,7 +275,7 @@ impl<'a> Iterator for AssemblerIter<'a> { let mut data_range = None; while data_range.is_none() && self.index < self.assembler.contigs.len() { let contig = self.assembler.contigs[self.index]; - self.left = self.left + contig.hole_size; + self.left += contig.hole_size; self.right = self.left + contig.data_size; data_range = if self.left < self.right { let data_range = (self.left + self.offset, self.right + self.offset); diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index c44ae5d6b..b6e02773e 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -420,7 +420,7 @@ impl + AsMut<[u8]>> Packet { Message::MldQuery => { let data = self.buffer.as_mut(); NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0); - data[field::SQRV] = data[field::SQRV] & 0xf; + data[field::SQRV] &= 0xf; }, Message::MldReport => { let data = self.buffer.as_mut(); diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index 93a29b014..8ef385a72 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -115,7 +115,7 @@ impl + AsMut<[u8]>> Header { data[field::RESERVED] = 0; // Retain the higher order 5 bits and lower order 1 bit - data[3] = data[3] & 0xf9; + data[3] &= 0xf9; } /// Set the fragment offset field. diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index d66793e4d..93a2775e4 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -310,7 +310,7 @@ impl + AsMut<[u8]>> Header { } Type::Rpl => { // Retain the higher order 4 bits of the padding field - data[field::PAD] = data[field::PAD] & 0xF0; + data[field::PAD] &= 0xF0; data[6] = 0; data[7] = 0; } diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 1abbba9f9..2ea35a907 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -125,7 +125,7 @@ impl + AsMut<[u8]>> Packet { #[inline] pub fn clear_s_flag(&mut self) { let data = self.buffer.as_mut(); - data[field::SQRV] = data[field::SQRV] & 0x7; + data[field::SQRV] &= 0x7; } /// Set the Querier's Robustness Variable. From 48539a84dc4d526f4d971c3ae1661ebd05d09e6d Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 14:35:42 -0800 Subject: [PATCH 040/566] Prefer elided lifetimes These were flagged by `cargo clippy`: warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --- src/storage/assembler.rs | 2 +- src/storage/ring_buffer.rs | 4 ++-- src/wire/tcp.rs | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 0a75fc96f..714cf3d08 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -243,7 +243,7 @@ impl Assembler { /// |--- 100 ---|--- 200 ---|--- 100 ---| /// /// An offset of 1500 would return the ranges: ``(1500, 1600), (1800, 1900)`` - pub fn iter_data<'a>(&'a self, first_offset: usize) -> AssemblerIter<'a> { + pub fn iter_data(&self, first_offset: usize) -> AssemblerIter { AssemblerIter::new(self, first_offset) } } diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 9b70b0298..873140e95 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -190,7 +190,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// This function may return a slice smaller than the given size /// if the free space in the buffer is not contiguous. // #[must_use] - pub fn enqueue_many<'b>(&'b mut self, size: usize) -> &'b mut [T] { + pub fn enqueue_many(&mut self, size: usize) -> &mut [T] { self.enqueue_many_with(|buf| { let size = cmp::min(size, buf.len()); (size, &mut buf[..size]) @@ -242,7 +242,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// This function may return a slice smaller than the given size /// if the allocated space in the buffer is not contiguous. // #[must_use] - pub fn dequeue_many<'b>(&'b mut self, size: usize) -> &'b mut [T] { + pub fn dequeue_many(&mut self, size: usize) -> &mut [T] { self.dequeue_many_with(|buf| { let size = cmp::min(size, buf.len()); (size, &mut buf[..size]) diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 79235509c..31a3cc1b4 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -307,9 +307,7 @@ impl> Packet { /// Return the selective acknowledgement ranges, if any. If there are none in the packet, an /// array of ``None`` values will be returned. /// - pub fn selective_ack_ranges<'s>( - &'s self - ) -> Result<[Option<(u32, u32)>; 3]> { + pub fn selective_ack_ranges(&self) -> Result<[Option<(u32, u32)>; 3]> { let data = self.buffer.as_ref(); let mut options = &data[field::OPTIONS(self.header_len())]; while !options.is_empty() { From dc7de0a550896485b265cb0c1acf23045270afe9 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 22:34:44 -0800 Subject: [PATCH 041/566] Use subsec_millis where possible These were flagged by `cargo clippy`: warning: calling `subsec_millis()` is more concise than this calculation --- src/time.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/time.rs b/src/time.rs index 221e148ee..82924dd5a 100644 --- a/src/time.rs +++ b/src/time.rs @@ -71,7 +71,7 @@ impl Instant { impl From<::std::time::Instant> for Instant { fn from(other: ::std::time::Instant) -> Instant { let elapsed = other.elapsed(); - Instant::from_millis((elapsed.as_secs() * 1_000) as i64 + (elapsed.subsec_nanos() / 1_000_000) as i64) + Instant::from_millis((elapsed.as_secs() * 1_000) as i64 + elapsed.subsec_millis() as i64) } } @@ -80,7 +80,7 @@ impl From<::std::time::SystemTime> for Instant { fn from(other: ::std::time::SystemTime) -> Instant { let n = other.duration_since(::std::time::UNIX_EPOCH) .expect("start time must not be before the unix epoch"); - Self::from_millis(n.as_secs() as i64 * 1000 + (n.subsec_nanos() / 1000000) as i64) + Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64) } } @@ -233,7 +233,7 @@ impl ops::DivAssign for Duration { impl From<::core::time::Duration> for Duration { fn from(other: ::core::time::Duration) -> Duration { Duration::from_millis( - other.as_secs() * 1000 + (other.subsec_nanos() / 1_000_000) as u64 + other.as_secs() * 1000 + other.subsec_millis() as u64 ) } } From ebe2449bcc356e745c35c49990183983d5847711 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 14:44:50 -0800 Subject: [PATCH 042/566] Remove some redundant closures These were flagged by `cargo clippy`: warning: redundant closure found --- src/wire/icmpv6.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index b6e02773e..14f1f5254 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -624,10 +624,10 @@ impl<'a> Repr<'a> { }, #[cfg(feature = "ethernet")] (msg_type, 0) if msg_type.is_ndisc() => { - NdiscRepr::parse(packet).map(|repr| Repr::Ndisc(repr)) + NdiscRepr::parse(packet).map(Repr::Ndisc) }, (msg_type, 0) if msg_type.is_mld() => { - MldRepr::parse(packet).map(|repr| Repr::Mld(repr)) + MldRepr::parse(packet).map(Repr::Mld) }, _ => Err(Error::Unrecognized) } From 95bbd248699fe026b7c5aecee55edd9c40981c6e Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:24:32 -0800 Subject: [PATCH 043/566] Clean up iterator chains These were flagged by `cargo clippy`: warning: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. warning: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` warning: called `skip_while(p).next()` on an `Iterator` The skip_while conversion is a little tricky. Clippy notes that: warning: called `skip_while(p).next()` on an `Iterator` help: this is more succinctly expressed by calling `.find(!p)` instead So the condition of the skip_while is inverted and then simplified using De Morgan's laws. --- src/iface/ethernet.rs | 7 +++---- src/iface/neighbor.rs | 2 +- src/socket/tcp.rs | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index af2efd722..b11b26fea 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -721,7 +721,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1 #[cfg(feature = "proto-ipv6")] pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool { - self.ip_addrs.iter().find(|cidr| { + self.ip_addrs.iter().any(|cidr| { match *cidr { IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK=> { // Take the lower order 24 bits of the IPv6 address and @@ -730,7 +730,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } _ => false, } - }).is_some() + }) } /// Check whether the interface has the given IP address assigned. @@ -1522,8 +1522,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn in_same_network(&self, addr: &IpAddress) -> bool { self.ip_addrs .iter() - .find(|cidr| cidr.contains_addr(addr)) - .is_some() + .any(|cidr| cidr.contains_addr(addr)) } fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 83863b7c0..f251d1cb2 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -106,7 +106,7 @@ impl<'a> Cache<'a> { #[cfg(any(feature = "std", feature = "alloc"))] ManagedMap::Owned(ref mut map) => { if current_storage_size >= self.gc_threshold { - let new_btree_map = map.into_iter() + let new_btree_map = map.iter_mut() .map(|(key, value)| (*key, *value)) .filter(|(_, v)| timestamp < v.expires_at) .collect(); diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 4ac071a63..fa8082f0c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -961,8 +961,7 @@ impl<'a> TcpSocket<'a> { reply_repr.sack_ranges[0] = self.assembler.iter_data( reply_repr.ack_number.map(|s| s.0 as usize).unwrap_or(0)) .map(|(left, right)| (left as u32, right as u32)) - .skip_while(|(left, right)| *left > last_seg_seq || *right < last_seg_seq) - .next(); + .find(|(left, right)| *left <= last_seg_seq && *right >= last_seg_seq); } if reply_repr.sack_ranges[0].is_none() { From ddb876bec42bcafddeede9266ef51917cc9192ae Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 27 Dec 2020 11:19:56 -0800 Subject: [PATCH 044/566] Remove redundant field names from structs These were flagged by `cargo clippy`: warning: redundant field names in struct initialization There are plenty more redundant field names, but I only changed the ones where the initialization was a single line of code. I still prefer the redundant style for multi-line initializations (and I'm under the impression that others agree), so I've also disabled the warning. --- src/iface/ethernet.rs | 12 ++---------- src/lib.rs | 1 + src/phy/loopback.rs | 2 +- src/phy/pcap_writer.rs | 6 +++--- src/phy/tracer.rs | 6 +++--- src/socket/set.rs | 6 ++---- src/wire/ip.rs | 6 +++--- src/wire/pretty_print.rs | 2 +- src/wire/tcp.rs | 2 +- 9 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index b98dea2ab..2d83aaaee 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1156,11 +1156,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { Icmpv6Repr::EchoRequest { ident, seq_no, data } => { match ip_repr { IpRepr::Ipv6(ipv6_repr) => { - let icmp_reply_repr = Icmpv6Repr::EchoReply { - ident: ident, - seq_no: seq_no, - data: data - }; + let icmp_reply_repr = Icmpv6Repr::EchoReply { ident, seq_no, data }; Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) }, _ => Err(Error::Unrecognized), @@ -1295,11 +1291,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { // Respond to echo requests. #[cfg(feature = "proto-ipv4")] Icmpv4Repr::EchoRequest { ident, seq_no, data } => { - let icmp_reply_repr = Icmpv4Repr::EchoReply { - ident: ident, - seq_no: seq_no, - data: data - }; + let icmp_reply_repr = Icmpv4Repr::EchoReply { ident, seq_no, data }; match ip_repr { IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)), _ => Err(Error::Unrecognized), diff --git a/src/lib.rs b/src/lib.rs index 6e67af288..f357ca6b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::if_same_then_else)] #![allow(clippy::manual_non_exhaustive)] #![allow(clippy::match_like_matches_macro)] +#![allow(clippy::redundant_field_names)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index b4e1f522c..fdbd21ec0 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -44,7 +44,7 @@ impl<'a> Device<'a> for Loopback { fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { self.queue.pop_front().map(move |buffer| { - let rx = RxToken { buffer: buffer }; + let rx = RxToken { buffer }; let tx = TxToken { queue: &mut self.queue }; (rx, tx) }) diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index cb4b9ece2..afa0d4c57 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -146,8 +146,8 @@ impl<'a, D, S> Device<'a> for PcapWriter fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { let &mut Self { ref mut lower, ref sink, mode, .. } = self; lower.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { token: rx_token, sink: sink.clone(), mode: mode }; - let tx = TxToken { token: tx_token, sink: sink.clone(), mode: mode }; + let rx = RxToken { token: rx_token, sink: sink.clone(), mode }; + let tx = TxToken { token: tx_token, sink: sink.clone(), mode }; (rx, tx) }) } @@ -155,7 +155,7 @@ impl<'a, D, S> Device<'a> for PcapWriter fn transmit(&'a mut self) -> Option { let &mut Self { ref mut lower, ref sink, mode } = self; lower.transmit().map(|token| { - TxToken { token, sink: sink.clone(), mode: mode } + TxToken { token, sink: sink.clone(), mode } }) } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 22a2288f1..d9ecfd279 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -52,8 +52,8 @@ impl<'a, D, P> Device<'a> for Tracer fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { let &mut Self { ref mut inner, writer, .. } = self; inner.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { token: rx_token, writer: writer }; - let tx = TxToken { token: tx_token, writer: writer }; + let rx = RxToken { token: rx_token, writer }; + let tx = TxToken { token: tx_token, writer }; (rx, tx) }) } @@ -61,7 +61,7 @@ impl<'a, D, P> Device<'a> for Tracer fn transmit(&'a mut self) -> Option { let &mut Self { ref mut inner, writer } = self; inner.transmit().map(|tx_token| { - TxToken { token: tx_token, writer: writer } + TxToken { token: tx_token, writer } }) } } diff --git a/src/socket/set.rs b/src/socket/set.rs index fd6d53826..8c6a56ad9 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -38,9 +38,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { pub fn new(sockets: SocketsT) -> Set<'a, 'b, 'c> where SocketsT: Into>>> { let sockets = sockets.into(); - Set { - sockets: sockets - } + Set { sockets } } /// Add a socket to the set with the reference count 1, and return its handle. @@ -55,7 +53,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { net_trace!("[{}]: adding", index); let handle = Handle(index); socket.meta_mut().handle = handle; - *slot = Some(Item { socket: socket, refs: 1 }); + *slot = Some(Item { socket, refs: 1 }); handle } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index d003a1516..83eb4df19 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -408,7 +408,7 @@ impl Endpoint { /// Create an endpoint address from given address and port. pub fn new(addr: Address, port: u16) -> Endpoint { - Endpoint { addr: addr, port: port } + Endpoint { addr, port } } /// Query whether the endpoint has a specified address and port. @@ -455,13 +455,13 @@ impl fmt::Display for Endpoint { impl From for Endpoint { fn from(port: u16) -> Endpoint { - Endpoint { addr: Address::Unspecified, port: port } + Endpoint { addr: Address::Unspecified, port } } } impl> From<(T, u16)> for Endpoint { fn from((addr, port): (T, u16)) -> Endpoint { - Endpoint { addr: addr.into(), port: port } + Endpoint { addr: addr.into(), port } } } diff --git a/src/wire/pretty_print.rs b/src/wire/pretty_print.rs index 6a65f4f4b..60a6fe4fb 100644 --- a/src/wire/pretty_print.rs +++ b/src/wire/pretty_print.rs @@ -43,7 +43,7 @@ impl PrettyIndent { /// Create an indentation state. The entire listing will be indented by the width /// of `prefix`, and `prefix` will appear at the start of the first line. pub fn new(prefix: &'static str) -> PrettyIndent { - PrettyIndent { prefix: prefix, level: 0 } + PrettyIndent { prefix, level: 0 } } /// Increase indentation level. diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 31a3cc1b4..faa3abcf3 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -629,7 +629,7 @@ impl<'a> TcpOption<'a> { option = TcpOption::SackRange(sack_ranges); }, (_, _) => - option = TcpOption::Unknown { kind: kind, data: data } + option = TcpOption::Unknown { kind, data } } } } From 1ff8edf201a3a6eb8eb0104f6dba920336766256 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:19:48 -0800 Subject: [PATCH 045/566] Clean up a couple of if-blocks These were flagged by `cargo clippy`: warning: this `else { if .. }` block can be collapsed --- examples/benchmark.rs | 2 ++ examples/loopback.rs | 1 + src/iface/ethernet.rs | 6 ++---- src/wire/icmpv6.rs | 8 ++------ src/wire/ip.rs | 8 +++----- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 92d8c460b..ce307db2f 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -1,3 +1,5 @@ +#![allow(clippy::collapsible_if)] + mod utils; use std::cmp; diff --git a/examples/loopback.rs b/examples/loopback.rs index a291aa4b3..9d0c39bec 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(unused_mut)] +#![allow(clippy::collapsible_if)] #[cfg(feature = "std")] #[allow(dead_code)] diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 2d83aaaee..9ab25c261 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1192,10 +1192,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { if flags.contains(NdiscNeighborFlags::OVERRIDE) { self.neighbor_cache.fill(ip_addr, lladdr, timestamp) - } else { - if !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, lladdr, timestamp) - } + } else if !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { + self.neighbor_cache.fill(ip_addr, lladdr, timestamp) } }, _ => (), diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 14f1f5254..b3e49a1e9 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -271,14 +271,10 @@ impl> Packet { /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); - if len < field::HEADER_END { + if len < field::HEADER_END || len < self.header_len() { Err(Error::Truncated) } else { - if len < self.header_len() { - Err(Error::Truncated) - } else { - Ok(()) - } + Ok(()) } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 83eb4df19..a675c426a 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -201,11 +201,9 @@ impl Address { } else { ones = false; } - } else { - if one { - // 1 where 0 was expected - return None - } + } else if one { + // 1 where 0 was expected + return None } mask >>= 1; } From e4e4e3ab802bd89fb24fb27abc2846d4c413ad5b Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:44:51 -0800 Subject: [PATCH 046/566] Remove some unneeded imports and a variable These were flagged by `cargo clippy`: warning: returning the result of a `let` binding from a block warning: this import is redundant --- src/iface/ethernet.rs | 5 ++--- src/phy/sys/linux.rs | 3 --- src/phy/sys/mod.rs | 1 - src/phy/sys/raw_socket.rs | 1 - src/phy/sys/tap_interface.rs | 1 - 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 9ab25c261..2ac8c7879 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1676,14 +1676,13 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn igmp_leave_packet<'any>(&self, group_addr: Ipv4Address) -> Option> { self.ipv4_address().map(|iface_addr| { let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; - let pkt = IpPacket::Igmp((Ipv4Repr { + IpPacket::Igmp((Ipv4Repr { src_addr: iface_addr, dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, protocol: IpProtocol::Igmp, payload_len: igmp_repr.buffer_len(), hop_limit: 1, - }, igmp_repr)); - pkt + }, igmp_repr)) }) } } diff --git a/src/phy/sys/linux.rs b/src/phy/sys/linux.rs index fdf52baf3..f582770ba 100644 --- a/src/phy/sys/linux.rs +++ b/src/phy/sys/linux.rs @@ -1,5 +1,3 @@ -use libc; - #[cfg(any(feature = "phy-raw_socket", feature = "phy-tap_interface"))] pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -14,4 +12,3 @@ pub const TUNSETIFF: libc::c_ulong = 0x400454CA; pub const IFF_TAP: libc::c_int = 0x0002; #[cfg(feature = "phy-tap_interface")] pub const IFF_NO_PI: libc::c_int = 0x1000; - diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index cfc9a88fb..6f1665be3 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -1,6 +1,5 @@ #![allow(unsafe_code)] -use libc; use std::{mem, ptr, io}; use std::os::unix::io::RawFd; use crate::time::Duration; diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 368f62d1f..f45662c1c 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -1,6 +1,5 @@ use std::{mem, io}; use std::os::unix::io::{RawFd, AsRawFd}; -use libc; use super::*; use crate::wire::EthernetFrame; diff --git a/src/phy/sys/tap_interface.rs b/src/phy/sys/tap_interface.rs index 29b1ffa92..9608e6e21 100644 --- a/src/phy/sys/tap_interface.rs +++ b/src/phy/sys/tap_interface.rs @@ -1,6 +1,5 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; -use libc; use super::*; use crate::wire::EthernetFrame; From 902817e2aa8b6cc2f689bd35bdf24091fc9702b8 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 21:49:51 -0800 Subject: [PATCH 047/566] Use iterators instead of manually looping These were flagged by `cargo clippy`: warning: the loop variable is used to index I've verified that this doesn't increase the size of consuming binaries. Pretty impressive. I tested this with a project that uses the following features: ethernet, proto-dhcpv4, socket-tcp. --- src/parsers.rs | 8 ++++---- src/wire/dhcpv4.rs | 11 ++++------- src/wire/ipv6.rs | 10 ++++------ 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/parsers.rs b/src/parsers.rs index 0400147a7..36bad299f 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -121,8 +121,8 @@ impl<'a> Parser<'a> { #[cfg(feature = "ethernet")] fn accept_mac_joined_with(&mut self, separator: u8) -> Result { let mut octets = [0u8; 6]; - for n in 0..6 { - octets[n] = self.accept_number(2, 0x100, true)? as u8; + for (n, octet) in octets.iter_mut().enumerate() { + *octet = self.accept_number(2, 0x100, true)? as u8; if n != 5 { self.accept_char(separator)?; } @@ -270,8 +270,8 @@ impl<'a> Parser<'a> { fn accept_ipv4_octets(&mut self) -> Result<[u8; 4]> { let mut octets = [0u8; 4]; - for n in 0..4 { - octets[n] = self.accept_number(3, 0x100, false)? as u8; + for (n, octet) in octets.iter_mut().enumerate() { + *octet = self.accept_number(3, 0x100, false)? as u8; if n != 3 { self.accept_char(b'.')?; } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index fbc09e132..64e9a9c4a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -759,14 +759,11 @@ impl<'a> Repr<'a> { parameter_request_list = Some(data); } DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => { - let mut dns_servers_inner = [None; 3]; - for i in 0..3 { - let offset = 4 * i; - let end = offset + 4; - if end > data.len() { break } - dns_servers_inner[i] = Some(Ipv4Address::from_bytes(&data[offset..end])); + let mut servers = [None; 3]; + for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) { + *server = Some(Ipv4Address::from_bytes(chunk)); } - dns_servers = Some(dns_servers_inner); + dns_servers = Some(servers); } DhcpOption::Other {..} => {} } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 8d93cd814..8f3d1bd91 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -78,9 +78,8 @@ impl Address { pub fn from_parts(data: &[u16]) -> Address { assert!(data.len() >= 8); let mut bytes = [0; 16]; - for word_idx in 0..8 { - let byte_idx = word_idx * 2; - NetworkEndian::write_u16(&mut bytes[byte_idx..(byte_idx + 2)], data[word_idx]); + for (word_idx, chunk) in bytes.chunks_mut(2).enumerate() { + NetworkEndian::write_u16(chunk, data[word_idx]); } Address(bytes) } @@ -91,9 +90,8 @@ impl Address { /// The function panics if `data` is not 8 words long. pub fn write_parts(&self, data: &mut [u16]) { assert!(data.len() >= 8); - for i in 0..8 { - let byte_idx = i * 2; - data[i] = NetworkEndian::read_u16(&self.0[byte_idx..(byte_idx + 2)]); + for (i, chunk) in self.0.chunks(2).enumerate() { + data[i] = NetworkEndian::read_u16(chunk); } } From ac67edd318a25be3ab8fe219af776c0e9c459c64 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 21:09:07 -0800 Subject: [PATCH 048/566] Silence a few warnings from clippy These were flagged by `cargo clippy`: warning: the operation is ineffective. Consider reducing it to `number` warning: this function has too many arguments (8/7) warning: you should consider adding a `Default` implementation for `phy::loopback::Loopback` I like the code better as it is. --- src/lib.rs | 1 + src/phy/loopback.rs | 1 + src/wire/ip.rs | 1 + src/wire/ipv6.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f357ca6b4..b22c08318 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::manual_non_exhaustive)] #![allow(clippy::match_like_matches_macro)] #![allow(clippy::redundant_field_names)] +#![allow(clippy::identity_op)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index fdbd21ec0..b41535a62 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -19,6 +19,7 @@ pub struct Loopback { queue: VecDeque>, } +#[allow(clippy::new_without_default)] impl Loopback { /// Creates a loopback device. /// diff --git a/src/wire/ip.rs b/src/wire/ip.rs index a675c426a..1b6acae56 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -108,6 +108,7 @@ impl Address { /// Create an address wrapping an IPv6 address with the given octets. #[cfg(feature = "proto-ipv6")] + #[allow(clippy::too_many_arguments)] pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7)) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 8f3d1bd91..731d288d7 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -47,6 +47,7 @@ impl Address { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); /// Construct an IPv6 address from parts. + #[allow(clippy::too_many_arguments)] pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { let mut addr = [0u8; 16]; From 5361a7b73c8826a9750863de1953ebbabcddc89b Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 21:09:33 -0800 Subject: [PATCH 049/566] Silence warning about mapping to unit type This was flagged by `cargo clippy`: warning: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` I decided to disable this because it was suggesting changes like the following and I prefer the original: @@ -1 +1 @@ -self.waker.take().map(|w| w.wake()); +if let Some(w) = self.waker.take() { w.wake() } --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index b22c08318..22bd6c47e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::match_like_matches_macro)] #![allow(clippy::redundant_field_names)] #![allow(clippy::identity_op)] +#![allow(clippy::option_map_unit_fn)] #[cfg(feature = "alloc")] extern crate alloc; From 400432802e429863a9858595983a248e4bde1b08 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 22:17:46 -0800 Subject: [PATCH 050/566] Remove an unneeded semicolon --- examples/dhcp_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index a751e59f3..daabc9c5f 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -95,6 +95,6 @@ fn main() { iface.poll_delay(&sockets, timestamp) .map(|sockets_timeout| timeout = sockets_timeout); phy_wait(fd, Some(timeout)) - .unwrap_or_else(|e| println!("Wait: {:?}", e));; + .unwrap_or_else(|e| println!("Wait: {:?}", e)); } } From 0f69c60e4391835c16e5bd12db544f15b641891f Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 23:05:48 -0800 Subject: [PATCH 051/566] Remove unnecessary clones These were flagged by `cargo clippy`: warning: using `clone` on a `Copy` type --- src/iface/ethernet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 2ac8c7879..60397cf3e 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -295,11 +295,11 @@ impl<'a> IpPacket<'a> { pub(crate) fn ip_repr(&self) -> IpRepr { match self { #[cfg(feature = "proto-ipv4")] - IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(ipv4_repr.clone()), + IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), #[cfg(feature = "proto-igmp")] - IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(ipv4_repr.clone()), + IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(ipv6_repr.clone()), + IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr), #[cfg(feature = "socket-raw")] IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), #[cfg(feature = "socket-udp")] From cc7fe85d702dab3678a748d1b02ee142010fa2b5 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 23:06:48 -0800 Subject: [PATCH 052/566] Collapse redundant closure This was flagged by `cargo clippy`: warning: redundant closure found --- src/iface/ethernet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 60397cf3e..3d642635a 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1441,7 +1441,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { match tcp_socket.process(timestamp, &ip_repr, &tcp_repr) { // The packet is valid and handled by socket. - Ok(reply) => return Ok(reply.map(|x| IpPacket::Tcp(x))), + Ok(reply) => return Ok(reply.map(IpPacket::Tcp)), // The packet is malformed, or doesn't match the socket state, // or the socket buffer is full. Err(e) => return Err(e) From cd2f2a45bbbb347bcbb3d526180ab7d8a37f3f5e Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 28 Dec 2020 22:26:29 -0800 Subject: [PATCH 053/566] Enforce that there are no warnings in clippy check --- .github/workflows/clippy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d55d697f1..81848f9f2 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -23,3 +23,4 @@ jobs: - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + args: -- -D warnings From ac5099551651e1ee1a4515a4ca3433195446d944 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Jan 2021 01:02:35 +0100 Subject: [PATCH 054/566] More clippy fixes. --- src/iface/ethernet.rs | 2 +- src/parsers.rs | 6 +++--- src/socket/tcp.rs | 2 +- src/storage/assembler.rs | 11 +++++++---- src/storage/ring_buffer.rs | 2 +- src/wire/ipv6option.rs | 14 ++++---------- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 3d642635a..7a5d73d71 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1519,7 +1519,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } } - fn has_neighbor<'a>(&self, addr: &'a IpAddress, timestamp: Instant) -> bool { + fn has_neighbor(&self, addr: &IpAddress, timestamp: Instant) -> bool { match self.route(addr, timestamp) { Ok(routed_addr) => { self.neighbor_cache diff --git a/src/parsers.rs b/src/parsers.rs index 36bad299f..bf87cdcae 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -88,11 +88,11 @@ impl<'a> Parser<'a> { fn accept_digit(&mut self, hex: bool) -> Result { let digit = self.advance()?; - if digit >= b'0' && digit <= b'9' { + if (b'0'..=b'9').contains(&digit) { Ok(digit - b'0') - } else if hex && digit >= b'a' && digit <= b'f' { + } else if hex && (b'a'..=b'f').contains(&digit) { Ok(digit - b'a' + 10) - } else if hex && digit >= b'A' && digit <= b'F' { + } else if hex && (b'A'..=b'F').contains(&digit) { Ok(digit - b'A' + 10) } else { Err(()) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8ed45b91a..c88f4ef05 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1416,7 +1416,7 @@ impl<'a> TcpSocket<'a> { payload_len, payload_offset); self.rx_buffer.write_unallocated(payload_offset, repr.payload); } - Err(()) => { + Err(_) => { net_debug!("{}:{}:{}: assembler: too many holes to add {} octets at offset {}", self.meta.handle, self.local_endpoint, self.remote_endpoint, payload_len, payload_offset); diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 714cf3d08..159d93d2f 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -1,5 +1,8 @@ use core::fmt; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TooManyHolesError; + /// A contiguous chunk of absent data, followed by a contiguous chunk of present data. #[derive(Debug, Clone, Copy, PartialEq, Eq)] struct Contig { @@ -148,10 +151,10 @@ impl Assembler { } /// Add a contig at the given index, and return a pointer to it. - fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, ()> { + fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> { debug_assert!(!self.contigs[at].is_empty()); - if !self.back().is_empty() { return Err(()) } + if !self.back().is_empty() { return Err(TooManyHolesError) } for i in (at + 1..self.contigs.len()).rev() { self.contigs[i] = self.contigs[i - 1]; @@ -163,7 +166,7 @@ impl Assembler { /// Add a new contiguous range to the assembler, and return `Ok(())`, /// or return `Err(())` if too many discontiguities are already recorded. - pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result<(), ()> { + pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result<(), TooManyHolesError> { let mut index = 0; while index != self.contigs.len() && size != 0 { let contig = self.contigs[index]; @@ -413,7 +416,7 @@ mod test { } // Maximum of allowed holes is reached let assr_before = assr.clone(); - assert_eq!(assr.add(1, 3), Err(())); + assert_eq!(assr.add(1, 3), Err(TooManyHolesError)); assert_eq!(assr_before, assr); } diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 873140e95..6ea46ac12 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -129,7 +129,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// or return `Err(Error::Exhausted)` if the buffer is full. /// /// This function is a shortcut for `ring_buf.enqueue_one_with(Ok)`. - pub fn enqueue_one<'b>(&'b mut self) -> Result<&'b mut T> { + pub fn enqueue_one(&mut self) -> Result<&mut T> { self.enqueue_one_with(Ok) } diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index d96969c99..18e5dcea4 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -307,14 +307,6 @@ impl<'a> Ipv6OptionsIterator<'a> { length, data } } - - /// Helper function to return an error in the implementation - /// of `Iterator`. - #[inline] - fn return_err(&mut self, err: Error) -> Option>> { - self.hit_error = true; - Some(Err(err)) - } } impl<'a> Iterator for Ipv6OptionsIterator<'a> { @@ -332,12 +324,14 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> { Some(Ok(repr)) } Err(e) => { - self.return_err(e) + self.hit_error = true; + Some(Err(e)) } } } Err(e) => { - self.return_err(e) + self.hit_error = true; + Some(Err(e)) } } } else { From b5a4def9dd02c1b30f5e80acc275c7be4424ad77 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Jan 2021 01:37:06 +0100 Subject: [PATCH 055/566] Make some time funcs const. --- src/time.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/time.rs b/src/time.rs index 82924dd5a..719ea8419 100644 --- a/src/time.rs +++ b/src/time.rs @@ -50,19 +50,19 @@ impl Instant { /// The fractional number of milliseconds that have passed /// since the beginning of time. - pub fn millis(&self) -> i64 { + pub const fn millis(&self) -> i64 { self.millis % 1000 } /// The number of whole seconds that have passed since the /// beginning of time. - pub fn secs(&self) -> i64 { + pub const fn secs(&self) -> i64 { self.millis / 1000 } /// The total number of milliseconds that have passed since /// the biginning of time. - pub fn total_millis(&self) -> i64 { + pub const fn total_millis(&self) -> i64 { self.millis } } @@ -141,27 +141,27 @@ pub struct Duration { impl Duration { /// Create a new `Duration` from a number of milliseconds. - pub fn from_millis(millis: u64) -> Duration { + pub const fn from_millis(millis: u64) -> Duration { Duration { millis } } /// Create a new `Instant` from a number of seconds. - pub fn from_secs(secs: u64) -> Duration { + pub const fn from_secs(secs: u64) -> Duration { Duration { millis: secs * 1000 } } /// The fractional number of milliseconds in this `Duration`. - pub fn millis(&self) -> u64 { + pub const fn millis(&self) -> u64 { self.millis % 1000 } /// The number of whole seconds in this `Duration`. - pub fn secs(&self) -> u64 { + pub const fn secs(&self) -> u64 { self.millis / 1000 } /// The total number of milliseconds in this `Duration`. - pub fn total_millis(&self) -> u64 { + pub const fn total_millis(&self) -> u64 { self.millis } } From 5117af776ae0ac474396f58e7e256b25354f75fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Jan 2021 03:11:03 +0100 Subject: [PATCH 056/566] tcp: Add RTT estimation. --- src/socket/tcp.rs | 168 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 24 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index c88f4ef05..63a6903be 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -54,6 +54,99 @@ impl fmt::Display for State { } } +// Conservative initial RTT estimate. +const RTTE_INITIAL_RTT: u32 = 300; +const RTTE_INITIAL_DEV: u32 = 100; + +// Minimum "safety margin" for the RTO that kicks in when the +// variance gets very low. +const RTTE_MIN_MARGIN: u32 = 5; + +const RTTE_MIN_RTO: u32 = 10; +const RTTE_MAX_RTO: u32 = 10000; + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct RttEstimator { + // Using u32 instead of Duration to save space (Duration is i64) + rtt: u32, + deviation: u32, + timestamp: Option<(Instant, TcpSeqNumber)>, + max_seq_sent: Option, + rto_count: u8, +} + +impl Default for RttEstimator { + fn default() -> Self { + Self { + rtt: RTTE_INITIAL_RTT, + deviation: RTTE_INITIAL_DEV, + timestamp: None, + max_seq_sent: None, + rto_count: 0, + } + } +} + +impl RttEstimator { + fn retransmission_timeout(&self) -> Duration { + let margin = RTTE_MIN_MARGIN.max(self.deviation * 4); + let ms = (self.rtt + margin).max(RTTE_MIN_RTO).min(RTTE_MAX_RTO); + Duration::from_millis(ms as u64) + } + + fn sample(&mut self, new_rtt: u32) { + // "Congestion Avoidance and Control", Van Jacobson, Michael J. Karels, 1988 + self.rtt = (self.rtt * 7 + new_rtt + 7) / 8; + let diff = (self.rtt as i32 - new_rtt as i32 ).abs() as u32; + self.deviation = (self.deviation * 3 + diff + 3) / 4; + + self.rto_count = 0; + + let rto = self.retransmission_timeout().millis(); + net_trace!("rtte: sample={:?} rtt={:?} dev={:?} rto={:?}", new_rtt, self.rtt, self.deviation, rto); + } + + fn on_send(&mut self, timestamp: Instant, seq: TcpSeqNumber) { + if self.max_seq_sent.map(|max_seq_sent| seq > max_seq_sent).unwrap_or(true) { + self.max_seq_sent = Some(seq); + if self.timestamp.is_none() { + self.timestamp = Some((timestamp, seq)); + net_trace!("rtte: sampling at seq={:?}", seq); + } + } + } + + fn on_ack(&mut self, timestamp: Instant, seq: TcpSeqNumber) { + if let Some((sent_timestamp, sent_seq)) = self.timestamp { + if seq >= sent_seq { + self.sample((timestamp - sent_timestamp).millis() as u32); + self.timestamp = None; + } + } + } + + fn on_retransmit(&mut self) { + if self.timestamp.is_some() { + net_trace!("rtte: abort sampling due to retransmit"); + } + self.timestamp = None; + self.rto_count = self.rto_count.saturating_add(1); + if self.rto_count >= 3 { + // This happens in 2 scenarios: + // - The RTT is higher than the initial estimate + // - The network conditions change, suddenly making the RTT much higher + // In these cases, the estimator can get stuck, because it can't sample because + // all packets sent would incur a retransmit. To avoid this, force an estimate + // increase if we see 3 consecutive retransmissions without any successful sample. + self.rto_count = 0; + self.rtt *= 2; + let rto = self.retransmission_timeout().millis(); + net_trace!("rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", self.rtt, self.deviation, rto); + } + } +} + #[derive(Debug, Clone, Copy, PartialEq)] enum Timer { Idle { @@ -69,7 +162,6 @@ enum Timer { } } -const RETRANSMIT_DELAY: Duration = Duration { millis: 100 }; const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; impl Default for Timer { @@ -140,12 +232,12 @@ impl Timer { } } - fn set_for_retransmit(&mut self, timestamp: Instant) { + fn set_for_retransmit(&mut self, timestamp: Instant, delay: Duration) { match *self { Timer::Idle { .. } | Timer::FastRetransmit { .. } => { *self = Timer::Retransmit { - expires_at: timestamp + RETRANSMIT_DELAY, - delay: RETRANSMIT_DELAY, + expires_at: timestamp + delay, + delay: delay, } } Timer::Retransmit { expires_at, delay } @@ -189,6 +281,7 @@ pub struct TcpSocket<'a> { pub(crate) meta: SocketMeta, state: State, timer: Timer, + rtte: RttEstimator, assembler: Assembler, rx_buffer: SocketBuffer<'a>, rx_fin_received: bool, @@ -279,6 +372,7 @@ impl<'a> TcpSocket<'a> { meta: SocketMeta::default(), state: State::Closed, timer: Timer::default(), + rtte: RttEstimator::default(), assembler: Assembler::new(rx_buffer.capacity()), tx_buffer: tx_buffer, rx_buffer: rx_buffer, @@ -463,6 +557,7 @@ impl<'a> TcpSocket<'a> { self.state = State::Closed; self.timer = Timer::default(); + self.rtte = RttEstimator::default(); self.assembler = Assembler::new(self.rx_buffer.capacity()); self.tx_buffer.clear(); self.rx_buffer.clear(); @@ -1154,6 +1249,8 @@ impl<'a> TcpSocket<'a> { self.meta.handle, self.local_endpoint, self.remote_endpoint); ack_of_fin = true; } + + self.rtte.on_ack(timestamp, ack_number); } } @@ -1538,6 +1635,7 @@ impl<'a> TcpSocket<'a> { self.meta.handle, self.local_endpoint, self.remote_endpoint, retransmit_delta); self.remote_last_seq = self.local_seq_no; + self.rtte.on_retransmit(); } } @@ -1723,10 +1821,14 @@ impl<'a> TcpSocket<'a> { self.remote_last_ack = repr.ack_number; self.remote_last_win = repr.window_len; + if repr.segment_len() > 0 { + self.rtte.on_send(timestamp, 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.timer.set_for_retransmit(timestamp, self.rtte.retransmission_timeout()); } if self.state == State::Closed { @@ -3646,7 +3748,7 @@ mod test { ..RECV_TEMPL })); recv!(s, time 1050, Err(Error::Exhausted)); - recv!(s, time 1100, Ok(TcpRepr { + recv!(s, time 2000, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), payload: &b"abcdef"[..], @@ -3678,21 +3780,21 @@ mod test { recv!(s, time 50, Err(Error::Exhausted)); - recv!(s, time 100, Ok(TcpRepr { + recv!(s, time 1000, Ok(TcpRepr { control: TcpControl::None, seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), payload: &b"abcdef"[..], ..RECV_TEMPL }), exact); - recv!(s, time 150, Ok(TcpRepr { + recv!(s, time 1500, Ok(TcpRepr { control: TcpControl::Psh, seq_number: LOCAL_SEQ + 1 + 6, ack_number: Some(REMOTE_SEQ + 1), payload: &b"012345"[..], ..RECV_TEMPL }), exact); - recv!(s, time 200, Err(Error::Exhausted)); + recv!(s, time 1550, Err(Error::Exhausted)); } #[test] @@ -3705,7 +3807,7 @@ mod test { max_seg_size: Some(BASE_MSS), ..RECV_TEMPL })); - recv!(s, time 150, Ok(TcpRepr { // retransmit + recv!(s, time 750, Ok(TcpRepr { // retransmit control: TcpControl::Syn, seq_number: LOCAL_SEQ, ack_number: Some(REMOTE_SEQ + 1), @@ -4527,9 +4629,9 @@ mod test { #[test] fn test_established_timeout() { let mut s = socket_established(); - s.set_timeout(Some(Duration::from_millis(200))); + 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(450))); + assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(1250))); s.send_slice(b"abcdef").unwrap(); assert_eq!(s.poll_at(), PollAt::Now); recv!(s, time 255, Ok(TcpRepr { @@ -4538,15 +4640,15 @@ mod test { payload: &b"abcdef"[..], ..RECV_TEMPL })); - assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(355))); - recv!(s, time 355, Ok(TcpRepr { + assert_eq!(s.poll_at(), 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(455))); - recv!(s, time 500, Ok(TcpRepr { + assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(1255))); + recv!(s, time 1255, Ok(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 1 + 6, ack_number: Some(REMOTE_SEQ + 1), @@ -4596,15 +4698,14 @@ mod test { #[test] fn test_fin_wait_1_timeout() { let mut s = socket_fin_wait_1(); - s.set_timeout(Some(Duration::from_millis(200))); + s.set_timeout(Some(Duration::from_millis(1000))); recv!(s, time 100, Ok(TcpRepr { control: TcpControl::Fin, seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL })); - assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(200))); - recv!(s, time 400, Ok(TcpRepr { + recv!(s, time 1100, Ok(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 1 + 1, ack_number: Some(REMOTE_SEQ + 1), @@ -4616,15 +4717,14 @@ mod test { #[test] fn test_last_ack_timeout() { let mut s = socket_last_ack(); - s.set_timeout(Some(Duration::from_millis(200))); + s.set_timeout(Some(Duration::from_millis(1000))); recv!(s, time 100, Ok(TcpRepr { control: TcpControl::Fin, seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 1), ..RECV_TEMPL })); - assert_eq!(s.poll_at(), PollAt::Time(Instant::from_millis(200))); - recv!(s, time 400, Ok(TcpRepr { + recv!(s, time 1100, Ok(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 1 + 1, ack_number: Some(REMOTE_SEQ + 1 + 1), @@ -5052,13 +5152,14 @@ mod test { #[test] fn test_timer_retransmit() { + const RTO: Duration = Duration::from_millis(100); let mut r = Timer::default(); assert_eq!(r.should_retransmit(Instant::from_secs(1)), None); - r.set_for_retransmit(Instant::from_millis(1000)); + r.set_for_retransmit(Instant::from_millis(1000), RTO); assert_eq!(r.should_retransmit(Instant::from_millis(1000)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1050)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1101)), Some(Duration::from_millis(101))); - r.set_for_retransmit(Instant::from_millis(1101)); + r.set_for_retransmit(Instant::from_millis(1101), RTO); assert_eq!(r.should_retransmit(Instant::from_millis(1101)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1150)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1200)), None); @@ -5067,4 +5168,23 @@ mod test { assert_eq!(r.should_retransmit(Instant::from_millis(1350)), None); } + #[test] + fn test_rtt_estimator() { + #[cfg(feature = "log")] + init_logger(); + + let mut r = RttEstimator::default(); + + let rtos = &[ + 751, 766, 755, 731, 697, 656, 613, 567, + 523, 484, 445, 411, 378, 350, 322, 299, + 280, 261, 243, 229, 215, 206, 197, 188 + ]; + + for &rto in rtos { + r.sample(100); + assert_eq!(r.retransmission_timeout(), Duration::from_millis(rto)); + } + } + } From b290069cc106557ec879441491c84c0850d62869 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 25 Dec 2020 23:56:39 -0800 Subject: [PATCH 057/566] Various cleanups These were flagged by `cargo clippy`: warning: using `clone` on a `Copy` type warning: passing a unit value to a function warning: redundant closure found warning: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --- src/iface/ethernet.rs | 4 ++-- src/lib.rs | 1 + src/socket/raw.rs | 10 +++++----- src/storage/ring_buffer.rs | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 7a5d73d71..7e125487a 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1757,7 +1757,7 @@ mod test { let mut pkts = Vec::new(); while let Some((rx, _tx)) = iface.device.receive() { rx.consume(timestamp, |pkt| { - pkts.push(pkt.iter().cloned().collect()); + pkts.push(pkt.to_vec()); Ok(()) }).unwrap(); } @@ -2567,7 +2567,7 @@ mod test { // Leave multicast groups let timestamp = Instant::now(); for group in &groups { - iface.leave_multicast_group(group.clone(), timestamp) + iface.leave_multicast_group(*group, timestamp) .unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index 22bd6c47e..0eb50e43b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::redundant_field_names)] #![allow(clippy::identity_op)] #![allow(clippy::option_map_unit_fn)] +#![allow(clippy::unit_arg)] #[cfg(feature = "alloc")] extern crate alloc; diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 56b66d513..61b771d25 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -473,14 +473,14 @@ mod test { { let mut socket = ipv4_locals::socket(buffer(0), buffer(2)); - let mut wrong_version = ipv4_locals::PACKET_BYTES.clone(); + let mut wrong_version = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()), Ok(())); - let mut wrong_protocol = ipv4_locals::PACKET_BYTES.clone(); + 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(())); @@ -491,14 +491,14 @@ mod test { { let mut socket = ipv6_locals::socket(buffer(0), buffer(2)); - let mut wrong_version = ipv6_locals::PACKET_BYTES.clone(); + let mut wrong_version = ipv6_locals::PACKET_BYTES; Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()), Ok(())); - let mut wrong_protocol = ipv6_locals::PACKET_BYTES.clone(); + 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(())); @@ -514,7 +514,7 @@ mod test { let mut socket = ipv4_locals::socket(buffer(1), buffer(0)); assert!(!socket.can_recv()); - let mut cksumd_packet = ipv4_locals::PACKET_BYTES.clone(); + let mut cksumd_packet = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut cksumd_packet).fill_checksum(); assert_eq!(socket.recv(), Err(Error::Exhausted)); diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 6ea46ac12..063c9f3ae 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -405,7 +405,7 @@ mod test { assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>, Err(Error::Exhausted)); - ring.enqueue_one_with(|e| Ok(e)).unwrap(); + ring.enqueue_one_with(Ok).unwrap(); assert!(!ring.is_empty()); assert!(!ring.is_full()); From 1cbc38d0ea1787e73ea6540a8febfefb05ef2dd7 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Tue, 29 Dec 2020 18:40:28 -0800 Subject: [PATCH 058/566] Clean up examples These were flagged by `cargo clippy`: warning: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` warning: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent warning: using `write!()` with a format string that ends in a single newline warning: useless conversion to the same type: `smoltcp::wire::Ipv4Address` warning: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` warning: returning the result of a `let` binding from a block warning: use of `unwrap_or` followed by a function call --- examples/dhcp_client.rs | 10 +++++----- examples/loopback.rs | 4 +--- examples/multicast.rs | 2 +- examples/ping.rs | 2 +- examples/server.rs | 2 +- examples/utils.rs | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index daabc9c5f..7fa009c0d 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -1,3 +1,4 @@ +#![allow(clippy::option_map_unit_fn)] mod utils; use std::collections::BTreeMap; @@ -57,10 +58,10 @@ fn main() { }); config.map(|config| { println!("DHCP config: {:?}", config); - match config.address { - Some(cidr) => if cidr != prev_cidr { + if let Some(cidr) = config.address { + if cidr != prev_cidr { iface.update_ip_addrs(|addrs| { - addrs.iter_mut().nth(0) + addrs.iter_mut().next() .map(|addr| { *addr = IpCidr::Ipv4(cidr); }); @@ -68,11 +69,10 @@ fn main() { prev_cidr = cidr; println!("Assigned a new IPv4 address: {}", cidr); } - _ => {} } config.router.map(|router| iface.routes_mut() - .add_default_ipv4_route(router.into()) + .add_default_ipv4_route(router) .unwrap() ); iface.routes_mut() diff --git a/examples/loopback.rs b/examples/loopback.rs index 9d0c39bec..c84f286c0 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -76,9 +76,7 @@ fn main() { utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/true); - - device + utils::parse_middleware_options(&mut matches, device, /*loopback=*/true) }; let mut neighbor_cache_entries = [None; 8]; diff --git a/examples/multicast.rs b/examples/multicast.rs index 92f1876cd..e637467c6 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -83,7 +83,7 @@ fn main() { // For display purposes only - normally we wouldn't process incoming IGMP packets // in the application layer socket.recv() - .and_then(|payload| Ipv4Packet::new_checked(payload)) + .and_then(Ipv4Packet::new_checked) .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) diff --git a/examples/ping.rs b/examples/ping.rs index 411e450c4..1d290d4f6 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -74,7 +74,7 @@ fn main() { let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4); let interval = matches.opt_str("interval") .map(|s| Duration::from_secs(u64::from_str(&s).unwrap())) - .unwrap_or(Duration::from_secs(1)); + .unwrap_or_else(|| Duration::from_secs(1)); let timeout = Duration::from_secs( matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5) ); diff --git a/examples/server.rs b/examples/server.rs index 572c84ae3..95675c7e3 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -109,7 +109,7 @@ fn main() { if socket.can_send() { debug!("tcp:6969 send greeting"); - write!(socket, "hello\n").unwrap(); + writeln!(socket, "hello").unwrap(); debug!("tcp:6969 close"); socket.close(); } diff --git a/examples/utils.rs b/examples/utils.rs index 09d43d477..02b2de0bb 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -42,7 +42,7 @@ pub fn setup_logging_with_clock(filter: &str, since_startup: F) }) .filter(None, LevelFilter::Trace) .parse(filter) - .parse(&env::var("RUST_LOG").unwrap_or("".to_owned())) + .parse(&env::var("RUST_LOG").unwrap_or_else(|_| "".to_owned())) .init(); } @@ -68,7 +68,7 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { Ok(matches) => { if matches.opt_present("h") || matches.free.len() != free.len() { let brief = format!("Usage: {} [OPTION]... {}", - env::args().nth(0).unwrap(), free.join(" ")); + env::args().next().unwrap(), free.join(" ")); print!("{}", options.usage(&brief)); process::exit(if matches.free.len() != free.len() { 1 } else { 0 }) } From 98f9f8e58271ddef48b09c2308719f32e875f6e7 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 4 Jan 2021 10:06:57 -0800 Subject: [PATCH 059/566] Allow DeviceCapabilities to be initialized This was flagged by `cargo clippy`: warning: field assignment outside of initializer for an instance created with Default::default() This changes the visibility of the dummy field to be public, but only to the crate. --- src/phy/mod.rs | 2 +- src/socket/tcp.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 8ef699141..adf05974b 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -217,7 +217,7 @@ pub struct DeviceCapabilities { /// Only present to prevent people from trying to initialize every field of DeviceLimits, /// which would not let us add new fields in the future. - dummy: () + pub(crate) dummy: () } /// An interface for sending and receiving raw network frames. diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 63a6903be..0d2d57115 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1972,8 +1972,10 @@ mod test { fn recv(socket: &mut TcpSocket, timestamp: Instant, mut f: F) where F: FnMut(Result) { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = 1520; + let caps = DeviceCapabilities { + max_transmission_unit: 1520, + ..Default::default() + }; let result = socket.dispatch(timestamp, &caps, |(ip_repr, tcp_repr)| { let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap(); @@ -4821,8 +4823,10 @@ mod test { #[test] fn test_set_hop_limit() { let mut s = socket_syn_received(); - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = 1520; + let caps = DeviceCapabilities { + max_transmission_unit: 1520, + ..Default::default() + }; s.set_hop_limit(Some(0x2a)); assert_eq!(s.dispatch(Instant::from_millis(0), &caps, |(ip_repr, _)| { From 732cddb17de205b38425cf71f6a8c2df6bcb5afd Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Tue, 29 Dec 2020 18:55:35 -0800 Subject: [PATCH 060/566] Enable clippy on tests and examples Might as well run the lints on our tests and examples. When I first started doing this cleanup, I thought this was the default, but I must have run `cargo clippy --all-targets` at some point in there. --- .github/workflows/clippy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 81848f9f2..76985f4b0 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -23,4 +23,4 @@ jobs: - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - args: -- -D warnings + args: --tests --examples -- -D warnings From 0b0f96e65a192dbfa252d40c16cf7feb2deee3d3 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 4 Jan 2021 10:42:26 -0800 Subject: [PATCH 061/566] Pin clippy check to 1.49.0 It can be rather surprising when new lints pop up when a new stable toolchain is released. Let's pin this check to a specific version to avoid those surprises. --- .github/workflows/clippy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 76985f4b0..585aab7f6 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -17,7 +17,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: 1.49.0 override: true components: clippy - uses: actions-rs/clippy-check@v1 From e9e147737328f76f8a125008f6d4a0101d73791c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Jan 2021 00:52:17 +0100 Subject: [PATCH 062/566] tcp: add Delayed ACK --- src/socket/mod.rs | 10 --- src/socket/tcp.rs | 198 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 192 insertions(+), 16 deletions(-) diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 24afb5a02..964e9b987 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -71,16 +71,6 @@ pub(crate) enum PollAt { Ingress, } -impl PollAt { - #[cfg(feature = "socket-tcp")] - fn is_ingress(&self) -> bool { - match *self { - PollAt::Ingress => true, - _ => false, - } - } -} - /// A network socket. /// /// This enumeration abstracts the various types of sockets based on the IP protocol. diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 0d2d57115..38b31733e 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -162,6 +162,7 @@ enum Timer { } } +const ACK_DELAY_DEFAULT: Duration = Duration { millis: 10 }; const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; impl Default for Timer { @@ -341,6 +342,12 @@ pub struct TcpSocket<'a> { /// each other which have the same ACK number. local_rx_dup_acks: u8, + /// Duration for Delayed ACK. If None no ACKs will be delayed. + ack_delay: Option, + /// Delayed ack timer. If set, packets containing exclusively + /// ACK or window updates (ie, no data) won't be sent until expiry. + ack_delay_until: Option, + #[cfg(feature = "async")] rx_waker: WakerRegistration, #[cfg(feature = "async")] @@ -397,6 +404,8 @@ impl<'a> TcpSocket<'a> { local_rx_last_ack: None, local_rx_last_seq: None, local_rx_dup_acks: 0, + ack_delay: Some(ACK_DELAY_DEFAULT), + ack_delay_until: None, #[cfg(feature = "async")] rx_waker: WakerRegistration::new(), @@ -453,6 +462,13 @@ impl<'a> TcpSocket<'a> { self.timeout } + /// Return the ACK delay duration. + /// + /// See also the [set_ack_delay](#method.set_ack_delay) method. + pub fn ack_delay(&self) -> Option { + self.ack_delay + } + /// Return the current window field value, including scaling according to RFC 1323. /// /// Used in internal calculations as well as packet generation. @@ -478,6 +494,13 @@ impl<'a> TcpSocket<'a> { self.timeout = duration } + /// Set the ACK delay duration. + /// + /// By default, the ACK delay is set to 10ms. + pub fn set_ack_delay(&mut self, duration: Option) { + self.ack_delay = duration + } + /// Return the keep-alive interval. /// /// See also the [set_keep_alive](#method.set_keep_alive) method. @@ -578,6 +601,8 @@ impl<'a> TcpSocket<'a> { self.remote_win_shift = rx_cap_log2.saturating_sub(16) as u8; self.remote_mss = DEFAULT_MSS; self.remote_last_ts = None; + self.ack_delay = Some(ACK_DELAY_DEFAULT); + self.ack_delay_until = None; #[cfg(feature = "async")] { @@ -1541,6 +1566,30 @@ impl<'a> TcpSocket<'a> { self.assembler); } + // Handle delayed acks + if let Some(ack_delay) = self.ack_delay { + if self.ack_to_transmit() || self.window_to_update() { + self.ack_delay_until = match self.ack_delay_until { + None => { + net_trace!("{}:{}:{}: starting delayed ack timer", + self.meta.handle, self.local_endpoint, self.remote_endpoint + ); + + Some(timestamp + ack_delay) + } + // RFC1122 says "in a stream of full-sized segments there SHOULD be an ACK + // for at least every second segment". + // For now, we send an ACK every second received packet, full-sized or not. + Some(_) => { + net_trace!("{}:{}:{}: delayed ack timer already started, forcing expiry", + self.meta.handle, self.local_endpoint, self.remote_endpoint + ); + None + } + }; + } + } + // Per RFC 5681, we should send an immediate ACK when either: // 1) an out-of-order segment is received, or // 2) a segment arrives that fills in all or part of a gap in sequence space. @@ -1590,6 +1639,13 @@ impl<'a> TcpSocket<'a> { can_data || can_fin } + fn delayed_ack_expired(&self, timestamp: Instant) -> bool { + match self.ack_delay_until { + None => true, + Some(t) => t <= timestamp, + } + } + fn ack_to_transmit(&self) -> bool { if let Some(remote_last_ack) = self.remote_last_ack { remote_last_ack < self.remote_seq_no + self.rx_buffer.len() @@ -1644,11 +1700,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() { + } else if self.ack_to_transmit() && self.delayed_ack_expired(timestamp) { // 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() { + } else if self.window_to_update() && self.delayed_ack_expired(timestamp) { // 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); @@ -1812,6 +1868,15 @@ impl<'a> TcpSocket<'a> { // the keep-alive timer. self.timer.rewind_keep_alive(timestamp, self.keep_alive); + // Reset delayed-ack timer + if self.ack_delay_until.is_some() { + net_trace!("{}:{}:{}: stop delayed ack timer", + self.meta.handle, self.local_endpoint, self.remote_endpoint + ); + + self.ack_delay_until = None; + } + // Leave the rest of the state intact if sending a keep-alive packet, since those // carry a fake segment. if is_keep_alive { return Ok(()) } @@ -1851,10 +1916,17 @@ impl<'a> TcpSocket<'a> { } else if self.state == State::Closed { // Socket was aborted, we have an RST packet to transmit. PollAt::Now - } else if self.seq_to_transmit() || self.ack_to_transmit() || self.window_to_update() { + } else if self.seq_to_transmit() { // We have a data or flag packet to transmit. PollAt::Now } else { + let want_ack = self.ack_to_transmit() || self.window_to_update(); + let delayed_ack_poll_at = match (want_ack, self.ack_delay_until) { + (false, _) => PollAt::Ingress, + (true, None) => PollAt::Now, + (true, Some(t)) => PollAt::Time(t), + }; + let timeout_poll_at = match (self.remote_last_ts, self.timeout) { // If we're transmitting or retransmitting data, we need to poll at the moment // when the timeout would expire. @@ -1864,9 +1936,8 @@ impl<'a> TcpSocket<'a> { }; // We wait for the earliest of our timers to fire. - *[self.timer.poll_at(), timeout_poll_at] + *[self.timer.poll_at(), timeout_poll_at, delayed_ack_poll_at] .iter() - .filter(|x| !x.is_ingress()) .min().unwrap_or(&PollAt::Ingress) } } @@ -2076,7 +2147,9 @@ mod test { let rx_buffer = SocketBuffer::new(vec![0; rx_len]); let tx_buffer = SocketBuffer::new(vec![0; tx_len]); - TcpSocket::new(rx_buffer, tx_buffer) + let mut socket = TcpSocket::new(rx_buffer, tx_buffer); + socket.set_ack_delay(None); + socket } fn socket_syn_received_with_buffer_sizes( @@ -5084,6 +5157,119 @@ mod test { assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); } + // =========================================================================================// + // Tests for delayed ACK + // =========================================================================================// + + #[test] + fn test_delayed_ack() { + let mut s = socket_established(); + s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + }); + + // No ACK is immediately sent. + recv!(s, Err(Error::Exhausted)); + + // After 10ms, it is sent. + recv!(s, time 11, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 61, + ..RECV_TEMPL + })); + } + + #[test] + fn test_delayed_ack_win() { + let mut s = socket_established(); + s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + }); + + // Reading the data off the buffer should cause a window update. + s.recv(|data| { + assert_eq!(data, b"abc"); + (3, ()) + }).unwrap(); + + // However, no ACK or window update is immediately sent. + recv!(s, Err(Error::Exhausted)); + + // After 10ms, it is sent. + recv!(s, time 11, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + ..RECV_TEMPL + })); + } + + #[test] + fn test_delayed_ack_reply() { + let mut s = socket_established(); + s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + }); + + s.recv(|data| { + assert_eq!(data, b"abc"); + (3, ()) + }).unwrap(); + + s.send_slice(&b"xyz"[..]).unwrap(); + + // Writing data to the socket causes ACK to not be delayed, + // because it is immediately sent with the data. + recv!(s, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + payload: &b"xyz"[..], + ..RECV_TEMPL + })); + } + + #[test] + fn test_delayed_ack_every_second_packet() { + let mut s = socket_established(); + s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + }); + + // No ACK is immediately sent. + recv!(s, Err(Error::Exhausted)); + + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"def"[..], + ..SEND_TEMPL + }); + + // Every 2nd packet, ACK is sent without delay. + recv!(s, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + })); + } + // =========================================================================================// // Tests for packet filtering. // =========================================================================================// From 923d6375f84e36e821697b4ddfab97c19285fd75 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 8 Jan 2021 19:25:21 +0100 Subject: [PATCH 063/566] Remove unnecessary semicolon --- examples/httpclient.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/httpclient.rs b/examples/httpclient.rs index d2d8f402b..2ca884440 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -55,7 +55,7 @@ fn main() { let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); - enum State { Connect, Request, Response }; + enum State { Connect, Request, Response } let mut state = State::Connect; loop { From 11255a89a1aa38cdbf0206e93149e353697d79fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 9 Jan 2021 01:52:08 +0100 Subject: [PATCH 064/566] Simplify Socket lifetimes --- src/dhcp/clientv4.rs | 3 +-- src/iface/ethernet.rs | 2 +- src/socket/icmp.rs | 24 +++++++++---------- src/socket/mod.rs | 28 +++++++++++----------- src/socket/raw.rs | 32 ++++++++++++------------- src/socket/ref_.rs | 6 ++--- src/socket/set.rs | 46 ++++++++++++++++++------------------ src/socket/tcp.rs | 4 ++-- src/socket/udp.rs | 26 ++++++++++---------- src/storage/packet_buffer.rs | 12 +++++----- 10 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 0828d1f88..7e344afab 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -94,8 +94,7 @@ impl Client { /// Instant::now() /// ); /// ``` - pub fn new<'a, 'b, 'c>(sockets: &mut SocketSet<'a, 'b, 'c>, rx_buffer: RawSocketBuffer<'b, 'c>, tx_buffer: RawSocketBuffer<'b, 'c>, now: Instant) -> Self - where 'b: 'c, + pub fn new<'a, 'b>(sockets: &mut SocketSet<'a, 'b>, rx_buffer: RawSocketBuffer<'b>, tx_buffer: RawSocketBuffer<'b>, now: Instant) -> Self { let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); let raw_handle = sockets.add(raw_socket); diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 7e125487a..f6a126763 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1727,7 +1727,7 @@ mod test { use super::{EthernetPacket, IpPacket}; fn create_loopback<'a, 'b, 'c>() -> (EthernetInterface<'static, 'b, 'c, Loopback>, - SocketSet<'static, 'a, 'b>) { + SocketSet<'static, 'a>) { // Create a basic device let device = Loopback::new(); let ip_addrs = [ diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 2afd8b87a..e6f655549 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -46,7 +46,7 @@ impl Default for Endpoint { pub type IcmpPacketMetadata = PacketMetadata; /// An ICMP packet ring buffer. -pub type IcmpSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, IpAddress>; +pub type IcmpSocketBuffer<'a> = PacketBuffer<'a, IpAddress>; /// A ICMP socket /// @@ -58,10 +58,10 @@ pub type IcmpSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, IpAddress>; /// [IcmpEndpoint]: enum.IcmpEndpoint.html /// [bind]: #method.bind #[derive(Debug)] -pub struct IcmpSocket<'a, 'b: 'a> { +pub struct IcmpSocket<'a> { pub(crate) meta: SocketMeta, - rx_buffer: IcmpSocketBuffer<'a, 'b>, - tx_buffer: IcmpSocketBuffer<'a, 'b>, + rx_buffer: IcmpSocketBuffer<'a>, + tx_buffer: IcmpSocketBuffer<'a>, /// The endpoint this socket is communicating with endpoint: Endpoint, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -72,10 +72,10 @@ pub struct IcmpSocket<'a, 'b: 'a> { tx_waker: WakerRegistration, } -impl<'a, 'b> IcmpSocket<'a, 'b> { +impl<'a> IcmpSocket<'a> { /// Create an ICMP socket with the given buffers. - pub fn new(rx_buffer: IcmpSocketBuffer<'a, 'b>, - tx_buffer: IcmpSocketBuffer<'a, 'b>) -> IcmpSocket<'a, 'b> { + pub fn new(rx_buffer: IcmpSocketBuffer<'a>, + tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> { IcmpSocket { meta: SocketMeta::default(), rx_buffer: rx_buffer, @@ -455,8 +455,8 @@ impl<'a, 'b> IcmpSocket<'a, 'b> { } } -impl<'a, 'b> Into> for IcmpSocket<'a, 'b> { - fn into(self) -> Socket<'a, 'b> { +impl<'a> Into> for IcmpSocket<'a> { + fn into(self) -> Socket<'a> { Socket::Icmp(self) } } @@ -467,12 +467,12 @@ mod tests_common { pub use crate::wire::IpAddress; pub use super::*; - pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static, 'static> { + pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static> { IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 66 * packets]) } - pub fn socket(rx_buffer: IcmpSocketBuffer<'static, 'static>, - tx_buffer: IcmpSocketBuffer<'static, 'static>) -> IcmpSocket<'static, 'static> { + pub fn socket(rx_buffer: IcmpSocketBuffer<'static>, + tx_buffer: IcmpSocketBuffer<'static>) -> IcmpSocket<'static> { IcmpSocket::new(rx_buffer, tx_buffer) } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 964e9b987..c81b2f0f2 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -82,17 +82,17 @@ pub(crate) enum PollAt { /// [AnySocket]: trait.AnySocket.html /// [SocketSet::get]: struct.SocketSet.html#method.get #[derive(Debug)] -pub enum Socket<'a, 'b: 'a> { +pub enum Socket<'a> { #[cfg(feature = "socket-raw")] - Raw(RawSocket<'a, 'b>), + Raw(RawSocket<'a>), #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - Icmp(IcmpSocket<'a, 'b>), + Icmp(IcmpSocket<'a>), #[cfg(feature = "socket-udp")] - Udp(UdpSocket<'a, 'b>), + Udp(UdpSocket<'a>), #[cfg(feature = "socket-tcp")] Tcp(TcpSocket<'a>), #[doc(hidden)] - __Nonexhaustive(PhantomData<(&'a (), &'b ())>) + __Nonexhaustive(PhantomData<&'a ()>) } macro_rules! dispatch_socket { @@ -117,7 +117,7 @@ macro_rules! dispatch_socket { }; } -impl<'a, 'b> Socket<'a, 'b> { +impl<'a> Socket<'a> { /// Return the socket handle. #[inline] pub fn handle(&self) -> SocketHandle { @@ -137,22 +137,22 @@ impl<'a, 'b> Socket<'a, 'b> { } } -impl<'a, 'b> SocketSession for Socket<'a, 'b> { +impl<'a> SocketSession for Socket<'a> { fn finish(&mut self) { dispatch_socket!(mut self, |socket| socket.finish()) } } /// A conversion trait for network sockets. -pub trait AnySocket<'a, 'b>: SocketSession + Sized { - fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a, 'b>>) -> +pub trait AnySocket<'a>: SocketSession + Sized { + fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option>; } macro_rules! from_socket { ($socket:ty, $variant:ident) => { - impl<'a, 'b> AnySocket<'a, 'b> for $socket { - fn downcast<'c>(ref_: SocketRef<'c, Socket<'a, 'b>>) -> + impl<'a> AnySocket<'a> for $socket { + fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option> { match SocketRef::into_inner(ref_) { &mut Socket::$variant(ref mut socket) => Some(SocketRef::new(socket)), @@ -164,10 +164,10 @@ macro_rules! from_socket { } #[cfg(feature = "socket-raw")] -from_socket!(RawSocket<'a, 'b>, Raw); +from_socket!(RawSocket<'a>, Raw); #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -from_socket!(IcmpSocket<'a, 'b>, Icmp); +from_socket!(IcmpSocket<'a>, Icmp); #[cfg(feature = "socket-udp")] -from_socket!(UdpSocket<'a, 'b>, Udp); +from_socket!(UdpSocket<'a>, Udp); #[cfg(feature = "socket-tcp")] from_socket!(TcpSocket<'a>, Tcp); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 61b771d25..2b99792ab 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -19,31 +19,31 @@ use crate::wire::{Ipv6Repr, Ipv6Packet}; pub type RawPacketMetadata = PacketMetadata<()>; /// A UDP packet ring buffer. -pub type RawSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, ()>; +pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>; /// A raw IP socket. /// /// A raw socket is bound to a specific IP protocol, and owns /// transmit and receive packet buffers. #[derive(Debug)] -pub struct RawSocket<'a, 'b: 'a> { +pub struct RawSocket<'a> { pub(crate) meta: SocketMeta, ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a, 'b>, - tx_buffer: RawSocketBuffer<'a, 'b>, + rx_buffer: RawSocketBuffer<'a>, + tx_buffer: RawSocketBuffer<'a>, #[cfg(feature = "async")] rx_waker: WakerRegistration, #[cfg(feature = "async")] tx_waker: WakerRegistration, } -impl<'a, 'b> RawSocket<'a, 'b> { +impl<'a> RawSocket<'a> { /// Create a raw IP socket bound to the given IP version and datagram protocol, /// with the given buffers. pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a, 'b>, - tx_buffer: RawSocketBuffer<'a, 'b>) -> RawSocket<'a, 'b> { + rx_buffer: RawSocketBuffer<'a>, + tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> { RawSocket { meta: SocketMeta::default(), ip_version, @@ -297,8 +297,8 @@ impl<'a, 'b> RawSocket<'a, 'b> { } } -impl<'a, 'b> Into> for RawSocket<'a, 'b> { - fn into(self) -> Socket<'a, 'b> { +impl<'a> Into> for RawSocket<'a> { + fn into(self) -> Socket<'a> { Socket::Raw(self) } } @@ -312,7 +312,7 @@ mod test { use crate::wire::{Ipv6Address, Ipv6Repr}; use super::*; - fn buffer(packets: usize) -> RawSocketBuffer<'static, 'static> { + fn buffer(packets: usize) -> RawSocketBuffer<'static> { RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]) } @@ -320,9 +320,9 @@ mod test { mod ipv4_locals { use super::*; - pub fn socket(rx_buffer: RawSocketBuffer<'static, 'static>, - tx_buffer: RawSocketBuffer<'static, 'static>) - -> RawSocket<'static, 'static> { + pub fn socket(rx_buffer: RawSocketBuffer<'static>, + tx_buffer: RawSocketBuffer<'static>) + -> RawSocket<'static> { RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO), rx_buffer, tx_buffer) } @@ -352,9 +352,9 @@ mod test { mod ipv6_locals { use super::*; - pub fn socket(rx_buffer: RawSocketBuffer<'static, 'static>, - tx_buffer: RawSocketBuffer<'static, 'static>) - -> RawSocket<'static, 'static> { + pub fn socket(rx_buffer: RawSocketBuffer<'static>, + tx_buffer: RawSocketBuffer<'static>) + -> RawSocket<'static> { RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO), rx_buffer, tx_buffer) } diff --git a/src/socket/ref_.rs b/src/socket/ref_.rs index 7d57ccf0d..9e030ff95 100644 --- a/src/socket/ref_.rs +++ b/src/socket/ref_.rs @@ -20,11 +20,11 @@ pub trait Session { } #[cfg(feature = "socket-raw")] -impl<'a, 'b> Session for RawSocket<'a, 'b> {} +impl<'a> Session for RawSocket<'a> {} #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -impl<'a, 'b> Session for IcmpSocket<'a, 'b> {} +impl<'a> Session for IcmpSocket<'a> {} #[cfg(feature = "socket-udp")] -impl<'a, 'b> Session for UdpSocket<'a, 'b> {} +impl<'a> Session for UdpSocket<'a> {} #[cfg(feature = "socket-tcp")] impl<'a> Session for TcpSocket<'a> {} diff --git a/src/socket/set.rs b/src/socket/set.rs index 8c6a56ad9..8f0bc0137 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -10,8 +10,8 @@ use crate::socket::TcpState; /// The only reason this struct is public is to allow the socket set storage /// to be allocated externally. #[derive(Debug)] -pub struct Item<'a, 'b: 'a> { - socket: Socket<'a, 'b>, +pub struct Item<'a> { + socket: Socket<'a>, refs: usize } @@ -27,16 +27,16 @@ impl fmt::Display for Handle { /// An extensible set of sockets. /// -/// The lifetimes `'b` and `'c` are used when storing a `Socket<'b, 'c>`. +/// The lifetime `'b` is used when storing a `Socket<'b>`. #[derive(Debug)] -pub struct Set<'a, 'b: 'a, 'c: 'a + 'b> { - sockets: ManagedSlice<'a, Option>> +pub struct Set<'a, 'b: 'a> { + sockets: ManagedSlice<'a, Option>> } -impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { +impl<'a, 'b: 'a> Set<'a, 'b> { /// Create a socket set using the provided storage. - pub fn new(sockets: SocketsT) -> Set<'a, 'b, 'c> - where SocketsT: Into>>> { + pub fn new(sockets: SocketsT) -> Set<'a, 'b> + where SocketsT: Into>>> { let sockets = sockets.into(); Set { sockets } } @@ -46,10 +46,10 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. pub fn add(&mut self, socket: T) -> Handle - where T: Into> + where T: Into> { - fn put<'b, 'c>(index: usize, slot: &mut Option>, - mut socket: Socket<'b, 'c>) -> Handle { + fn put<'b>(index: usize, slot: &mut Option>, + mut socket: Socket<'b>) -> Handle { net_trace!("[{}]: adding", index); let handle = Handle(index); socket.meta_mut().handle = handle; @@ -83,7 +83,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get>(&mut self, handle: Handle) -> SocketRef { + pub fn get>(&mut self, handle: Handle) -> SocketRef { match self.sockets[handle.0].as_mut() { Some(item) => { T::downcast(SocketRef::new(&mut item.socket)) @@ -97,7 +97,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { /// /// # Panics /// This function may panic if the handle does not belong to this socket set. - pub fn remove(&mut self, handle: Handle) -> Socket<'b, 'c> { + pub fn remove(&mut self, handle: Handle) -> Socket<'b> { net_trace!("[{}]: removing", handle.0); match self.sockets[handle.0].take() { Some(item) => item.socket, @@ -166,12 +166,12 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { } /// Iterate every socket in this set. - pub fn iter<'d>(&'d self) -> Iter<'d, 'b, 'c> { + pub fn iter<'d>(&'d self) -> Iter<'d, 'b> { Iter { lower: self.sockets.iter() } } /// Iterate every socket in this set, as SocketRef. - pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'b, 'c> { + pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'b> { IterMut { lower: self.sockets.iter_mut() } } } @@ -180,12 +180,12 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> { /// /// This struct is created by the [iter](struct.SocketSet.html#method.iter) /// on [socket sets](struct.SocketSet.html). -pub struct Iter<'a, 'b: 'a, 'c: 'a + 'b> { - lower: slice::Iter<'a, Option>> +pub struct Iter<'a, 'b: 'a> { + lower: slice::Iter<'a, Option>> } -impl<'a, 'b: 'a, 'c: 'a + 'b> Iterator for Iter<'a, 'b, 'c> { - type Item = &'a Socket<'b, 'c>; +impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> { + type Item = &'a Socket<'b>; fn next(&mut self) -> Option { while let Some(item_opt) = self.lower.next() { @@ -201,12 +201,12 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Iterator for Iter<'a, 'b, 'c> { /// /// This struct is created by the [iter_mut](struct.SocketSet.html#method.iter_mut) /// on [socket sets](struct.SocketSet.html). -pub struct IterMut<'a, 'b: 'a, 'c: 'a + 'b> { - lower: slice::IterMut<'a, Option>>, +pub struct IterMut<'a, 'b: 'a> { + lower: slice::IterMut<'a, Option>>, } -impl<'a, 'b: 'a, 'c: 'a + 'b> Iterator for IterMut<'a, 'b, 'c> { - type Item = SocketRef<'a, Socket<'b, 'c>>; +impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> { + type Item = SocketRef<'a, Socket<'b>>; fn next(&mut self) -> Option { while let Some(item_opt) = self.lower.next() { diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 38b31733e..346cd4ad2 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1943,8 +1943,8 @@ impl<'a> TcpSocket<'a> { } } -impl<'a, 'b> Into> for TcpSocket<'a> { - fn into(self) -> Socket<'a, 'b> { +impl<'a> Into> for TcpSocket<'a> { + fn into(self) -> Socket<'a> { Socket::Tcp(self) } } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index caf8cb4cc..88f5be4ca 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -13,18 +13,18 @@ use crate::socket::WakerRegistration; pub type UdpPacketMetadata = PacketMetadata; /// A UDP packet ring buffer. -pub type UdpSocketBuffer<'a, 'b> = PacketBuffer<'a, 'b, IpEndpoint>; +pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>; /// A User Datagram Protocol socket. /// /// A UDP socket is bound to a specific endpoint, and owns transmit and receive /// packet buffers. #[derive(Debug)] -pub struct UdpSocket<'a, 'b: 'a> { +pub struct UdpSocket<'a> { pub(crate) meta: SocketMeta, endpoint: IpEndpoint, - rx_buffer: UdpSocketBuffer<'a, 'b>, - tx_buffer: UdpSocketBuffer<'a, 'b>, + rx_buffer: UdpSocketBuffer<'a>, + tx_buffer: UdpSocketBuffer<'a>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. hop_limit: Option, #[cfg(feature = "async")] @@ -33,10 +33,10 @@ pub struct UdpSocket<'a, 'b: 'a> { tx_waker: WakerRegistration, } -impl<'a, 'b> UdpSocket<'a, 'b> { +impl<'a> UdpSocket<'a> { /// Create an UDP socket with the given buffers. - pub fn new(rx_buffer: UdpSocketBuffer<'a, 'b>, - tx_buffer: UdpSocketBuffer<'a, 'b>) -> UdpSocket<'a, 'b> { + pub fn new(rx_buffer: UdpSocketBuffer<'a>, + tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { UdpSocket { meta: SocketMeta::default(), endpoint: IpEndpoint::default(), @@ -336,8 +336,8 @@ impl<'a, 'b> UdpSocket<'a, 'b> { } } -impl<'a, 'b> Into> for UdpSocket<'a, 'b> { - fn into(self) -> Socket<'a, 'b> { +impl<'a> Into> for UdpSocket<'a> { + fn into(self) -> Socket<'a> { Socket::Udp(self) } } @@ -352,13 +352,13 @@ mod test { use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; use super::*; - fn buffer(packets: usize) -> UdpSocketBuffer<'static, 'static> { + fn buffer(packets: usize) -> UdpSocketBuffer<'static> { UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; packets], vec![0; 16 * packets]) } - fn socket(rx_buffer: UdpSocketBuffer<'static, 'static>, - tx_buffer: UdpSocketBuffer<'static, 'static>) - -> UdpSocket<'static, 'static> { + fn socket(rx_buffer: UdpSocketBuffer<'static>, + tx_buffer: UdpSocketBuffer<'static>) + -> UdpSocket<'static> { UdpSocket::new(rx_buffer, tx_buffer) } diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index ab9159617..be9866bea 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -35,19 +35,19 @@ impl PacketMetadata { /// An UDP packet ring buffer. #[derive(Debug)] -pub struct PacketBuffer<'a, 'b, H: 'a> { +pub struct PacketBuffer<'a, H: 'a> { metadata_ring: RingBuffer<'a, PacketMetadata>, - payload_ring: RingBuffer<'b, u8>, + payload_ring: RingBuffer<'a, u8>, } -impl<'a, 'b, H> PacketBuffer<'a, 'b, H> { +impl<'a, H> PacketBuffer<'a, H> { /// Create a new packet buffer with the provided metadata and payload storage. /// /// Metadata storage limits the maximum _number_ of packets in the buffer and payload /// storage limits the maximum _total size_ of packets. - pub fn new(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, 'b, H> + pub fn new(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, H> where MS: Into>>, - PS: Into>, + PS: Into>, { PacketBuffer { metadata_ring: RingBuffer::new(metadata_storage), @@ -184,7 +184,7 @@ impl<'a, 'b, H> PacketBuffer<'a, 'b, H> { mod test { use super::*; - fn buffer() -> PacketBuffer<'static, 'static, ()> { + fn buffer() -> PacketBuffer<'static, ()> { PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; 16]) } From 633e7c2591e21534db651d1e94ca13c9a2ae6895 Mon Sep 17 00:00:00 2001 From: Niclas Hoyer Date: Sat, 9 Jan 2021 00:55:28 +0100 Subject: [PATCH 065/566] Use #[non_exhaustive] instead of manual variant --- .github/workflows/test.yml | 8 +++---- README.md | 2 +- src/iface/ethernet.rs | 15 ++++--------- src/lib.rs | 5 +---- src/socket/mod.rs | 11 ++++------ src/socket/raw.rs | 1 - src/socket/set.rs | 1 - src/wire/arp.rs | 6 +----- src/wire/icmpv4.rs | 7 +----- src/wire/icmpv6.rs | 6 +----- src/wire/ip.rs | 44 ++++---------------------------------- src/wire/ipv6option.rs | 10 +-------- src/wire/ipv6routing.rs | 10 +-------- 13 files changed, 23 insertions(+), 103 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 498e606cf..b4d11ebc5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,11 +11,11 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.36, and nightly. + # Test on stable, MSRV 1.40, and nightly. # Failure is permitted on nightly. rust: - stable - - 1.36.0 + - 1.40.0 - nightly features: @@ -57,11 +57,11 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.36, and nightly. + # Test on stable, MSRV 1.40, and nightly. # Failure is permitted on nightly. rust: - stable - - 1.36.0 + - 1.40.0 - nightly features: diff --git a/README.md b/README.md index a240a1594..edcc96341 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.36 and later. +and compiles on stable Rust 1.40 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index f6a126763..0a0d13ebf 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -678,7 +678,6 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> Socket::Tcp(ref mut socket) => socket.dispatch(timestamp, &caps, |response| respond!(IpPacket::Tcp(response))), - Socket::__Nonexhaustive(_) => unreachable!() }; match (device_result, socket_result) { @@ -800,7 +799,8 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { .filter_map( |addr| match *addr { IpCidr::Ipv4(cidr) => Some(cidr.address()), - _ => None, + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None }) .next() } @@ -886,8 +886,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { Ok(None) } } - - _ => Err(Error::Unrecognized) } } @@ -1253,7 +1251,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } } } - _ => return Err(Error::Unrecognized), } } self.process_nxt_hdr(sockets, timestamp, ipv6_repr, hbh_repr.next_header, @@ -1421,8 +1418,7 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { }; Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) }, - IpRepr::Unspecified { .. } | - IpRepr::__Nonexhaustive => Err(Error::Unaddressable), + IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } } @@ -1467,7 +1463,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { let dst_hardware_addr = match arp_repr { ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr, - _ => unreachable!() }; self.dispatch_ethernet(tx_token, timestamp, arp_repr.buffer_len(), |mut frame| { @@ -1555,8 +1550,6 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { b[12], b[13], b[14], b[15], ])), - IpAddress::__Nonexhaustive => - unreachable!() }; if let Some(hardware_addr) = hardware_addr { return Ok((hardware_addr, tx_token)) @@ -1770,7 +1763,7 @@ mod test { impl phy::TxToken for MockTxToken { fn consume(self, _: Instant, _: usize, _: F) -> Result where F: FnOnce(&mut [u8]) -> Result { - Err(Error::__Nonexhaustive) + Err(Error::Unaddressable) } } diff --git a/src/lib.rs b/src/lib.rs index 0eb50e43b..7d18e89da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,6 +116,7 @@ pub mod dhcp; /// The error type for the networking stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] pub enum Error { /// An operation cannot proceed because a buffer is empty or full. Exhausted, @@ -148,9 +149,6 @@ pub enum Error { /// An incoming packet was recognized but contradicted internal state. /// E.g. a TCP packet addressed to a socket that doesn't exist. Dropped, - - #[doc(hidden)] - __Nonexhaustive } /// The result type for the networking stack. @@ -169,7 +167,6 @@ impl fmt::Display for Error { Error::Fragmented => write!(f, "fragmented packet"), Error::Malformed => write!(f, "malformed packet"), Error::Dropped => write!(f, "dropped by socket"), - Error::__Nonexhaustive => unreachable!() } } } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index c81b2f0f2..d7a971bd2 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -11,7 +11,6 @@ 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 core::marker::PhantomData; use crate::time::Instant; mod meta; @@ -91,8 +90,6 @@ pub enum Socket<'a> { Udp(UdpSocket<'a>), #[cfg(feature = "socket-tcp")] Tcp(TcpSocket<'a>), - #[doc(hidden)] - __Nonexhaustive(PhantomData<&'a ()>) } macro_rules! dispatch_socket { @@ -112,7 +109,6 @@ macro_rules! dispatch_socket { &$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code, #[cfg(feature = "socket-tcp")] &$( $mut_ )* Socket::Tcp(ref $( $mut_ )* $socket) => $code, - &$( $mut_ )* Socket::__Nonexhaustive(_) => unreachable!() } }; } @@ -154,9 +150,10 @@ macro_rules! from_socket { impl<'a> AnySocket<'a> for $socket { fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option> { - match SocketRef::into_inner(ref_) { - &mut Socket::$variant(ref mut socket) => Some(SocketRef::new(socket)), - _ => None, + if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) { + Some(SocketRef::new(socket)) + } else { + None } } } diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 2b99792ab..e4e60edb6 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -257,7 +257,6 @@ impl<'a> RawSocket<'a> { Ok((IpRepr::Ipv6(ipv6_repr), packet.payload())) } IpVersion::Unspecified => unreachable!(), - IpVersion::__Nonexhaustive => unreachable!() } } diff --git a/src/socket/set.rs b/src/socket/set.rs index 8f0bc0137..883bed86d 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -155,7 +155,6 @@ impl<'a, 'b: 'a> Set<'a, 'b> { } else { socket.close() }, - Socket::__Nonexhaustive(_) => unreachable!() } } if may_remove { diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 150adbf7c..e213b29db 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -252,6 +252,7 @@ use crate::wire::{EthernetAddress, Ipv4Address}; /// A high-level representation of an Address Resolution Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum Repr { /// An Ethernet and IPv4 Address Resolution Protocol packet. EthernetIpv4 { @@ -261,8 +262,6 @@ pub enum Repr { target_hardware_addr: EthernetAddress, target_protocol_addr: Ipv4Address }, - #[doc(hidden)] - __Nonexhaustive } impl Repr { @@ -292,7 +291,6 @@ impl Repr { pub fn buffer_len(&self) -> usize { match *self { Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end, - Repr::__Nonexhaustive => unreachable!() } } @@ -314,7 +312,6 @@ impl Repr { packet.set_target_hardware_addr(target_hardware_addr.as_bytes()); packet.set_target_protocol_addr(target_protocol_addr.as_bytes()); }, - Repr::__Nonexhaustive => unreachable!() } } } @@ -351,7 +348,6 @@ impl fmt::Display for Repr { target_hardware_addr, target_protocol_addr, operation) }, - Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index fae318a52..5748c899c 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -366,6 +366,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Control Message Protocol version 4 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum Repr<'a> { EchoRequest { ident: u16, @@ -382,8 +383,6 @@ pub enum Repr<'a> { header: Ipv4Repr, data: &'a [u8] }, - #[doc(hidden)] - __Nonexhaustive } impl<'a> Repr<'a> { @@ -446,7 +445,6 @@ impl<'a> Repr<'a> { &Repr::DstUnreachable { header, data, .. } => { field::UNUSED.end + header.buffer_len() + data.len() } - &Repr::__Nonexhaustive => unreachable!() } } @@ -483,8 +481,6 @@ impl<'a> Repr<'a> { let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; payload.copy_from_slice(&data[..]) } - - Repr::__Nonexhaustive => unreachable!() } if checksum_caps.icmpv4.tx() { @@ -526,7 +522,6 @@ impl<'a> fmt::Display for Repr<'a> { Repr::DstUnreachable { reason, .. } => write!(f, "ICMPv4 destination unreachable ({})", reason), - Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index b3e49a1e9..bd232449c 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -503,6 +503,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Control Message Protocol version 6 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum Repr<'a> { DstUnreachable { reason: DstUnreachable, @@ -538,8 +539,6 @@ pub enum Repr<'a> { #[cfg(feature = "ethernet")] Ndisc(NdiscRepr<'a>), Mld(MldRepr<'a>), - #[doc(hidden)] - __Nonexhaustive } impl<'a> Repr<'a> { @@ -647,7 +646,6 @@ impl<'a> Repr<'a> { &Repr::Mld(mld) => { mld.buffer_len() }, - &Repr::__Nonexhaustive => unreachable!() } } @@ -720,8 +718,6 @@ impl<'a> Repr<'a> { Repr::Mld(mld) => { mld.emit(packet) }, - - Repr::__Nonexhaustive => unreachable!(), } if checksum_caps.icmpv6.tx() { diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 1b6acae56..f6f67776e 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -10,14 +10,13 @@ use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[non_exhaustive] pub enum Version { Unspecified, #[cfg(feature = "proto-ipv4")] Ipv4, #[cfg(feature = "proto-ipv6")] Ipv6, - #[doc(hidden)] - __Nonexhaustive, } impl Version { @@ -44,7 +43,6 @@ impl fmt::Display for Version { Version::Ipv4 => write!(f, "IPv4"), #[cfg(feature = "proto-ipv6")] Version::Ipv6 => write!(f, "IPv6"), - Version::__Nonexhaustive => unreachable!() } } } @@ -85,6 +83,7 @@ impl fmt::Display for Protocol { /// An internetworking address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[non_exhaustive] pub enum Address { /// An unspecified address. /// May be used as a placeholder for storage where the address is not assigned yet. @@ -95,8 +94,6 @@ pub enum Address { /// An IPv6 address. #[cfg(feature = "proto-ipv6")] Ipv6(Ipv6Address), - #[doc(hidden)] - __Nonexhaustive } impl Address { @@ -122,7 +119,6 @@ impl Address { Address::Ipv4(ref addr) => addr.as_bytes(), #[cfg(feature = "proto-ipv6")] Address::Ipv6(ref addr) => addr.as_bytes(), - Address::__Nonexhaustive => unreachable!() } } @@ -134,7 +130,6 @@ impl Address { Address::Ipv4(addr) => addr.is_unicast(), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => addr.is_unicast(), - Address::__Nonexhaustive => unreachable!() } } @@ -146,7 +141,6 @@ impl Address { Address::Ipv4(addr) => addr.is_multicast(), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => addr.is_multicast(), - Address::__Nonexhaustive => unreachable!() } } @@ -158,7 +152,6 @@ impl Address { Address::Ipv4(addr) => addr.is_broadcast(), #[cfg(feature = "proto-ipv6")] Address::Ipv6(_) => false, - Address::__Nonexhaustive => unreachable!() } } @@ -170,7 +163,6 @@ impl Address { Address::Ipv4(addr) => addr.is_unspecified(), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => addr.is_unspecified(), - Address::__Nonexhaustive => unreachable!() } } @@ -182,7 +174,6 @@ impl Address { Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), #[cfg(feature = "proto-ipv6")] Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), - Address::__Nonexhaustive => unreachable!() } } @@ -265,7 +256,6 @@ impl fmt::Display for Address { Address::Ipv4(addr) => write!(f, "{}", addr), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => write!(f, "{}", addr), - Address::__Nonexhaustive => unreachable!() } } } @@ -273,13 +263,12 @@ impl fmt::Display for Address { /// A specification of a CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[non_exhaustive] pub enum Cidr { #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Cidr), #[cfg(feature = "proto-ipv6")] Ipv6(Ipv6Cidr), - #[doc(hidden)] - __Nonexhaustive, } impl Cidr { @@ -296,8 +285,6 @@ impl Cidr { Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)), Address::Unspecified => panic!("a CIDR block cannot be based on an unspecified address"), - Address::__Nonexhaustive => - unreachable!() } } @@ -308,7 +295,6 @@ impl Cidr { Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), #[cfg(feature = "proto-ipv6")] Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()), - Cidr::__Nonexhaustive => unreachable!() } } @@ -319,7 +305,6 @@ impl Cidr { Cidr::Ipv4(cidr) => cidr.prefix_len(), #[cfg(feature = "proto-ipv6")] Cidr::Ipv6(cidr) => cidr.prefix_len(), - Cidr::__Nonexhaustive => unreachable!() } } @@ -340,9 +325,6 @@ impl Cidr { // a fully unspecified address covers both IPv4 and IPv6, // and no CIDR block can do that. false, - (&Cidr::__Nonexhaustive, _) | - (_, &Address::__Nonexhaustive) => - unreachable!() } } @@ -359,9 +341,6 @@ impl Cidr { #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))] (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false, - (&Cidr::__Nonexhaustive, _) | - (_, &Cidr::__Nonexhaustive) => - unreachable!() } } } @@ -387,7 +366,6 @@ impl fmt::Display for Cidr { Cidr::Ipv4(cidr) => write!(f, "{}", cidr), #[cfg(feature = "proto-ipv6")] Cidr::Ipv6(cidr) => write!(f, "{}", cidr), - Cidr::__Nonexhaustive => unreachable!() } } } @@ -470,6 +448,7 @@ impl> From<(T, u16)> for Endpoint { /// high-level representation for some IP protocol version, or an unspecified representation, /// which permits the `IpAddress::Unspecified` addresses. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum Repr { Unspecified { src_addr: Address, @@ -482,8 +461,6 @@ pub enum Repr { Ipv4(Ipv4Repr), #[cfg(feature = "proto-ipv6")] Ipv6(Ipv6Repr), - #[doc(hidden)] - __Nonexhaustive } #[cfg(feature = "proto-ipv4")] @@ -509,7 +486,6 @@ impl Repr { Repr::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(_) => Version::Ipv6, - Repr::__Nonexhaustive => unreachable!() } } @@ -521,7 +497,6 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr), - Repr::__Nonexhaustive => unreachable!() } } @@ -533,7 +508,6 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr), - Repr::__Nonexhaustive => unreachable!() } } @@ -545,7 +519,6 @@ impl Repr { Repr::Ipv4(repr) => repr.protocol, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.next_header, - Repr::__Nonexhaustive => unreachable!() } } @@ -557,7 +530,6 @@ impl Repr { Repr::Ipv4(repr) => repr.payload_len, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.payload_len, - Repr::__Nonexhaustive => unreachable!() } } @@ -572,7 +544,6 @@ impl Repr { #[cfg(feature = "proto-ipv6")] Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) => *payload_len = length, - Repr::__Nonexhaustive => unreachable!() } } @@ -584,7 +555,6 @@ impl Repr { Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit, - Repr::__Nonexhaustive => unreachable!() } } @@ -711,8 +681,6 @@ impl Repr { &Repr::Unspecified { .. } => panic!("source and destination IP address families do not match"), - - &Repr::__Nonexhaustive => unreachable!() } } @@ -730,8 +698,6 @@ impl Repr { #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.buffer_len(), - Repr::__Nonexhaustive => - unreachable!() } } @@ -749,8 +715,6 @@ impl Repr { #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), - Repr::__Nonexhaustive => - unreachable!() } } diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 18e5dcea4..726428464 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -214,6 +214,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> { /// A high-level representation of an IPv6 Extension Header Option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum Repr<'a> { Pad1, PadN(u8), @@ -222,9 +223,6 @@ pub enum Repr<'a> { length: u8, data: &'a [u8] }, - - #[doc(hidden)] - __Nonexhaustive } impl<'a> Repr<'a> { @@ -253,8 +251,6 @@ impl<'a> Repr<'a> { field::DATA(length).end, Repr::Unknown{ length, .. } => field::DATA(length).end, - - Repr::__Nonexhaustive => unreachable!() } } @@ -276,8 +272,6 @@ impl<'a> Repr<'a> { opt.set_data_len(length); opt.data_mut().copy_from_slice(&data[..length as usize]); } - - Repr::__Nonexhaustive => unreachable!() } } } @@ -352,8 +346,6 @@ impl<'a> fmt::Display for Repr<'a> { write!(f, "{} length={} ", Type::PadN, len), Repr::Unknown{ type_, length, .. } => write!(f, "{} length={} ", type_, length), - - Repr::__Nonexhaustive => unreachable!() } } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 93a2775e4..f4078996b 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -389,6 +389,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { /// A high-level representation of an IPv6 Routing Header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum Repr<'a> { Type2 { /// The type of header immediately following the Routing header. @@ -417,9 +418,6 @@ pub enum Repr<'a> { /// Vector of addresses, numbered 1 to `n`. addresses: &'a[u8], }, - - #[doc(hidden)] - __Nonexhaustive } @@ -458,8 +456,6 @@ impl<'a> Repr<'a> { &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => { field::DATA(length).end } - - &Repr::__Nonexhaustive => unreachable!() } } @@ -485,8 +481,6 @@ impl<'a> Repr<'a> { header.clear_reserved(); header.set_addresses(addresses); } - - Repr::__Nonexhaustive => unreachable!(), } } } @@ -502,8 +496,6 @@ impl<'a> fmt::Display for Repr<'a> { write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) } - - Repr::__Nonexhaustive => unreachable!(), } } } From 61faa269d6fe7d55368acc8087cd4f67bfb47981 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 10 Jan 2021 13:50:08 -0800 Subject: [PATCH 066/566] Use #[non_exhaustive] on structs as well This builds on 633e7c25, adding the #[non_exhaustive] attribute to applicable struct definitions. --- src/lib.rs | 1 - src/phy/mod.rs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7d18e89da..a6332eb9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,6 @@ compile_error!("at least one socket needs to be enabled"); */ // FIXME(dlrobertson): clippy fails with this lint #![allow(clippy::if_same_then_else)] -#![allow(clippy::manual_non_exhaustive)] #![allow(clippy::match_like_matches_macro)] #![allow(clippy::redundant_field_names)] #![allow(clippy::identity_op)] diff --git a/src/phy/mod.rs b/src/phy/mod.rs index adf05974b..e21963b7f 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -158,6 +158,7 @@ impl Checksum { /// A description of checksum behavior for every supported protocol. #[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct ChecksumCapabilities { pub ipv4: Checksum, pub udp: Checksum, @@ -166,7 +167,6 @@ pub struct ChecksumCapabilities { pub icmpv4: Checksum, #[cfg(feature = "proto-ipv6")] pub icmpv6: Checksum, - dummy: (), } impl ChecksumCapabilities { @@ -181,7 +181,6 @@ impl ChecksumCapabilities { icmpv4: Checksum::None, #[cfg(feature = "proto-ipv6")] icmpv6: Checksum::None, - ..Self::default() } } } @@ -191,6 +190,7 @@ impl ChecksumCapabilities { /// Higher-level protocols may achieve higher throughput or lower latency if they consider /// the bandwidth or packet size limitations. #[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct DeviceCapabilities { /// Maximum transmission unit. /// @@ -214,10 +214,6 @@ pub struct DeviceCapabilities { /// If the network device is capable of verifying or computing checksums for some protocols, /// it can request that the stack not do so in software to improve performance. pub checksum: ChecksumCapabilities, - - /// Only present to prevent people from trying to initialize every field of DeviceLimits, - /// which would not let us add new fields in the future. - pub(crate) dummy: () } /// An interface for sending and receiving raw network frames. From 6d569deb2ad7ddfb86b0ad09674e09e4ab76ac79 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Sun, 10 Jan 2021 14:16:08 -0800 Subject: [PATCH 067/566] Clean up clippy warnings about if-else blocks This de-duplicates and (hopefully) simplifies a few if-else blocks. The others were given an exception because I thought they were more readable as is. I've verified that these changes don't result in larger binaries. --- src/iface/ethernet.rs | 14 +++++--------- src/lib.rs | 2 -- src/socket/tcp.rs | 1 + src/wire/arp.rs | 1 + src/wire/ipv4.rs | 1 + 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 0a0d13ebf..1be01c750 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1026,12 +1026,9 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { !self.has_multicast_group(ipv4_repr.dst_addr) { // 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 { - return Ok(None); - } else if match self.routes.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), timestamp) { - Some(router_addr) => !self.has_ip_addr(router_addr), - None => true, - } { + if !self.any_ip || + self.routes.lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), timestamp) + .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) { return Ok(None); } } @@ -1188,10 +1185,9 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { let ip_addr = ip_repr.src_addr.into(); match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - if flags.contains(NdiscNeighborFlags::OVERRIDE) { + if flags.contains(NdiscNeighborFlags::OVERRIDE) || + !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { self.neighbor_cache.fill(ip_addr, lladdr, timestamp) - } else if !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, lladdr, timestamp) } }, _ => (), diff --git a/src/lib.rs b/src/lib.rs index a6332eb9d..7198b57d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,8 +87,6 @@ feature = "socket-tcp")))] compile_error!("at least one socket needs to be enabled"); */ -// FIXME(dlrobertson): clippy fails with this lint -#![allow(clippy::if_same_then_else)] #![allow(clippy::match_like_matches_macro)] #![allow(clippy::redundant_field_names)] #![allow(clippy::identity_op)] diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 346cd4ad2..2edccb873 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1905,6 +1905,7 @@ impl<'a> TcpSocket<'a> { Ok(()) } + #[allow(clippy::if_same_then_else)] pub(crate) fn poll_at(&self) -> PollAt { // The logic here mirrors the beginning of dispatch() closely. if !self.remote_endpoint.is_specified() { diff --git a/src/wire/arp.rs b/src/wire/arp.rs index e213b29db..97fcf3677 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -86,6 +86,7 @@ impl> Packet { /// /// [set_hardware_len]: #method.set_hardware_len /// [set_protocol_len]: #method.set_protocol_len + #[allow(clippy::if_same_then_else)] pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::OPER.end { diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index aab7e610a..6d51d4118 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -271,6 +271,7 @@ impl> Packet { /// /// [set_header_len]: #method.set_header_len /// [set_total_len]: #method.set_total_len + #[allow(clippy::if_same_then_else)] pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::DST_ADDR.end { From c492ed242a6f392437398674290e75569e49475b Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sat, 16 Jan 2021 01:22:16 +0000 Subject: [PATCH 068/566] subnet_broadcasts Adds `is_subnet_broadcast` to the ethernet interface which checks for subnet broadcasts, which are discussed on page 8 in https://tools.ietf.org/html/rfc917. The subnet broadcast addresses are derived from the interfaces ipv4 addresses. --- src/iface/ethernet.rs | 53 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 1be01c750..4aa2e7e84 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1023,7 +1023,9 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { if !self.has_ip_addr(ipv4_repr.dst_addr) && !ipv4_repr.dst_addr.is_broadcast() && - !self.has_multicast_group(ipv4_repr.dst_addr) { + !self.has_multicast_group(ipv4_repr.dst_addr) && + !self.is_subnet_broadcast(ipv4_repr.dst_addr) { + // 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 || @@ -1066,6 +1068,19 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } } + /// Checks if an incoming packet has a broadcast address for the interfaces + /// associated ipv4 addresses. + #[cfg(feature = "proto-ipv4")] + fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { + self.ip_addrs.iter() + .filter_map(|own_cidr| match own_cidr { + IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None + }) + .any(|broadcast_address| address == broadcast_address) + } + /// Host duties of the **IGMPv2** protocol. /// /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. @@ -1695,7 +1710,7 @@ mod test { use crate::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; #[cfg(feature = "proto-ipv4")] - use crate::wire::{Ipv4Address, Ipv4Repr}; + use crate::wire::{Ipv4Address, Ipv4Repr, Ipv4Cidr}; #[cfg(feature = "proto-igmp")] use crate::wire::Ipv4Packet; #[cfg(feature = "proto-ipv4")] @@ -1875,6 +1890,40 @@ mod test { Ok(Some(expected_repr))); } + #[test] + #[cfg(feature = "proto-ipv4")] + fn test_local_subnet_broadcasts() { + let (mut iface, _) = create_loopback(); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); + }); + }); + + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), true); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), false); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); + }); + }); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), true); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); + }); + }); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), true); + } + #[test] #[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] fn test_icmp_error_port_unreachable() { From 8aed2484de2485dd8137db57354601d9ccbdd96f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Jan 2021 22:20:40 +0100 Subject: [PATCH 069/566] Clarify docs of DeviceCapabilities MTU. Fixes #392 --- src/phy/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index e21963b7f..03bc779bc 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -197,7 +197,13 @@ pub struct DeviceCapabilities { /// The network device is unable to send or receive frames larger than the value returned /// by this function. /// - /// For Ethernet, MTU will fall between 576 (for IPv4) or 1280 (for IPv6) and 9216 octets. + /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but + /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14. + /// + /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet + /// devices. This is a common source of confusion. + /// + /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets. pub max_transmission_unit: usize, /// Maximum burst size, in terms of MTU. From 43cf6caddff2c0b09901b6823d3f459bcc8570ba Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Jan 2021 22:45:15 +0100 Subject: [PATCH 070/566] Simplify SocketSet lifetimes --- src/dhcp/clientv4.rs | 2 +- src/iface/ethernet.rs | 2 +- src/socket/set.rs | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 7e344afab..a2d33426e 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -94,7 +94,7 @@ impl Client { /// Instant::now() /// ); /// ``` - pub fn new<'a, 'b>(sockets: &mut SocketSet<'a, 'b>, rx_buffer: RawSocketBuffer<'b>, tx_buffer: RawSocketBuffer<'b>, now: Instant) -> Self + pub fn new<'a>(sockets: &mut SocketSet<'a>, rx_buffer: RawSocketBuffer<'a>, tx_buffer: RawSocketBuffer<'a>, now: Instant) -> Self { let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); let raw_handle = sockets.add(raw_socket); diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 4aa2e7e84..c5d76454e 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1731,7 +1731,7 @@ mod test { use super::{EthernetPacket, IpPacket}; fn create_loopback<'a, 'b, 'c>() -> (EthernetInterface<'static, 'b, 'c, Loopback>, - SocketSet<'static, 'a>) { + SocketSet<'a>) { // Create a basic device let device = Loopback::new(); let ip_addrs = [ diff --git a/src/socket/set.rs b/src/socket/set.rs index 883bed86d..826776348 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -27,16 +27,16 @@ impl fmt::Display for Handle { /// An extensible set of sockets. /// -/// The lifetime `'b` is used when storing a `Socket<'b>`. +/// The lifetime `'a` is used when storing a `Socket<'a>`. #[derive(Debug)] -pub struct Set<'a, 'b: 'a> { - sockets: ManagedSlice<'a, Option>> +pub struct Set<'a> { + sockets: ManagedSlice<'a, Option>> } -impl<'a, 'b: 'a> Set<'a, 'b> { +impl<'a> Set<'a> { /// Create a socket set using the provided storage. - pub fn new(sockets: SocketsT) -> Set<'a, 'b> - where SocketsT: Into>>> { + pub fn new(sockets: SocketsT) -> Set<'a> + where SocketsT: Into>>> { let sockets = sockets.into(); Set { sockets } } @@ -46,10 +46,10 @@ impl<'a, 'b: 'a> Set<'a, 'b> { /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. pub fn add(&mut self, socket: T) -> Handle - where T: Into> + where T: Into> { - fn put<'b>(index: usize, slot: &mut Option>, - mut socket: Socket<'b>) -> Handle { + fn put<'a>(index: usize, slot: &mut Option>, + mut socket: Socket<'a>) -> Handle { net_trace!("[{}]: adding", index); let handle = Handle(index); socket.meta_mut().handle = handle; @@ -83,7 +83,7 @@ impl<'a, 'b: 'a> Set<'a, 'b> { /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get>(&mut self, handle: Handle) -> SocketRef { + pub fn get>(&mut self, handle: Handle) -> SocketRef { match self.sockets[handle.0].as_mut() { Some(item) => { T::downcast(SocketRef::new(&mut item.socket)) @@ -97,7 +97,7 @@ impl<'a, 'b: 'a> Set<'a, 'b> { /// /// # Panics /// This function may panic if the handle does not belong to this socket set. - pub fn remove(&mut self, handle: Handle) -> Socket<'b> { + pub fn remove(&mut self, handle: Handle) -> Socket<'a> { net_trace!("[{}]: removing", handle.0); match self.sockets[handle.0].take() { Some(item) => item.socket, @@ -165,12 +165,12 @@ impl<'a, 'b: 'a> Set<'a, 'b> { } /// Iterate every socket in this set. - pub fn iter<'d>(&'d self) -> Iter<'d, 'b> { + pub fn iter<'d>(&'d self) -> Iter<'d, 'a> { Iter { lower: self.sockets.iter() } } /// Iterate every socket in this set, as SocketRef. - pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'b> { + pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> { IterMut { lower: self.sockets.iter_mut() } } } From 0c3867470378fe2a2261641f7270ce0c90f4d115 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Jan 2021 22:50:32 +0100 Subject: [PATCH 071/566] Simplify EthernetInterface lifetimes. --- src/iface/ethernet.rs | 65 ++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index c5d76454e..22b6e3e61 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -4,8 +4,6 @@ use core::cmp; use managed::{ManagedSlice, ManagedMap}; -#[cfg(not(feature = "proto-igmp"))] -use core::marker::PhantomData; use crate::{Error, Result}; use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken}; @@ -57,9 +55,9 @@ use crate::iface::Routes; /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be /// a `&mut [T]`, or `Vec` if a heap is available. -pub struct Interface<'b, 'c, 'e, DeviceT: for<'d> Device<'d>> { +pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, - inner: InterfaceInner<'b, 'c, 'e>, + inner: InterfaceInner<'a>, } /// The device independent part of an Ethernet network interface. @@ -69,17 +67,15 @@ pub struct Interface<'b, 'c, 'e, DeviceT: for<'d> Device<'d>> { /// the `device` mutably until they're used, which makes it impossible to call other /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. -struct InterfaceInner<'b, 'c, 'e> { - neighbor_cache: NeighborCache<'b>, +struct InterfaceInner<'a> { + neighbor_cache: NeighborCache<'a>, ethernet_addr: EthernetAddress, - ip_addrs: ManagedSlice<'c, IpCidr>, + ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, - routes: Routes<'e>, + routes: Routes<'a>, #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'e, Ipv4Address, ()>, - #[cfg(not(feature = "proto-igmp"))] - _ipv4_multicast_groups: PhantomData<&'e ()>, + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, /// When to report for (all or) the next multicast group membership via IGMP #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState, @@ -88,22 +84,20 @@ struct InterfaceInner<'b, 'c, 'e> { /// A builder structure used for creating a Ethernet network /// interface. -pub struct InterfaceBuilder <'b, 'c, 'e, DeviceT: for<'d> Device<'d>> { +pub struct InterfaceBuilder <'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, ethernet_addr: Option, - neighbor_cache: Option>, - ip_addrs: ManagedSlice<'c, IpCidr>, + neighbor_cache: Option>, + ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, - routes: Routes<'e>, + routes: Routes<'a>, /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'e, Ipv4Address, ()>, - #[cfg(not(feature = "proto-igmp"))] - _ipv4_multicast_groups: PhantomData<&'e ()>, + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, } -impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> +impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Create a builder used for creating a network interface using the /// given device and address. @@ -141,8 +135,6 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> routes: Routes::new(ManagedMap::Borrowed(&mut [])), #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), - #[cfg(not(feature = "proto-igmp"))] - _ipv4_multicast_groups: PhantomData, } } @@ -167,7 +159,7 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> /// /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self - where T: Into> + where T: Into> { let ip_addrs = ip_addrs.into(); InterfaceInner::check_ip_addrs(&ip_addrs); @@ -198,8 +190,8 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> /// [routes]. /// /// [routes]: struct.EthernetInterface.html#method.routes - pub fn routes(mut self, routes: T) -> InterfaceBuilder<'b, 'c, 'e, DeviceT> - where T: Into> + pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a, DeviceT> + where T: Into> { self.routes = routes.into(); self @@ -217,14 +209,14 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> /// [`join_multicast_group()`]: struct.EthernetInterface.html#method.join_multicast_group #[cfg(feature = "proto-igmp")] pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self - where T: Into> + where T: Into> { self.ipv4_multicast_groups = ipv4_multicast_groups.into(); self } /// Set the Neighbor Cache the interface will use. - pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'b>) -> Self { + pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { self.neighbor_cache = Some(neighbor_cache); self } @@ -240,7 +232,7 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> /// /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self) -> Interface<'b, 'c, 'e, DeviceT> { + pub fn finalize(self) -> Interface<'a, DeviceT> { match (self.ethernet_addr, self.neighbor_cache) { (Some(ethernet_addr), Some(neighbor_cache)) => { let device_capabilities = self.device.capabilities(); @@ -255,8 +247,6 @@ impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> routes: self.routes, #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: self.ipv4_multicast_groups, - #[cfg(not(feature = "proto-igmp"))] - _ipv4_multicast_groups: PhantomData, #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, } @@ -385,7 +375,7 @@ enum IgmpReportState { }, } -impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> +impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Get the Ethernet address of the interface. pub fn ethernet_addr(&self) -> EthernetAddress { @@ -495,7 +485,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> /// /// # Panics /// This function panics if any of the addresses are not unicast. - pub fn update_ip_addrs)>(&mut self, f: F) { + pub fn update_ip_addrs)>(&mut self, f: F) { f(&mut self.inner.ip_addrs); InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) } @@ -511,11 +501,11 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> self.inner.ipv4_address() } - pub fn routes(&self) -> &Routes<'e> { + pub fn routes(&self) -> &Routes<'a> { &self.inner.routes } - pub fn routes_mut(&mut self) -> &mut Routes<'e> { + pub fn routes_mut(&mut self) -> &mut Routes<'a> { &mut self.inner.routes } @@ -752,7 +742,7 @@ impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> } } -impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { +impl<'a> InterfaceInner<'a> { fn check_ethernet_addr(addr: &EthernetAddress) { if addr.is_multicast() { panic!("Ethernet address {} is not unicast", addr) @@ -1730,8 +1720,7 @@ mod test { use super::{EthernetPacket, IpPacket}; - fn create_loopback<'a, 'b, 'c>() -> (EthernetInterface<'static, 'b, 'c, Loopback>, - SocketSet<'a>) { + fn create_loopback<'a>() -> (EthernetInterface<'a, Loopback>, SocketSet<'a>) { // Create a basic device let device = Loopback::new(); let ip_addrs = [ @@ -1757,7 +1746,7 @@ mod test { } #[cfg(feature = "proto-igmp")] - fn recv_all<'b>(iface: &mut EthernetInterface<'static, 'b, 'static, Loopback>, timestamp: Instant) -> Vec> { + fn recv_all(iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); while let Some((rx, _tx)) = iface.device.receive() { rx.consume(timestamp, |pkt| { @@ -2533,7 +2522,7 @@ mod test { #[test] #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { - fn recv_igmp<'b>(mut iface: &mut EthernetInterface<'static, 'b, 'static, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + fn recv_igmp(mut iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { let checksum_caps = &iface.device.capabilities().checksum; recv_all(&mut iface, timestamp) .iter() From c9d15551d38f1af319045c7e665b27d8a3389921 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Jan 2021 23:28:48 +0100 Subject: [PATCH 072/566] Update README.md with now-supported features. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index edcc96341..331f7aa06 100644 --- a/README.md +++ b/README.md @@ -106,11 +106,11 @@ The TCP protocol is supported over IPv4 and IPv6, and server and client TCP sock * Multiple packets are transmitted without waiting for an acknowledgement. * Reassembly of out-of-order segments is supported, with no more than 4 or 32 gaps in sequence space. * Keep-alive packets may be sent at a configurable interval. - * Retransmission timeout starts at a fixed interval of 100 ms and doubles every time. + * Retransmission timeout starts at at an estimate of RTT, and doubles every time. * Time-wait timeout has a fixed interval of 10 s. * User timeout has a configurable interval. + * Delayed acknowledgements are supported, with configurable delay. * Selective acknowledgements are **not** implemented. - * Delayed acknowledgements are **not** implemented. * Silly window syndrome avoidance is **not** implemented. * Nagle's algorithm is **not** implemented. * Congestion control is **not** implemented. From b7a2dbd4f5c8c8db000d2dbef4432f2c8f85f598 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Jan 2021 00:14:15 +0100 Subject: [PATCH 073/566] Add CHANGELOG.md --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..e499995ce --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +No unreleased changes yet + +## [0.7.0] - 2021-01-20 + +### New features +- tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. (#351) +- sockets: Add support for attaching async/await Wakers to sockets. Wakers are woken on socket state changes. (#394) +- tcp: Set retransmission timeout based on an RTT estimation, instead of the previously fixed 100ms. This improves performance on high-latency links, such as mobile networks. (#406) +- tcp: add Delayed ACK support. On by default, with a 10ms delay. (#404) +- ip: Process broadcast packets directed to the subnet's broadcast address, such as 192.168.1.255. Previously broadcast packets were +only processed when directed to the 255.255.255.255 address. (#377) + +### Fixes +- udp,raw,icmp: Fix packet buffer panic caused by large payload (#332) +- dhcpv4: use offered ip in requested ip option (#310) +- dhcpv4: Re-export dhcp::clientv4::Config +- dhcpv4: Enable `proto-dhcpv4` feature by default. (#327) +- ethernet,arp: Allow for ARP retry during egress (#368) +- ethernet,arp: Only limit the neighbor cache rate after sending a request packet (#369) +- tcp: use provided ip for TcpSocket::connect instead of 0.0.0.0 (#329) +- tcp: Accept data packets in FIN_WAIT_2 state. (#350) +- tcp: Always send updated ack number in `ack_reply()`. (#353) +- tcp: allow sending ACKs in FinWait2 state. (#388) +- tcp: fix racey simultaneous close not sending FIN. (#398) +- tcp: Do not send window updates in states that shouldn't do so (#360) +- tcp: Return RST to unexpected ACK in SYN-SENT state. (#367) +- tcp: Take MTU into account during TcpSocket dispatch. (#384) +- tcp: don't send data outside the remote window (#387) +- phy: Take Ethernet header into account for MTU of RawSocket and TapInterface. (#393) +- phy: add null terminator to c-string passed to libc API (#372) + +### Quality of Life™ improvements +- Update to Rust 2018 edition (#396) +- Migrate CI to Github Actions (#390) +- Fix clippy lints, enforce clippy in CI (#395, #402, #403, #405, #407) +- Use #[non_exhaustive] for enums and structs (#409, #411) +- Simplify lifetime parameters of sockets, SocketSet, EthernetInterface (#410, #413) + +[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.7.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.6.0...v0.7.0 From fe8507b6232be320fe116fa80959269a745e4e12 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Jan 2021 00:17:40 +0100 Subject: [PATCH 074/566] Link to PRs in changelog. --- CHANGELOG.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e499995ce..9b795c59a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,38 +11,38 @@ No unreleased changes yet ## [0.7.0] - 2021-01-20 ### New features -- tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. (#351) -- sockets: Add support for attaching async/await Wakers to sockets. Wakers are woken on socket state changes. (#394) -- tcp: Set retransmission timeout based on an RTT estimation, instead of the previously fixed 100ms. This improves performance on high-latency links, such as mobile networks. (#406) -- tcp: add Delayed ACK support. On by default, with a 10ms delay. (#404) +- tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. ([351](https://github.com/smoltcp-rs/smoltcp/pull/351)) +- sockets: Add support for attaching async/await Wakers to sockets. Wakers are woken on socket state changes. ([394](https://github.com/smoltcp-rs/smoltcp/pull/394)) +- tcp: Set retransmission timeout based on an RTT estimation, instead of the previously fixed 100ms. This improves performance on high-latency links, such as mobile networks. ([406](https://github.com/smoltcp-rs/smoltcp/pull/406)) +- tcp: add Delayed ACK support. On by default, with a 10ms delay. ([404](https://github.com/smoltcp-rs/smoltcp/pull/404)) - ip: Process broadcast packets directed to the subnet's broadcast address, such as 192.168.1.255. Previously broadcast packets were -only processed when directed to the 255.255.255.255 address. (#377) +only processed when directed to the 255.255.255.255 address. ([377](https://github.com/smoltcp-rs/smoltcp/pull/377)) ### Fixes -- udp,raw,icmp: Fix packet buffer panic caused by large payload (#332) -- dhcpv4: use offered ip in requested ip option (#310) +- udp,raw,icmp: Fix packet buffer panic caused by large payload ([332](https://github.com/smoltcp-rs/smoltcp/pull/332)) +- dhcpv4: use offered ip in requested ip option ([310](https://github.com/smoltcp-rs/smoltcp/pull/310)) - dhcpv4: Re-export dhcp::clientv4::Config -- dhcpv4: Enable `proto-dhcpv4` feature by default. (#327) -- ethernet,arp: Allow for ARP retry during egress (#368) -- ethernet,arp: Only limit the neighbor cache rate after sending a request packet (#369) -- tcp: use provided ip for TcpSocket::connect instead of 0.0.0.0 (#329) -- tcp: Accept data packets in FIN_WAIT_2 state. (#350) -- tcp: Always send updated ack number in `ack_reply()`. (#353) -- tcp: allow sending ACKs in FinWait2 state. (#388) -- tcp: fix racey simultaneous close not sending FIN. (#398) -- tcp: Do not send window updates in states that shouldn't do so (#360) -- tcp: Return RST to unexpected ACK in SYN-SENT state. (#367) -- tcp: Take MTU into account during TcpSocket dispatch. (#384) -- tcp: don't send data outside the remote window (#387) -- phy: Take Ethernet header into account for MTU of RawSocket and TapInterface. (#393) -- phy: add null terminator to c-string passed to libc API (#372) +- dhcpv4: Enable `proto-dhcpv4` feature by default. ([327](https://github.com/smoltcp-rs/smoltcp/pull/327)) +- ethernet,arp: Allow for ARP retry during egress ([368](https://github.com/smoltcp-rs/smoltcp/pull/368)) +- ethernet,arp: Only limit the neighbor cache rate after sending a request packet ([369](https://github.com/smoltcp-rs/smoltcp/pull/369)) +- tcp: use provided ip for TcpSocket::connect instead of 0.0.0.0 ([329](https://github.com/smoltcp-rs/smoltcp/pull/329)) +- tcp: Accept data packets in FIN_WAIT_2 state. ([350](https://github.com/smoltcp-rs/smoltcp/pull/350)) +- tcp: Always send updated ack number in `ack_reply()`. ([353](https://github.com/smoltcp-rs/smoltcp/pull/353)) +- tcp: allow sending ACKs in FinWait2 state. ([388](https://github.com/smoltcp-rs/smoltcp/pull/388)) +- tcp: fix racey simultaneous close not sending FIN. ([398](https://github.com/smoltcp-rs/smoltcp/pull/398)) +- tcp: Do not send window updates in states that shouldn't do so ([360](https://github.com/smoltcp-rs/smoltcp/pull/360)) +- tcp: Return RST to unexpected ACK in SYN-SENT state. ([367](https://github.com/smoltcp-rs/smoltcp/pull/367)) +- tcp: Take MTU into account during TcpSocket dispatch. ([384](https://github.com/smoltcp-rs/smoltcp/pull/384)) +- tcp: don't send data outside the remote window ([387](https://github.com/smoltcp-rs/smoltcp/pull/387)) +- phy: Take Ethernet header into account for MTU of RawSocket and TapInterface. ([393](https://github.com/smoltcp-rs/smoltcp/pull/393)) +- phy: add null terminator to c-string passed to libc API ([372](https://github.com/smoltcp-rs/smoltcp/pull/372)) ### Quality of Life™ improvements -- Update to Rust 2018 edition (#396) -- Migrate CI to Github Actions (#390) -- Fix clippy lints, enforce clippy in CI (#395, #402, #403, #405, #407) -- Use #[non_exhaustive] for enums and structs (#409, #411) -- Simplify lifetime parameters of sockets, SocketSet, EthernetInterface (#410, #413) +- Update to Rust 2018 edition ([396](https://github.com/smoltcp-rs/smoltcp/pull/396)) +- Migrate CI to Github Actions ([390](https://github.com/smoltcp-rs/smoltcp/pull/390)) +- Fix clippy lints, enforce clippy in CI ([395](https://github.com/smoltcp-rs/smoltcp/pull/395), [402](https://github.com/smoltcp-rs/smoltcp/pull/402), [403](https://github.com/smoltcp-rs/smoltcp/pull/403), [405](https://github.com/smoltcp-rs/smoltcp/pull/405), [407](https://github.com/smoltcp-rs/smoltcp/pull/407)) +- Use #[non_exhaustive] for enums and structs ([409](https://github.com/smoltcp-rs/smoltcp/pull/409), [411](https://github.com/smoltcp-rs/smoltcp/pull/411)) +- Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD [0.7.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.6.0...v0.7.0 From 2650fc9ff457a73b6a99e5297b60d3083d9f88e9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Jan 2021 00:19:25 +0100 Subject: [PATCH 075/566] changelog: mention MSRV bump --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b795c59a..4d0a0a58e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ No unreleased changes yet ## [0.7.0] - 2021-01-20 +Minimum Supported Rust Version (MSRV) **bumped** from 1.36 to 1.40 + ### New features - tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. ([351](https://github.com/smoltcp-rs/smoltcp/pull/351)) - sockets: Add support for attaching async/await Wakers to sockets. Wakers are woken on socket state changes. ([394](https://github.com/smoltcp-rs/smoltcp/pull/394)) From 80ba25de059ea7ce2396e06a0b65214aeb362610 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Jan 2021 00:23:19 +0100 Subject: [PATCH 076/566] Bump version, update repo URLs --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6f1130bf..d41843e8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "smoltcp" -version = "0.6.0" +version = "0.7.0" edition = "2018" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." documentation = "https://docs.rs/smoltcp/" -homepage = "https://github.com/m-labs/smoltcp" -repository = "https://github.com/m-labs/smoltcp.git" +homepage = "https://github.com/smoltcp-rs/smoltcp" +repository = "https://github.com/smoltcp-rs/smoltcp.git" readme = "README.md" keywords = ["ip", "tcp", "udp", "ethernet", "network"] categories = ["embedded", "network-programming"] From b3b871e152f510b61f958e337552575c2a6493c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 Feb 2021 16:45:32 +0100 Subject: [PATCH 077/566] Fix build when with no socket features --- .github/workflows/test.yml | 3 +++ Cargo.toml | 9 +++++---- src/lib.rs | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4d11ebc5..80b7550cb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,9 @@ jobs: # Test default features. - default + # Test minimal featureset + - std proto-ipv4 + # Test features chosen to be as orthogonal as possible. - std ethernet phy-raw_socket proto-ipv6 socket-udp - std ethernet phy-tap_interface proto-ipv6 socket-udp diff --git a/Cargo.toml b/Cargo.toml index d41843e8e..4ea3a949c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,10 +39,11 @@ ethernet = [] "proto-igmp" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4", "socket-raw"] "proto-ipv6" = [] -"socket-raw" = [] -"socket-udp" = [] -"socket-tcp" = [] -"socket-icmp" = [] +"socket" = [] +"socket-raw" = ["socket"] +"socket-udp" = ["socket"] +"socket-tcp" = ["socket"] +"socket-icmp" = ["socket"] "async" = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ diff --git a/src/lib.rs b/src/lib.rs index 7198b57d3..cc0e6c224 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,7 @@ pub mod storage; pub mod phy; pub mod wire; pub mod iface; +#[cfg(feature = "socket")] pub mod socket; pub mod time; #[cfg(feature = "proto-dhcpv4")] From 9248b5c0d3bc1b3f2188a8fb315b4fa7c418810d Mon Sep 17 00:00:00 2001 From: Vlad Krasnov Date: Tue, 9 Feb 2021 14:07:57 -0500 Subject: [PATCH 078/566] Ask for dst_addr in neighbor solicitation request --- src/iface/ethernet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 22b6e3e61..bb60bfb50 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1595,7 +1595,7 @@ impl<'a> InterfaceInner<'a> { dst_addr); let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { - target_addr: src_addr, + target_addr: dst_addr, lladdr: Some(self.ethernet_addr), }); From 3a721ca810f78ff0c20ea8b018381169c544c8c6 Mon Sep 17 00:00:00 2001 From: Ben Stabler Date: Thu, 11 Feb 2021 22:49:33 -0800 Subject: [PATCH 079/566] Make wire/ipv4 functions const where possible. --- src/wire/ipv4.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 6d51d4118..dc74c9a88 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -39,7 +39,7 @@ impl Address { pub const MULTICAST_ALL_ROUTERS: Address = Address([224, 0, 0, 2]); /// Construct an IPv4 address from parts. - pub fn new(a0: u8, a1: u8, a2: u8, a3: u8) -> Address { + pub const fn new(a0: u8, a1: u8, a2: u8, a3: u8) -> Address { Address([a0, a1, a2, a3]) } @@ -54,7 +54,7 @@ impl Address { } /// Return an IPv4 address as a sequence of octets, in big-endian. - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { &self.0 } @@ -71,12 +71,12 @@ impl Address { } /// Query whether the address is a multicast address. - pub fn is_multicast(&self) -> bool { + pub const fn is_multicast(&self) -> bool { self.0[0] & 0xf0 == 224 } /// Query whether the address falls into the "unspecified" range. - pub fn is_unspecified(&self) -> bool { + pub const fn is_unspecified(&self) -> bool { self.0[0] == 0 } @@ -86,7 +86,7 @@ impl Address { } /// Query whether the address falls into the "loopback" range. - pub fn is_loopback(&self) -> bool { + pub const fn is_loopback(&self) -> bool { self.0[0] == 127 } } @@ -125,8 +125,10 @@ impl Cidr { /// /// # Panics /// This function panics if the prefix length is larger than 32. - pub fn new(address: Address, prefix_len: u8) -> Cidr { - assert!(prefix_len <= 32); + pub const fn new(address: Address, prefix_len: u8) -> Cidr { + // Replace with const panic (or assert) when stabilized + // see: https://github.com/rust-lang/rust/issues/51999 + ["Prefix length should be <= 32"][(prefix_len > 32) as usize]; Cidr { address, prefix_len } } @@ -141,17 +143,17 @@ impl Cidr { } /// Return the address of this IPv4 CIDR block. - pub fn address(&self) -> Address { + pub const fn address(&self) -> Address { self.address } /// Return the prefix length of this IPv4 CIDR block. - pub fn prefix_len(&self) -> u8 { + pub const fn prefix_len(&self) -> u8 { self.prefix_len } /// Return the network mask of this IPv4 CIDR. - pub fn netmask(&self) -> Address { + pub const fn netmask(&self) -> Address { if self.prefix_len == 0 { return Address([0, 0, 0, 0]); } @@ -188,7 +190,7 @@ impl Cidr { } /// Return the network block of this IPv4 CIDR. - pub fn network(&self) -> Cidr { + pub const fn network(&self) -> Cidr { let mask = self.netmask().0; let network = [ self.address.0[0] & mask[0], From d5a98dd0598567c8a37fa40c660551b37898d2ef Mon Sep 17 00:00:00 2001 From: Ben Stabler Date: Thu, 11 Feb 2021 23:10:35 -0800 Subject: [PATCH 080/566] Silence clippy lint for const assert work-around, remove const where using features not yet stable in 1.40 --- src/wire/ipv4.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index dc74c9a88..a5369edb6 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -54,7 +54,7 @@ impl Address { } /// Return an IPv4 address as a sequence of octets, in big-endian. - pub const fn as_bytes(&self) -> &[u8] { + pub fn as_bytes(&self) -> &[u8] { &self.0 } @@ -125,6 +125,7 @@ impl Cidr { /// /// # Panics /// This function panics if the prefix length is larger than 32. + #[allow(clippy::no_effect)] pub const fn new(address: Address, prefix_len: u8) -> Cidr { // Replace with const panic (or assert) when stabilized // see: https://github.com/rust-lang/rust/issues/51999 @@ -153,7 +154,7 @@ impl Cidr { } /// Return the network mask of this IPv4 CIDR. - pub const fn netmask(&self) -> Address { + pub fn netmask(&self) -> Address { if self.prefix_len == 0 { return Address([0, 0, 0, 0]); } @@ -190,7 +191,7 @@ impl Cidr { } /// Return the network block of this IPv4 CIDR. - pub const fn network(&self) -> Cidr { + pub fn network(&self) -> Cidr { let mask = self.netmask().0; let network = [ self.address.0[0] & mask[0], From d8c2f16c01697d6616e629b9377b9c797f36fcf0 Mon Sep 17 00:00:00 2001 From: "Nathan K. Zhinn" Date: Wed, 17 Feb 2021 15:11:56 +0000 Subject: [PATCH 081/566] Fix bpf ffi and add OpenBSD bpf cfg This fixes the ordering that was causing a dealloc before the FFI call to libc open. --- src/phy/sys/bpf.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/phy/sys/bpf.rs b/src/phy/sys/bpf.rs index 3d802424a..8a8efe0a3 100644 --- a/src/phy/sys/bpf.rs +++ b/src/phy/sys/bpf.rs @@ -6,16 +6,16 @@ use libc; use super::{ifreq, ifreq_for}; /// set interface -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "openbsd"))] const BIOCSETIF: libc::c_ulong = 0x8020426c; /// get buffer length -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "openbsd"))] const BIOCGBLEN: libc::c_ulong = 0x40044266; /// set immediate/nonblocking read -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "openbsd"))] const BIOCIMMEDIATE: libc::c_ulong = 0x80044270; // TODO: check if this is same for OSes other than macos -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "openbsd"))] const BPF_HDRLEN: usize = 18; macro_rules! try_ioctl { @@ -43,8 +43,8 @@ impl AsRawFd for BpfDevice { fn open_device() -> io::Result { unsafe { for i in 0..256 { - let dev = format!("/dev/bpf{}\0", i).as_ptr() as *const libc::c_char; - match libc::open(dev, libc::O_RDWR) { + let dev = format!("/dev/bpf{}\0", i); + match libc::open(dev.as_ptr() as *const libc::c_char, libc::O_RDWR) { -1 => continue, fd => return Ok(fd), }; From c12fc6fbb08473a8b470b3bc736878ff9724ad83 Mon Sep 17 00:00:00 2001 From: "Nathan K. Zhinn" <79179929+pk-j-xyz@users.noreply.github.com> Date: Mon, 1 Mar 2021 04:37:37 +0000 Subject: [PATCH 082/566] Fix BPF header length on OpenBSD. The actual header length may be larger than the bpf_hdr struct due to aligning: https://github.com/openbsd/src/blob/37ecb4d066e5566411cc16b362d3960c93b1d0be/sys/net/bpf.c#L1649 https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/net/bpf.c#L3580 Tests are only valid for 32 and 64 bit architectures. I did not bother guarding them with additional cfg flags. --- src/phy/sys/bpf.rs | 33 +++++++++++++++++++++++++++++++-- src/wire/ethernet.rs | 11 +++++++---- src/wire/mod.rs | 1 + 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/phy/sys/bpf.rs b/src/phy/sys/bpf.rs index 8a8efe0a3..0c1c519f4 100644 --- a/src/phy/sys/bpf.rs +++ b/src/phy/sys/bpf.rs @@ -1,8 +1,10 @@ use std::io; +use std::mem; use std::os::unix::io::{AsRawFd, RawFd}; use libc; +use crate::wire::ETHERNET_HEADER_LEN; use super::{ifreq, ifreq_for}; /// set interface @@ -14,9 +16,19 @@ const BIOCGBLEN: libc::c_ulong = 0x40044266; /// set immediate/nonblocking read #[cfg(any(target_os = "macos", target_os = "openbsd"))] const BIOCIMMEDIATE: libc::c_ulong = 0x80044270; -// TODO: check if this is same for OSes other than macos +/// set bpf_hdr struct size +#[cfg(target_os = "macos")] +const SIZEOF_BPF_HDR: usize = 18; +/// set bpf_hdr struct size +#[cfg(target_os = "openbsd")] +const SIZEOF_BPF_HDR: usize = 24; +/// The actual header length may be larger than the bpf_hdr struct due to aligning +/// see https://github.com/openbsd/src/blob/37ecb4d066e5566411cc16b362d3960c93b1d0be/sys/net/bpf.c#L1649 +/// and https://github.com/apple/darwin-xnu/blob/8f02f2a044b9bb1ad951987ef5bab20ec9486310/bsd/net/bpf.c#L3580 #[cfg(any(target_os = "macos", target_os = "openbsd"))] -const BPF_HDRLEN: usize = 18; +const BPF_HDRLEN: usize = (((SIZEOF_BPF_HDR + ETHERNET_HEADER_LEN) + mem::align_of::() - 1) + & !(mem::align_of::() - 1)) + - ETHERNET_HEADER_LEN; macro_rules! try_ioctl { ($fd:expr,$cmd:expr,$req:expr) => { @@ -145,3 +157,20 @@ impl Drop for BpfDevice { } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[cfg(target_os = "macos")] + fn test_aligned_bpf_hdr_len() { + assert_eq!(18, BPF_HDRLEN); + } + + #[test] + #[cfg(target_os = "openbsd")] + fn test_aligned_bpf_hdr_len() { + assert_eq!(26, BPF_HDRLEN); + } +} diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 449454a9e..0c425f18b 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -91,6 +91,9 @@ mod field { pub const PAYLOAD: Rest = 14..; } +/// The Ethernet header length +pub const HEADER_LEN: usize = field::PAYLOAD.start; + impl> Frame { /// Imbue a raw octet buffer with Ethernet frame structure. pub fn new_unchecked(buffer: T) -> Frame { @@ -111,7 +114,7 @@ impl> Frame { /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); - if len < field::PAYLOAD.start { + if len < HEADER_LEN { Err(Error::Truncated) } else { Ok(()) @@ -125,13 +128,13 @@ impl> Frame { /// Return the length of a frame header. pub fn header_len() -> usize { - field::PAYLOAD.start + HEADER_LEN } /// Return the length of a buffer required to hold a packet with the payload /// of a given length. pub fn buffer_len(payload_len: usize) -> usize { - field::PAYLOAD.start + payload_len + HEADER_LEN + payload_len } /// Return the destination address field. @@ -262,7 +265,7 @@ impl Repr { /// Return the length of a header that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { - field::PAYLOAD.start + HEADER_LEN } /// Emit a high-level representation into an Ethernet II frame. diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 8641f1438..c813c7b9f 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -119,6 +119,7 @@ pub use self::pretty_print::PrettyPrinter; pub use self::ethernet::{EtherType as EthernetProtocol, Address as EthernetAddress, Frame as EthernetFrame, + HEADER_LEN as ETHERNET_HEADER_LEN, Repr as EthernetRepr}; #[cfg(all(feature = "proto-ipv4", feature = "ethernet"))] From 6316b89da8f54058763f6bb88591cc1f2082f9fa Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 7 Mar 2021 04:09:44 +0000 Subject: [PATCH 083/566] socket/tcp: Fix is_active comment typo --- src/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 2edccb873..f158826bd 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -752,7 +752,7 @@ impl<'a> TcpSocket<'a> { /// If a connection is established, [abort](#method.close) will send a reset to /// the remote endpoint. /// - /// In terms of the TCP state machine, the socket must be in the `CLOSED`, `TIME-WAIT`, + /// In terms of the TCP state machine, the socket must not be in the `CLOSED`, `TIME-WAIT`, /// or `LISTEN` state. #[inline] pub fn is_active(&self) -> bool { From 6287584f7f4b3e29115f1cc27d8b8ab146782ad3 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 7 Mar 2021 04:14:10 +0000 Subject: [PATCH 084/566] socket/tcp: Fix missing ` in doc comment --- src/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 2edccb873..baccd3579 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -858,7 +858,7 @@ impl<'a> TcpSocket<'a> { /// Call `f` with the largest contiguous slice of octets in the transmit buffer, /// and enqueue the amount of elements returned by `f`. /// - /// This function returns `Err(Error::Illegal) if the transmit half of + /// This function returns `Err(Error::Illegal)` if the transmit half of /// the connection is not open; see [may_send](#method.may_send). pub fn send<'b, F, R>(&'b mut self, f: F) -> Result where F: FnOnce(&'b mut [u8]) -> (usize, R) { From ed867800798ab2b6ba24adfacbcdf4a9cf3f3b0c Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 12 Mar 2021 06:02:04 +0000 Subject: [PATCH 085/566] Simplify enum_with_unknown! macro. --- src/macros.rs | 15 +-------------- src/phy/pcap_writer.rs | 2 +- src/wire/icmpv4.rs | 10 +++++----- src/wire/icmpv6.rs | 8 ++++---- src/wire/igmp.rs | 2 +- src/wire/ipv6option.rs | 4 ++-- src/wire/ipv6routing.rs | 2 +- src/wire/mld.rs | 2 +- src/wire/ndiscoption.rs | 2 +- 9 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 2c1da7f64..f8cb381b6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -23,21 +23,8 @@ macro_rules! enum_with_unknown { ( $( #[$enum_attr:meta] )* pub enum $name:ident($ty:ty) { - $( $variant:ident = $value:expr ),+ $(,)* - } - ) => { - enum_with_unknown! { - $( #[$enum_attr] )* - pub doc enum $name($ty) { - $( #[doc(shown)] $variant = $value ),+ - } - } - }; - ( - $( #[$enum_attr:meta] )* - pub doc enum $name:ident($ty:ty) { $( - $( #[$variant_attr:meta] )+ + $( #[$variant_attr:meta] )* $variant:ident = $value:expr $(,)* ),+ } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index afa0d4c57..afee86691 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -10,7 +10,7 @@ use crate::time::Instant; enum_with_unknown! { /// Captured packet header type. - pub doc enum PcapLinkType(u32) { + pub enum PcapLinkType(u32) { /// Ethernet frames Ethernet = 1, /// IPv4 or IPv6 packets (depending on the version field) diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 5748c899c..955afb1b0 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -8,7 +8,7 @@ use crate::wire::{Ipv4Packet, Ipv4Repr}; enum_with_unknown! { /// Internet protocol control message type. - pub doc enum Message(u8) { + pub enum Message(u8) { /// Echo reply EchoReply = 0, /// Destination unreachable @@ -52,7 +52,7 @@ impl fmt::Display for Message { enum_with_unknown! { /// Internet protocol control message subtype for type "Destination Unreachable". - pub doc enum DstUnreachable(u8) { + pub enum DstUnreachable(u8) { /// Destination network unreachable NetUnreachable = 0, /// Destination host unreachable @@ -131,7 +131,7 @@ impl fmt::Display for DstUnreachable { enum_with_unknown! { /// Internet protocol control message subtype for type "Redirect Message". - pub doc enum Redirect(u8) { + pub enum Redirect(u8) { /// Redirect Datagram for the Network Net = 0, /// Redirect Datagram for the Host @@ -145,7 +145,7 @@ enum_with_unknown! { enum_with_unknown! { /// Internet protocol control message subtype for type "Time Exceeded". - pub doc enum TimeExceeded(u8) { + pub enum TimeExceeded(u8) { /// TTL expired in transit TtlExpired = 0, /// Fragment reassembly time exceeded @@ -155,7 +155,7 @@ enum_with_unknown! { enum_with_unknown! { /// Internet protocol control message subtype for type "Parameter Problem". - pub doc enum ParamProblem(u8) { + pub enum ParamProblem(u8) { /// Pointer indicates the error AtPointer = 0, /// Missing a required option diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index bd232449c..597414a80 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -11,7 +11,7 @@ use crate::wire::NdiscRepr; enum_with_unknown! { /// Internet protocol control message type. - pub doc enum Message(u8) { + pub enum Message(u8) { /// Destination Unreachable. DstUnreachable = 0x01, /// Packet Too Big. @@ -98,7 +98,7 @@ impl fmt::Display for Message { enum_with_unknown! { /// Internet protocol control message subtype for type "Destination Unreachable". - pub doc enum DstUnreachable(u8) { + pub enum DstUnreachable(u8) { /// No Route to destination. NoRoute = 0, /// Communication with destination administratively prohibited. @@ -141,7 +141,7 @@ impl fmt::Display for DstUnreachable { enum_with_unknown! { /// Internet protocol control message subtype for the type "Parameter Problem". - pub doc enum ParamProblem(u8) { + pub enum ParamProblem(u8) { /// Erroneous header field encountered. ErroneousHdrField = 0, /// Unrecognized Next Header type encountered. @@ -168,7 +168,7 @@ impl fmt::Display for ParamProblem { enum_with_unknown! { /// Internet protocol control message subtype for the type "Time Exceeded". - pub doc enum TimeExceeded(u8) { + pub enum TimeExceeded(u8) { /// Hop limit exceeded in transit. HopLimitExceeded = 0, /// Fragment reassembly time exceeded. diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 8fac53534..237f32b05 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -9,7 +9,7 @@ use crate::wire::Ipv4Address; enum_with_unknown! { /// Internet Group Management Protocol v1/v2 message version/type. - pub doc enum Message(u8) { + pub enum Message(u8) { /// Membership Query MembershipQuery = 0x11, /// Version 2 Membership Report diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 726428464..023f74ffb 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -3,7 +3,7 @@ use crate::{Error, Result}; enum_with_unknown! { /// IPv6 Extension Header Option Type - pub doc enum Type(u8) { + pub enum Type(u8) { /// 1 byte of padding Pad1 = 0, /// Multiple bytes of padding @@ -24,7 +24,7 @@ impl fmt::Display for Type { enum_with_unknown! { /// Action required when parsing the given IPv6 Extension /// Header Option Type fails - pub doc enum FailureType(u8) { + pub enum FailureType(u8) { /// Skip this option and continue processing the packet Skip = 0b00000000, /// Discard the containing packet diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index f4078996b..a76d38efa 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -6,7 +6,7 @@ use crate::wire::Ipv6Address as Address; enum_with_unknown! { /// IPv6 Extension Routing Header Routing Type - pub doc enum Type(u8) { + pub enum Type(u8) { /// Source Route (DEPRECATED) /// /// See https://tools.ietf.org/html/rfc5095 for details. diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 2ea35a907..8fe5e8dc6 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -15,7 +15,7 @@ enum_with_unknown! { /// more details. /// /// [RFC 3810 § 5.2.12]: https://tools.ietf.org/html/rfc3010#section-5.2.12 - pub doc enum RecordType(u8) { + pub enum RecordType(u8) { /// Interface has a filter mode of INCLUDE for the specified multicast address. ModeIsInclude = 0x01, /// Interface has a filter mode of EXCLUDE for the specified multicast address. diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 88b22f027..6ea9721ed 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -8,7 +8,7 @@ use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; enum_with_unknown! { /// NDISC Option Type - pub doc enum Type(u8) { + pub enum Type(u8) { /// Source Link-layer Address SourceLinkLayerAddr = 0x1, /// Target Link-layer Address From 785e17bb73b10510ded4a98a932d1070226d15e9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 12 Mar 2021 06:16:17 +0000 Subject: [PATCH 086/566] Enable all Linux `phy` components for Android as well. Fixes #433. --- src/phy/mod.rs | 4 ++-- src/phy/sys/mod.rs | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 03bc779bc..e2962fdaa 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -98,7 +98,7 @@ mod pcap_writer; mod loopback; #[cfg(all(feature = "phy-raw_socket", unix))] mod raw_socket; -#[cfg(all(feature = "phy-tap_interface", target_os = "linux"))] +#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] mod tap_interface; #[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] @@ -112,7 +112,7 @@ pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; pub use self::loopback::Loopback; #[cfg(all(feature = "phy-raw_socket", unix))] pub use self::raw_socket::RawSocket; -#[cfg(all(feature = "phy-tap_interface", target_os = "linux"))] +#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] pub use self::tap_interface::TapInterface; #[cfg(feature = "ethernet")] diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 6f1665be3..59f59cfdc 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -4,22 +4,22 @@ use std::{mem, ptr, io}; use std::os::unix::io::RawFd; use crate::time::Duration; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[path = "linux.rs"] mod imp; -#[cfg(all(feature = "phy-raw_socket", target_os = "linux"))] +#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] pub mod raw_socket; -#[cfg(all(feature = "phy-raw_socket", not(target_os = "linux"), unix))] +#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub mod bpf; -#[cfg(all(feature = "phy-tap_interface", target_os = "linux"))] +#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] pub mod tap_interface; -#[cfg(all(feature = "phy-raw_socket", target_os = "linux"))] +#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] pub use self::raw_socket::RawSocketDesc; -#[cfg(all(feature = "phy-raw_socket", not(target_os = "linux"), unix))] +#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub use self::bpf::BpfDevice as RawSocketDesc; -#[cfg(all(feature = "phy-tap_interface", target_os = "linux"))] +#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] pub use self::tap_interface::TapInterfaceDesc; /// Wait until given file descriptor becomes readable, but no longer than given timeout. @@ -79,7 +79,8 @@ fn ifreq_for(name: &str) -> ifreq { ifreq } -#[cfg(all(target_os = "linux", any(feature = "phy-tap_interface", feature = "phy-raw_socket")))] +#[cfg(all(any(target_os = "linux", target_os = "android"), + any(feature = "phy-tap_interface", feature = "phy-raw_socket")))] fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, cmd: libc::c_ulong) -> io::Result { unsafe { From 3eea096b7e9430cad601372781f065534aa69fb4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 12 Mar 2021 14:03:51 +0100 Subject: [PATCH 087/566] Adding DHCP lease management --- src/dhcp/clientv4.rs | 48 +++++++++++++++++++++++++++++++++++--------- src/wire/dhcpv4.rs | 20 ++++++++++++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index a2d33426e..15eb5d239 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -13,7 +13,7 @@ use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT}; const DISCOVER_TIMEOUT: u64 = 10; const REQUEST_TIMEOUT: u64 = 1; const REQUEST_RETRIES: u16 = 15; -const RENEW_INTERVAL: u64 = 60; +const DEFAULT_RENEW_INTERVAL: u32 = 60; const RENEW_RETRIES: u16 = 3; const PARAMETER_REQUEST_LIST: &[u8] = &[ dhcpv4_field::OPT_SUBNET_MASK, @@ -59,6 +59,8 @@ pub struct Client { raw_handle: SocketHandle, /// When to send next request next_egress: Instant, + /// When any existing DHCP address will expire. + lease_expiration: Option, transaction_id: u32, } @@ -104,6 +106,7 @@ impl Client { raw_handle, next_egress: now, transaction_id: 1, + lease_expiration: None, } } @@ -114,6 +117,25 @@ impl Client { self.next_egress - now } + /// Check if the existing IP address lease has expired. + /// + /// # Note + /// RC 2131 requires that a client immediately cease usage of an + /// address once the lease has expired. + /// + /// # Args + /// * `now` - The current network time instant. + /// + /// # Returns + /// True if a lease exists and it has expired. False otherwise. + pub fn lease_expired(&self, now: Instant) -> bool { + if let Some(expiration) = self.lease_expiration { + return now > expiration + } + + false + } + /// Process incoming packets on the contained RawSocket, and send /// DHCP requests when timeouts are ready. /// @@ -199,12 +221,18 @@ impl Client { // once we receive the ack, we can pass the config to the user let config = if dhcp_repr.message_type == DhcpMessageType::Ack { - let address = dhcp_repr.subnet_mask - .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len()) - .map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len)); - let router = dhcp_repr.router; - let dns_servers = dhcp_repr.dns_servers - .unwrap_or([None; 3]); + let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2); + self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into())); + + // RFC 2131 indicates clients should renew a lease halfway through its expiration. + self.next_egress = now + Duration::from_secs((lease_duration / 2).into()); + + let address = dhcp_repr.subnet_mask + .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len()) + .map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len)); + let router = dhcp_repr.router; + let dns_servers = dhcp_repr.dns_servers + .unwrap_or([None; 3]); Some(Config { address, router, dns_servers }) } else { None @@ -227,7 +255,6 @@ impl Client { if dhcp_repr.message_type == DhcpMessageType::Ack && server_identifier == r_state.server_identifier => { - self.next_egress = now + Duration::from_secs(RENEW_INTERVAL); let p_state = RenewState { retry: 0, endpoint_ip: *src_ip, @@ -239,7 +266,7 @@ impl Client { if dhcp_repr.message_type == DhcpMessageType::Ack && server_identifier == p_state.server_identifier => { - self.next_egress = now + Duration::from_secs(RENEW_INTERVAL); + self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into()); p_state.retry = 0; None } @@ -294,6 +321,7 @@ impl Client { server_identifier: None, parameter_request_list: None, max_size: Some(raw_socket.payload_recv_capacity() as u16), + lease_duration: None, dns_servers: None, }; let mut send_packet = |iface, endpoint, dhcp_repr| { @@ -330,7 +358,7 @@ impl Client { } ClientState::Renew(ref mut p_state) => { p_state.retry += 1; - self.next_egress = now + Duration::from_secs(RENEW_INTERVAL); + self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into()); let endpoint = IpEndpoint { addr: p_state.endpoint_ip.into(), diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 64e9a9c4a..f7dfeca4f 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -50,6 +50,7 @@ pub enum DhcpOption<'a> { RequestedIp(Ipv4Address), ClientIdentifier(EthernetAddress), ServerIdentifier(Ipv4Address), + IpLeaseTime(u32), Router(Ipv4Address), SubnetMask(Ipv4Address), MaximumDhcpMessageSize(u16), @@ -103,6 +104,9 @@ impl<'a> DhcpOption<'a> { (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]])); } + (field::OPT_IP_LEASE_TIME, 4) => { + option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) + } (_, _) => { option = DhcpOption::Other { kind: kind, data: data }; } @@ -129,6 +133,7 @@ impl<'a> DhcpOption<'a> { &DhcpOption::MaximumDhcpMessageSize(_) => { 4 } + &DhcpOption::IpLeaseTime(_) => 6, &DhcpOption::Other { data, .. } => 2 + data.len() } } @@ -178,6 +183,10 @@ impl<'a> DhcpOption<'a> { buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE; buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]); } + DhcpOption::IpLeaseTime(lease_time) => { + buffer[0] = field::OPT_IP_LEASE_TIME; + buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]); + } DhcpOption::Other { kind, data: provided } => { buffer[0] = kind; buffer[2..skip_length].copy_from_slice(provided); @@ -674,6 +683,8 @@ pub struct Repr<'a> { pub dns_servers: Option<[Option; 3]>, /// The maximum size dhcp packet the interface can receive pub max_size: Option, + /// The DHCP IP lease duration, specified in seconds. + pub lease_duration: Option } impl<'a> Repr<'a> { @@ -725,6 +736,7 @@ impl<'a> Repr<'a> { let mut parameter_request_list = None; let mut dns_servers = None; let mut max_size = None; + let mut lease_duration = None; let mut options = packet.options()?; while !options.is_empty() { @@ -755,6 +767,9 @@ impl<'a> Repr<'a> { DhcpOption::MaximumDhcpMessageSize(size) => { max_size = Some(size); } + DhcpOption::IpLeaseTime(duration) => { + lease_duration = Some(duration); + } DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => { parameter_request_list = Some(data); } @@ -776,6 +791,7 @@ impl<'a> Repr<'a> { transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip, broadcast, requested_ip, server_identifier, router, subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size, + lease_duration, message_type: message_type?, }) } @@ -820,6 +836,9 @@ impl<'a> Repr<'a> { if let Some(size) = self.max_size { let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp); } + if let Some(duration) = self.lease_duration { + let tmp = options; options = DhcpOption::IpLeaseTime(duration).emit(tmp); + } if let Some(list) = self.parameter_request_list { let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }; let tmp = options; options = option.emit(tmp); @@ -984,6 +1003,7 @@ mod test { relay_agent_ip: IP_NULL, broadcast: false, max_size: Some(DHCP_SIZE), + lease_duration: None, requested_ip: Some(IP_NULL), client_identifier: Some(CLIENT_MAC), server_identifier: None, From bda278f5faf6ffbae98976d1de04c267a2fc071d Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 12 Mar 2021 16:14:44 +0100 Subject: [PATCH 088/566] Refactoring retry mechanism --- src/dhcp/clientv4.rs | 47 +++++++------------------------------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 15eb5d239..c225e9d3c 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -14,7 +14,6 @@ const DISCOVER_TIMEOUT: u64 = 10; const REQUEST_TIMEOUT: u64 = 1; const REQUEST_RETRIES: u16 = 15; const DEFAULT_RENEW_INTERVAL: u32 = 60; -const RENEW_RETRIES: u16 = 3; const PARAMETER_REQUEST_LIST: &[u8] = &[ dhcpv4_field::OPT_SUBNET_MASK, dhcpv4_field::OPT_ROUTER, @@ -39,7 +38,6 @@ struct RequestState { #[derive(Debug)] struct RenewState { - retry: u16, endpoint_ip: Ipv4Address, server_identifier: Ipv4Address, } @@ -60,7 +58,7 @@ pub struct Client { /// When to send next request next_egress: Instant, /// When any existing DHCP address will expire. - lease_expiration: Option, + lease_expiration: Instant, transaction_id: u32, } @@ -106,7 +104,7 @@ impl Client { raw_handle, next_egress: now, transaction_id: 1, - lease_expiration: None, + lease_expiration: now, } } @@ -117,25 +115,6 @@ impl Client { self.next_egress - now } - /// Check if the existing IP address lease has expired. - /// - /// # Note - /// RC 2131 requires that a client immediately cease usage of an - /// address once the lease has expired. - /// - /// # Args - /// * `now` - The current network time instant. - /// - /// # Returns - /// True if a lease exists and it has expired. False otherwise. - pub fn lease_expired(&self, now: Instant) -> bool { - if let Some(expiration) = self.lease_expiration { - return now > expiration - } - - false - } - /// Process incoming packets on the contained RawSocket, and send /// DHCP requests when timeouts are ready. /// @@ -222,7 +201,7 @@ impl Client { // once we receive the ack, we can pass the config to the user let config = if dhcp_repr.message_type == DhcpMessageType::Ack { let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2); - self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into())); + self.lease_expiration = now + Duration::from_secs(lease_duration.into()); // RFC 2131 indicates clients should renew a lease halfway through its expiration. self.next_egress = now + Duration::from_secs((lease_duration / 2).into()); @@ -256,20 +235,11 @@ impl Client { server_identifier == r_state.server_identifier => { let p_state = RenewState { - retry: 0, endpoint_ip: *src_ip, server_identifier, }; Some(ClientState::Renew(p_state)) } - ClientState::Renew(ref mut p_state) - if dhcp_repr.message_type == DhcpMessageType::Ack && - server_identifier == p_state.server_identifier => - { - self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into()); - p_state.retry = 0; - None - } _ => None }.map(|new_state| self.state = new_state); @@ -283,13 +253,11 @@ impl Client { net_debug!("DHCP request retries exceeded, restarting discovery"); true } - ClientState::Renew(ref mut r_state) if r_state.retry >= RENEW_RETRIES => { - net_debug!("DHCP renew retries exceeded, restarting discovery"); - true - } _ => false }; - if retries_exceeded { + + if now >= self.lease_expiration || retries_exceeded { + net_debug!("DHCP lease expiration / retries exceeded"); self.reset(now); // Return a config now so that user code assigns the // 0.0.0.0/0 address, which will be used sending a DHCP @@ -324,12 +292,12 @@ impl Client { lease_duration: None, dns_servers: None, }; + let mut send_packet = |iface, endpoint, dhcp_repr| { send_packet(iface, raw_socket, &endpoint, &dhcp_repr, checksum_caps) .map(|()| None) }; - match self.state { ClientState::Discovering => { self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT); @@ -357,7 +325,6 @@ impl Client { send_packet(iface, endpoint, dhcp_repr) } ClientState::Renew(ref mut p_state) => { - p_state.retry += 1; self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into()); let endpoint = IpEndpoint { From 532fb02297fe4345c2986bf990abed1bc561c432 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 12 Mar 2021 17:28:00 +0100 Subject: [PATCH 089/566] Updating control logic --- src/dhcp/clientv4.rs | 14 ++++++++------ src/wire/dhcpv4.rs | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index c225e9d3c..5896f09e9 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -58,7 +58,7 @@ pub struct Client { /// When to send next request next_egress: Instant, /// When any existing DHCP address will expire. - lease_expiration: Instant, + lease_expiration: Option, transaction_id: u32, } @@ -104,7 +104,7 @@ impl Client { raw_handle, next_egress: now, transaction_id: 1, - lease_expiration: now, + lease_expiration: None, } } @@ -201,7 +201,7 @@ impl Client { // once we receive the ack, we can pass the config to the user let config = if dhcp_repr.message_type == DhcpMessageType::Ack { let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2); - self.lease_expiration = now + Duration::from_secs(lease_duration.into()); + self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into())); // RFC 2131 indicates clients should renew a lease halfway through its expiration. self.next_egress = now + Duration::from_secs((lease_duration / 2).into()); @@ -256,8 +256,9 @@ impl Client { _ => false }; - if now >= self.lease_expiration || retries_exceeded { - net_debug!("DHCP lease expiration / retries exceeded"); + let lease_expired = self.lease_expiration.map_or(false, |expiration| now >= expiration); + + if lease_expired || retries_exceeded { self.reset(now); // Return a config now so that user code assigns the // 0.0.0.0/0 address, which will be used sending a DHCP @@ -292,12 +293,12 @@ impl Client { lease_duration: None, dns_servers: None, }; - let mut send_packet = |iface, endpoint, dhcp_repr| { send_packet(iface, raw_socket, &endpoint, &dhcp_repr, checksum_caps) .map(|()| None) }; + match self.state { ClientState::Discovering => { self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT); @@ -352,6 +353,7 @@ impl Client { net_trace!("DHCP reset"); self.state = ClientState::Discovering; self.next_egress = now; + self.lease_expiration = None; } } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index f7dfeca4f..231f97f19 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -877,7 +877,7 @@ mod test { 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; - static ACK_BYTES: &[u8] = &[ + static ACK_DNS_SERVER_BYTES: &[u8] = &[ 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -901,6 +901,11 @@ mod test { 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff ]; + static ACK_LEASE_TIME_BYTES: &[u8] = &[ + // TODO: Fill out. + 0x00, + ]; + const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]); const CLIENT_MAC: EthernetAddress = EthernetAddress([0x0, 0x0b, 0x82, 0x01, 0xfc, 0x42]); const DHCP_SIZE: u16 = 1500; @@ -1051,8 +1056,9 @@ mod test { #[test] fn test_parse_ack_dns_servers() { - let packet = Packet::new_unchecked(ACK_BYTES); + let packet = Packet::new_unchecked(ACK_DNS_SERVER_BYTES); let repr = Repr::parse(&packet).unwrap(); + // The packet described by ACK_BYTES advertises 4 DNS servers // Here we ensure that we correctly parse the first 3 into our fixed // length-3 array (see issue #305) @@ -1061,4 +1067,14 @@ mod test { Some(Ipv4Address([163, 1, 74, 7])), Some(Ipv4Address([163, 1, 74, 3]))])); } + + #[test] + fn test_parse_ack_lease_duration() { + let packet = Packet::new_unchecked(ACK_LEASE_TIME_BYTES); + let repr = Repr::parse(&packet).unwrap(); + + // Verify that the lease time in the ACK is properly parsed. The packet contains a lease + // duration of 600s. + assert_eq!(repr.lease_duration, Some(600)); + } } From 267084aedfd5979c7937f9c1d4a40b87764421d7 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 12 Mar 2021 17:36:43 +0100 Subject: [PATCH 090/566] Adding parsing test with lease time --- src/wire/dhcpv4.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 231f97f19..37f6d9404 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -902,8 +902,25 @@ mod test { ]; static ACK_LEASE_TIME_BYTES: &[u8] = &[ - // TODO: Fill out. - 0x00, + 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2, + 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, + 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01, + 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]); @@ -1074,7 +1091,7 @@ mod test { let repr = Repr::parse(&packet).unwrap(); // Verify that the lease time in the ACK is properly parsed. The packet contains a lease - // duration of 600s. - assert_eq!(repr.lease_duration, Some(600)); + // duration of 598s. + assert_eq!(repr.lease_duration, Some(598)); } } From e3954ebb8149341c52a4992e037d4b5109387910 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Fri, 12 Mar 2021 18:36:17 +0100 Subject: [PATCH 091/566] Adding changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0a0a58e..1f44148ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -No unreleased changes yet +### New features +* dhcp: Updated DHCP client to respect lease times provided by the server. ## [0.7.0] - 2021-01-20 From bb0c891cad5b29fc957e6f8a81b6707e7ee6fae4 Mon Sep 17 00:00:00 2001 From: Martijn Groeneveldt Date: Wed, 17 Mar 2021 16:13:18 +0100 Subject: [PATCH 092/566] Update managed to the latest version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4ea3a949c..bed522b67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ license = "0BSD" autoexamples = false [dependencies] -managed = { version = "0.7", default-features = false, features = ["map"] } +managed = { version = "0.8", default-features = false, features = ["map"] } byteorder = { version = "1.0", default-features = false } log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } From 921a96254b0a0e78c85be830b21e49efdafce916 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 18 Mar 2021 16:26:44 +0100 Subject: [PATCH 093/566] Removing lease expiration on reset - the lease does not expire --- src/dhcp/clientv4.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 5896f09e9..30efa8e53 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -353,7 +353,6 @@ impl Client { net_trace!("DHCP reset"); self.state = ClientState::Discovering; self.next_egress = now; - self.lease_expiration = None; } } From b94c3ea82d1e53b4fa570376eb91380ddc6751a0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Mar 2021 02:21:12 +0100 Subject: [PATCH 094/566] Remove support table from docs. Fixes #361 The table is no longer very informative since it's all "yes" now. --- src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cc0e6c224..8f28466cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,19 +64,6 @@ //! feature ever defined, to ensure that, when the representation layer is unable to make sense //! of a packet, it is still logged correctly and in full. //! -//! ## Packet and representation layer support -//! | Protocol | Packet | Representation | -//! |----------|--------|----------------| -//! | Ethernet | Yes | Yes | -//! | ARP | Yes | Yes | -//! | IPv4 | Yes | Yes | -//! | ICMPv4 | Yes | Yes | -//! | IGMPv1/2 | Yes | Yes | -//! | IPv6 | Yes | Yes | -//! | ICMPv6 | Yes | Yes | -//! | TCP | Yes | Yes | -//! | UDP | Yes | Yes | -//! //! [wire]: wire/index.html //! [osi]: https://en.wikipedia.org/wiki/OSI_model //! [berk]: https://en.wikipedia.org/wiki/Berkeley_sockets From bc0ffb629e97c3769602d980afa657958b6fae23 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Mar 2021 00:49:08 +0100 Subject: [PATCH 095/566] Fix feature-related compilation issues. --- Cargo.toml | 8 ++++---- src/lib.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bed522b67..125c4ad26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,12 +32,12 @@ url = "1.0" std = ["managed/std"] alloc = ["managed/alloc"] verbose = [] -ethernet = [] -"phy-raw_socket" = ["std", "libc"] -"phy-tap_interface" = ["std", "libc"] +ethernet = ["socket"] +"phy-raw_socket" = ["std", "libc", "ethernet"] +"phy-tap_interface" = ["std", "libc", "ethernet"] "proto-ipv4" = [] "proto-igmp" = ["proto-ipv4"] -"proto-dhcpv4" = ["proto-ipv4", "socket-raw"] +"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"] "proto-ipv6" = [] "socket" = [] "socket-raw" = ["socket"] diff --git a/src/lib.rs b/src/lib.rs index 8f28466cb..624b6b92f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,20 @@ compile_error!("at least one socket needs to be enabled"); */ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))] +compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6"); + +#[cfg(all( + feature = "socket", + not(any( + feature = "socket-raw", + feature = "socket-udp", + feature = "socket-tcp", + feature = "socket-icmp", + )) +))] +compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp"); + use core::fmt; #[macro_use] From 7bbde4f940afbd5cfb1facbc3f7819fcd26b3cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Mar 2021 02:58:51 +0100 Subject: [PATCH 096/566] Fix timeval in phy_wait for times greater than 1 second --- src/phy/sys/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 59f59cfdc..0730d1baa 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -47,7 +47,8 @@ pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 }; let timeout_ptr = if let Some(duration) = duration { - timeout.tv_usec = (duration.total_millis() * 1_000) as libc::suseconds_t; + timeout.tv_sec = duration.secs() as libc::time_t; + timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t; &mut timeout as *mut _ } else { ptr::null_mut() From 3dab2c48fceac1b711b9b840d8636348cef7af85 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 25 Mar 2021 00:06:15 +0100 Subject: [PATCH 097/566] Fix "leftover tokens" macro error --- src/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index f8cb381b6..0f1cc98ef 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,8 +25,8 @@ macro_rules! enum_with_unknown { pub enum $name:ident($ty:ty) { $( $( #[$variant_attr:meta] )* - $variant:ident = $value:expr $(,)* - ),+ + $variant:ident = $value:expr + ),+ $(,)? } ) => { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] From e4bf8bbdb6cb6a6e7dd621de46a41c4a865c0a43 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 25 Mar 2021 17:37:12 +0100 Subject: [PATCH 098/566] Add v0.7.1 changelog --- CHANGELOG.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f44148ef..02a023579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### New features -* dhcp: Updated DHCP client to respect lease times provided by the server. +- Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) + +## [0.7.1] - 2021-03-25 + +- ndisc: Fix NeighborSolicit incorrectly asking for src addr instead of dst addr ([419](https://github.com/smoltcp-rs/smoltcp/pull/419)) +- dhcpv4: respect lease time from the server instead of renewing every 60 seconds. ([437](https://github.com/smoltcp-rs/smoltcp/pull/437)) +- Fix build errors due to invalid combinations of features ([416](https://github.com/smoltcp-rs/smoltcp/pull/416), [447](https://github.com/smoltcp-rs/smoltcp/pull/447)) +- wire/ipv4: make some functions const ([420](https://github.com/smoltcp-rs/smoltcp/pull/420)) +- phy: fix BPF on OpenBSD ([421](https://github.com/smoltcp-rs/smoltcp/pull/421), [427](https://github.com/smoltcp-rs/smoltcp/pull/427)) +- phy: enable RawSocket, TapInterface on Android ([435](https://github.com/smoltcp-rs/smoltcp/pull/435)) +- phy: fix phy_wait for waits longer than 1 second ([449](https://github.com/smoltcp-rs/smoltcp/pull/449)) ## [0.7.0] - 2021-01-20 @@ -48,4 +57,5 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.7.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.6.0...v0.7.0 From 5510170d2edd2e26274241f33ec9d8f5ad83b29c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Mar 2021 15:02:11 +0100 Subject: [PATCH 099/566] Fix 0.7.1 release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a023579..8c0f62dc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) -## [0.7.1] - 2021-03-25 +## [0.7.1] - 2021-03-27 - ndisc: Fix NeighborSolicit incorrectly asking for src addr instead of dst addr ([419](https://github.com/smoltcp-rs/smoltcp/pull/419)) - dhcpv4: respect lease time from the server instead of renewing every 60 seconds. ([437](https://github.com/smoltcp-rs/smoltcp/pull/437)) From d352e151f6582fc55680cfcf068f5343ee46038f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 4 Jun 2020 03:00:49 +0200 Subject: [PATCH 100/566] Add support for IP mediums. - Add `medium` in `DeviceCapabilities`. - Rename EthernetInterface to Interface. - Add support to Interface for both Ethernet and IP mediums. The medium to use is detected from `device.capabilities().medium`. - Ethernet-only features are gated behind the "ethernet" feature, as before. - IP features are always enabled for now. --- examples/benchmark.rs | 4 +- examples/client.rs | 4 +- examples/dhcp_client.rs | 4 +- examples/httpclient.rs | 4 +- examples/loopback.rs | 8 +- examples/multicast.rs | 4 +- examples/ping.rs | 4 +- examples/server.rs | 4 +- fuzz/fuzz_targets/tcp_headers.rs | 8 +- src/dhcp/clientv4.rs | 2 +- src/iface/{ethernet.rs => interface.rs} | 673 +++++++++++++----------- src/iface/mod.rs | 8 +- src/phy/loopback.rs | 7 +- src/phy/mod.rs | 42 +- src/phy/raw_socket.rs | 3 +- src/phy/tap_interface.rs | 3 +- 16 files changed, 439 insertions(+), 343 deletions(-) rename src/iface/{ethernet.rs => interface.rs} (85%) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index ce307db2f..9ce2f57bd 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -13,7 +13,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -90,7 +90,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/client.rs b/examples/client.rs index 66f5fb7ca..ecce1c7ff 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -7,7 +7,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -39,7 +39,7 @@ fn main() { let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 7fa009c0d..6e9a67096 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; use smoltcp::time::Instant; use smoltcp::dhcp::Dhcpv4Client; @@ -28,7 +28,7 @@ fn main() { let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; let mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 2ca884440..be523060e 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -8,7 +8,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -45,7 +45,7 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/loopback.rs b/examples/loopback.rs index c84f286c0..13d0479b8 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -9,9 +9,9 @@ mod utils; use core::str; use log::{info, debug, error}; -use smoltcp::phy::Loopback; +use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -65,7 +65,7 @@ mod mock { fn main() { let clock = mock::Clock::new(); - let device = Loopback::new(); + let device = Loopback::new(Medium::Ethernet); #[cfg(feature = "std")] let device = { @@ -83,7 +83,7 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(EthernetAddress::default()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/multicast.rs b/examples/multicast.rs index e637467c6..cffb73a47 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -7,7 +7,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address, Ipv4Packet, IgmpPacket, IgmpRepr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, RawSocket, RawSocketBuffer, RawPacketMetadata, UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; @@ -37,7 +37,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) diff --git a/examples/ping.rs b/examples/ping.rs index 1d290d4f6..69f3de4ad 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -14,7 +14,7 @@ use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address, Icmpv6Repr, Icmpv6Packet, Ipv4Address, Icmpv4Repr, Icmpv4Packet}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; macro_rules! send_icmp_ping { @@ -98,7 +98,7 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .ip_addrs(ip_addrs) .routes(routes) diff --git a/examples/server.rs b/examples/server.rs index 95675c7e3..832f97572 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -8,7 +8,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; @@ -54,7 +54,7 @@ fn main() { IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64) ]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index fec277294..aec7a2523 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -6,10 +6,10 @@ use std as core; extern crate getopts; use core::cmp; -use smoltcp::phy::Loopback; +use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -118,7 +118,7 @@ fuzz_target!(|data: &[u8]| { utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_middleware_options(&mut matches, Loopback::new(), + let device = utils::parse_middleware_options(&mut matches, Loopback::new(Medium::Ethernet), /*loopback=*/true); smoltcp::phy::FuzzInjector::new(device, @@ -130,7 +130,7 @@ fuzz_target!(|data: &[u8]| { let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(EthernetAddress::default()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 30efa8e53..e5029dfe9 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -6,7 +6,7 @@ use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, use crate::wire::dhcpv4::field as dhcpv4_field; use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; use crate::phy::{Device, ChecksumCapabilities}; -use crate::iface::EthernetInterface as Interface; +use crate::iface::Interface; use crate::time::{Instant, Duration}; use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT}; diff --git a/src/iface/ethernet.rs b/src/iface/interface.rs similarity index 85% rename from src/iface/ethernet.rs rename to src/iface/interface.rs index bb60bfb50..5fd518f7e 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/interface.rs @@ -6,51 +6,15 @@ use core::cmp; use managed::{ManagedSlice, ManagedMap}; use crate::{Error, Result}; -use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken}; +use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken, Medium}; use crate::time::{Duration, Instant}; -use crate::wire::pretty_print::PrettyPrinter; -use crate::wire::{EthernetAddress, EthernetProtocol, EthernetFrame}; -use crate::wire::{IpAddress, IpProtocol, IpRepr, IpCidr}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, IPV6_MIN_MTU}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{ArpPacket, ArpRepr, ArpOperation}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable}; -#[cfg(feature = "proto-igmp")] -use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use crate::wire::IcmpRepr; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6HopByHopHeader, Ipv6HopByHopRepr}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6OptionRepr, Ipv6OptionFailureType}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{NdiscNeighborFlags, NdiscRepr}; -#[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))] -use crate::wire::Icmpv6DstUnreachable; -#[cfg(feature = "socket-udp")] -use crate::wire::{UdpPacket, UdpRepr}; -#[cfg(feature = "socket-tcp")] -use crate::wire::{TcpPacket, TcpRepr, TcpControl}; - -use crate::socket::{Socket, SocketSet, AnySocket, PollAt}; -#[cfg(feature = "socket-raw")] -use crate::socket::RawSocket; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use crate::socket::IcmpSocket; -#[cfg(feature = "socket-udp")] -use crate::socket::UdpSocket; -#[cfg(feature = "socket-tcp")] -use crate::socket::TcpSocket; +use crate::wire::*; +use crate::socket::*; +#[cfg(feature = "ethernet")] use crate::iface::{NeighborCache, NeighborAnswer}; use crate::iface::Routes; -/// An Ethernet network interface. +/// A network interface. /// /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be @@ -68,8 +32,10 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. struct InterfaceInner<'a> { - neighbor_cache: NeighborCache<'a>, - ethernet_addr: EthernetAddress, + #[cfg(feature = "ethernet")] + neighbor_cache: Option>, + #[cfg(feature = "ethernet")] + ethernet_addr: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -82,11 +48,12 @@ struct InterfaceInner<'a> { device_capabilities: DeviceCapabilities, } -/// A builder structure used for creating a Ethernet network -/// interface. +/// A builder structure used for creating a network interface. pub struct InterfaceBuilder <'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, + #[cfg(feature = "ethernet")] ethernet_addr: Option, + #[cfg(feature = "ethernet")] neighbor_cache: Option>, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] @@ -101,33 +68,36 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Create a builder used for creating a network interface using the /// given device and address. - /// - /// # Examples - /// - /// ``` - /// # use std::collections::BTreeMap; - /// use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache}; - /// # use smoltcp::phy::Loopback; - /// use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; - /// - /// let device = // ... - /// # Loopback::new(); - /// let hw_addr = // ... - /// # EthernetAddress::default(); - /// let neighbor_cache = // ... - /// # NeighborCache::new(BTreeMap::new()); - /// let ip_addrs = // ... - /// # []; - /// let iface = EthernetInterfaceBuilder::new(device) - /// .ethernet_addr(hw_addr) - /// .neighbor_cache(neighbor_cache) - /// .ip_addrs(ip_addrs) - /// .finalize(); - /// ``` + #[cfg_attr(feature = "ethernet", doc = r##" +# Examples + +``` +# use std::collections::BTreeMap; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +# use smoltcp::phy::{Loopback, Medium}; +use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; + +let device = // ... +# Loopback::new(Medium::Ethernet); +let hw_addr = // ... +# EthernetAddress::default(); +let neighbor_cache = // ... +# NeighborCache::new(BTreeMap::new()); +let ip_addrs = // ... +# []; +let iface = InterfaceBuilder::new(device) + .ethernet_addr(hw_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs(ip_addrs) + .finalize(); +``` + "##)] pub fn new(device: DeviceT) -> Self { InterfaceBuilder { device: device, + #[cfg(feature = "ethernet")] ethernet_addr: None, + #[cfg(feature = "ethernet")] neighbor_cache: None, ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] @@ -144,7 +114,8 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// # Panics /// This function panics if the address is not unicast. /// - /// [ethernet_addr]: struct.EthernetInterface.html#method.ethernet_addr + /// [ethernet_addr]: struct.Interface.html#method.ethernet_addr + #[cfg(feature = "ethernet")] pub fn ethernet_addr(mut self, addr: EthernetAddress) -> Self { InterfaceInner::check_ethernet_addr(&addr); self.ethernet_addr = Some(addr); @@ -157,7 +128,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// # Panics /// This function panics if any of the addresses are not unicast. /// - /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs + /// [ip_addrs]: struct.Interface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self where T: Into> { @@ -178,8 +149,8 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// This option is not available or required for IPv6 as packets sent to /// the interface are not filtered by IPv6 address. /// - /// [routes]: struct.EthernetInterface.html#method.routes - /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs + /// [routes]: struct.Interface.html#method.routes + /// [ip_addrs]: struct.Interface.html#method.ip_addrs #[cfg(feature = "proto-ipv4")] pub fn any_ip(mut self, enabled: bool) -> Self { self.any_ip = enabled; @@ -189,7 +160,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// Set the IP routes the interface will use. See also /// [routes]. /// - /// [routes]: struct.EthernetInterface.html#method.routes + /// [routes]: struct.Interface.html#method.routes pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a, DeviceT> where T: Into> { @@ -206,7 +177,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// storage, i.e. providing a non-empty storage to `ipv4_multicast_groups()`. /// Note that this way initial membership reports are **not** sent. /// - /// [`join_multicast_group()`]: struct.EthernetInterface.html#method.join_multicast_group + /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group #[cfg(feature = "proto-igmp")] pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self where T: Into> @@ -216,6 +187,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> } /// Set the Neighbor Cache the interface will use. + #[cfg(feature = "ethernet")] pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { self.neighbor_cache = Some(neighbor_cache); self @@ -233,38 +205,55 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache pub fn finalize(self) -> Interface<'a, DeviceT> { - match (self.ethernet_addr, self.neighbor_cache) { - (Some(ethernet_addr), Some(neighbor_cache)) => { - let device_capabilities = self.device.capabilities(); - - Interface { - device: self.device, - inner: InterfaceInner { - ethernet_addr, device_capabilities, neighbor_cache, - ip_addrs: self.ip_addrs, - #[cfg(feature = "proto-ipv4")] - any_ip: self.any_ip, - routes: self.routes, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: self.ipv4_multicast_groups, - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState::Inactive, - } - } - }, - _ => panic!("a required option was not set"), + let device_capabilities = self.device.capabilities(); + + #[cfg(feature = "ethernet")] + let mut ethernet_addr = None; + #[cfg(feature = "ethernet")] + let mut neighbor_cache = None; + match device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + ethernet_addr = Some(self.ethernet_addr.expect("ethernet_addr required option was not set")); + neighbor_cache = Some(self.neighbor_cache.expect("neighbor_cache required option was not set")); + } + Medium::Ip => { + #[cfg(feature = "ethernet")] + assert!(self.ethernet_addr.is_none(), "ethernet_addr is set, but device medium is IP"); + #[cfg(feature = "ethernet")] + assert!(self.neighbor_cache.is_none(), "neighbor_cache is set, but device medium is IP"); + } + } + + Interface { + device: self.device, + inner: InterfaceInner { + #[cfg(feature = "ethernet")] + ethernet_addr, + ip_addrs: self.ip_addrs, + #[cfg(feature = "proto-ipv4")] + any_ip: self.any_ip, + routes: self.routes, + device_capabilities, + #[cfg(feature = "ethernet")] + neighbor_cache, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: self.ipv4_multicast_groups, + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + } } } } #[derive(Debug, PartialEq)] +#[cfg(feature = "ethernet")] enum EthernetPacket<'a> { #[cfg(feature = "proto-ipv4")] Arp(ArpRepr), Ip(IpPacket<'a>), } - #[derive(Debug, PartialEq)] pub(crate) enum IpPacket<'a> { #[cfg(feature = "proto-ipv4")] @@ -378,17 +367,25 @@ enum IgmpReportState { impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Get the Ethernet address of the interface. + /// + /// # Panics + /// This function panics if if the interface's medium is not Ethernet. + + #[cfg(feature = "ethernet")] pub fn ethernet_addr(&self) -> EthernetAddress { - self.inner.ethernet_addr + self.inner.ethernet_addr.unwrap() } /// Set the Ethernet address of the interface. /// /// # Panics - /// This function panics if the address is not unicast. + /// This function panics if the address is not unicast, or if the + /// interface's medium is not Ethernet. + #[cfg(feature = "ethernet")] pub fn set_ethernet_addr(&mut self, addr: EthernetAddress) { - self.inner.ethernet_addr = addr; - InterfaceInner::check_ethernet_addr(&self.inner.ethernet_addr); + assert!(self.device.capabilities().medium == Medium::Ethernet); + InterfaceInner::check_ethernet_addr(&addr); + self.inner.ethernet_addr = Some(addr); } /// Get a reference to the inner device. @@ -424,7 +421,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, _timestamp, pkt)?; Ok(true) } else { Ok(false) @@ -450,7 +447,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, _timestamp, pkt)?; Ok(true) } else { Ok(false) @@ -593,23 +590,47 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Some(tokens) => tokens, }; rx_token.consume(timestamp, |frame| { - inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { - net_debug!("cannot process ingress packet: {}", err); - net_debug!("packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame)); - err - }).and_then(|response| { - processed_any = true; - match response { - Some(packet) => { - inner.dispatch(tx_token, timestamp, packet).map_err(|err| { - net_debug!("cannot dispatch response packet: {}", err); - err - }) - } - None => Ok(()) + match inner.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { + net_debug!("cannot process ingress packet: {}", err); + net_debug!("packet dump follows:\n{}", + PrettyPrinter::>::new("", &frame)); + err + }).and_then(|response| { + processed_any = true; + match response { + Some(packet) => { + inner.dispatch(tx_token, timestamp, packet).map_err(|err| { + net_debug!("cannot dispatch response packet: {}", err); + err + }) + } + None => Ok(()) + } + }) } - }) + Medium::Ip => { + inner.process_ip(sockets, timestamp, &frame).map_err(|err| { + net_debug!("cannot process ingress packet: {}", err); + //net_debug!("packet dump follows:\n{}", + // PrettyPrinter::>::new("", &frame)); + err + }).and_then(|response| { + processed_any = true; + match response { + Some(packet) => { + inner.dispatch_ip(tx_token, timestamp, packet).map_err(|err| { + net_debug!("cannot dispatch response packet: {}", err); + err + }) + } + None => Ok(()) + } + }) + } + } })?; } Ok(processed_any) @@ -617,7 +638,12 @@ impl<'a, DeviceT> Interface<'a, DeviceT> fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { let mut caps = self.device.capabilities(); - caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len(); + match caps.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => + caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len(), + _ => {} + } let mut emitted_any = false; for mut socket in sockets.iter_mut() { @@ -634,9 +660,8 @@ impl<'a, DeviceT> Interface<'a, DeviceT> ($response:expr) => ({ let response = $response; neighbor_addr = Some(response.ip_repr().dst_addr()); - let response = EthernetPacket::Ip(response); let tx_token = device.transmit().ok_or(Error::Exhausted)?; - device_result = inner.dispatch(tx_token, timestamp, response); + device_result = inner.dispatch_ip(tx_token, timestamp, response); device_result }) } @@ -703,7 +728,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> 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(tx_token, timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } self.inner.igmp_report_state = IgmpReportState::Inactive; @@ -721,7 +746,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(tx_token, timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } let next_timeout = (timeout + interval).max(timestamp); @@ -743,6 +768,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } impl<'a> InterfaceInner<'a> { + #[cfg(feature = "ethernet")] fn check_ethernet_addr(addr: &EthernetAddress) { if addr.is_multicast() { panic!("Ethernet address {} is not unicast", addr) @@ -810,6 +836,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn process_ethernet<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) -> Result>> @@ -819,7 +846,7 @@ impl<'a> InterfaceInner<'a> { // Ignore any packets not directed to our hardware address or any of the multicast groups. if !eth_frame.dst_addr().is_broadcast() && !eth_frame.dst_addr().is_multicast() && - eth_frame.dst_addr() != self.ethernet_addr + eth_frame.dst_addr() != self.ethernet_addr.unwrap() { return Ok(None) } @@ -829,17 +856,58 @@ impl<'a> InterfaceInner<'a> { EthernetProtocol::Arp => self.process_arp(timestamp, ð_frame), #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Ipv4 => - self.process_ipv4(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), + EthernetProtocol::Ipv4 => { + let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; + if eth_frame.src_addr().is_unicast() && ipv4_packet.src_addr().is_unicast() { + // 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.process_ipv4(sockets, timestamp, &ipv4_packet).map(|o| o.map(EthernetPacket::Ip)) + } + #[cfg(feature = "proto-ipv6")] + EthernetProtocol::Ipv6 => { + let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; + if eth_frame.src_addr().is_unicast() && ipv6_packet.src_addr().is_unicast() { + // 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.process_ipv6(sockets, timestamp, &ipv6_packet).map(|o| o.map(EthernetPacket::Ip)) + } + // Drop all other traffic. + _ => Err(Error::Unrecognized), + } + } + + fn process_ip<'frame, T: AsRef<[u8]>> + (&mut self, sockets: &mut SocketSet, timestamp: Instant, ip_payload: &'frame T) -> + Result>> + { + 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) + } #[cfg(feature = "proto-ipv6")] - EthernetProtocol::Ipv6 => - self.process_ipv6(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), + Ok(IpVersion::Ipv6) => { + let ipv6_packet = Ipv6Packet::new_checked(ip_payload)?; + self.process_ipv6(sockets, timestamp, &ipv6_packet) + } // Drop all other traffic. _ => Err(Error::Unrecognized), } } - #[cfg(feature = "proto-ipv4")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn process_arp<'frame, T: AsRef<[u8]>> (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> Result>> @@ -855,7 +923,7 @@ impl<'a> InterfaceInner<'a> { operation, source_hardware_addr, source_protocol_addr, target_protocol_addr, .. } => { if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() { - self.neighbor_cache.fill(source_protocol_addr.into(), + self.neighbor_cache.as_mut().unwrap().fill(source_protocol_addr.into(), source_hardware_addr, timestamp); } else { @@ -867,7 +935,7 @@ impl<'a> InterfaceInner<'a> { if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, - source_hardware_addr: self.ethernet_addr, + source_hardware_addr: self.ethernet_addr.unwrap(), source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr @@ -902,12 +970,10 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv6")] - fn process_ipv6<'frame, T: AsRef<[u8]>> + fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized> (&mut self, sockets: &mut SocketSet, timestamp: Instant, - eth_frame: &EthernetFrame<&'frame T>) -> - Result>> - { - let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; + ipv6_packet: &Ipv6Packet<&'frame T>) -> + Result>> { let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; if !ipv6_repr.src_addr.is_unicast() { @@ -916,15 +982,6 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Malformed) } - if eth_frame.src_addr().is_unicast() { - // Fill the neighbor cache from IP header of unicast frames. - let ip_addr = IpAddress::Ipv6(ipv6_repr.src_addr); - if self.in_same_network(&ip_addr) && - !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp); - } - } - let ip_payload = ipv6_packet.payload(); #[cfg(feature = "socket-raw")] @@ -980,12 +1037,11 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv4")] - fn process_ipv4<'frame, T: AsRef<[u8]>> + fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized> (&mut self, sockets: &mut SocketSet, timestamp: Instant, - eth_frame: &EthernetFrame<&'frame T>) -> + ipv4_packet: &Ipv4Packet<&'frame T>) -> Result>> { - let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; let checksum_caps = self.device_capabilities.checksum.clone(); let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?; @@ -995,14 +1051,6 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Malformed) } - if eth_frame.src_addr().is_unicast() { - // Fill the neighbor cache from IP header of unicast frames. - let ip_addr = IpAddress::Ipv4(ipv4_repr.src_addr); - if self.in_same_network(&ip_addr) { - self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp); - } - } - let ip_repr = IpRepr::Ipv4(ipv4_repr); let ip_payload = ipv4_packet.payload(); @@ -1126,7 +1174,7 @@ 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, _sockets: &mut SocketSet, _timestamp: Instant, ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; @@ -1167,8 +1215,9 @@ impl<'a> InterfaceInner<'a> { Icmpv6Repr::EchoReply { .. } => Ok(None), // Forward any NDISC packets to the ndisc packet handler + #[cfg(feature = "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(_timestamp, ipv6_repr, repr), _ => Ok(None) }, @@ -1182,7 +1231,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "proto-ipv6")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>) -> Result>> { match repr { @@ -1191,8 +1240,8 @@ impl<'a> InterfaceInner<'a> { match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { if flags.contains(NdiscNeighborFlags::OVERRIDE) || - !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, lladdr, timestamp) + !self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, timestamp).found() { + self.neighbor_cache.as_mut().unwrap().fill(ip_addr, lladdr, timestamp) } }, _ => (), @@ -1202,7 +1251,7 @@ impl<'a> InterfaceInner<'a> { NdiscRepr::NeighborSolicit { target_addr, lladdr, .. } => { match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - self.neighbor_cache.fill(ip_repr.src_addr.into(), lladdr, timestamp) + self.neighbor_cache.as_mut().unwrap().fill(ip_repr.src_addr.into(), lladdr, timestamp) }, _ => (), } @@ -1210,7 +1259,7 @@ impl<'a> InterfaceInner<'a> { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr: target_addr, - lladdr: Some(self.ethernet_addr) + lladdr: Some(self.ethernet_addr.unwrap()) }); let ip_repr = Ipv6Repr { src_addr: target_addr, @@ -1454,6 +1503,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, packet: EthernetPacket) -> Result<()> where Tx: TxToken @@ -1480,6 +1530,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn dispatch_ethernet(&mut self, tx_token: Tx, timestamp: Instant, buffer_len: usize, f: F) -> Result<()> where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>) @@ -1488,7 +1539,7 @@ impl<'a> InterfaceInner<'a> { tx_token.consume(timestamp, 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); + frame.set_src_addr(self.ethernet_addr.unwrap()); f(frame); @@ -1517,15 +1568,20 @@ impl<'a> InterfaceInner<'a> { fn has_neighbor(&self, addr: &IpAddress, timestamp: Instant) -> bool { match self.route(addr, timestamp) { - Ok(routed_addr) => { - self.neighbor_cache - .lookup(&routed_addr, timestamp) - .found() + Ok(_routed_addr) => { + match self.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => self.neighbor_cache.as_ref().unwrap() + .lookup(&_routed_addr, timestamp) + .found(), + Medium::Ip => true, + } } Err(_) => false } } + #[cfg(feature = "ethernet")] fn lookup_hardware_addr(&mut self, tx_token: Tx, timestamp: Instant, src_addr: &IpAddress, dst_addr: &IpAddress) -> Result<(EthernetAddress, Tx)> @@ -1559,7 +1615,7 @@ impl<'a> InterfaceInner<'a> { let dst_addr = self.route(dst_addr, timestamp)?; - match self.neighbor_cache.lookup(&dst_addr, timestamp) { + match self.neighbor_cache.as_mut().unwrap().lookup(&dst_addr, timestamp) { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), NeighborAnswer::RateLimited => @@ -1575,7 +1631,7 @@ impl<'a> InterfaceInner<'a> { let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, - source_hardware_addr: self.ethernet_addr, + source_hardware_addr: self.ethernet_addr.unwrap(), source_protocol_addr: src_addr, target_hardware_addr: EthernetAddress::BROADCAST, target_protocol_addr: dst_addr, @@ -1596,7 +1652,7 @@ impl<'a> InterfaceInner<'a> { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, - lladdr: Some(self.ethernet_addr), + lladdr: Some(self.ethernet_addr.unwrap()), }); let packet = IpPacket::Icmpv6(( @@ -1616,7 +1672,7 @@ impl<'a> InterfaceInner<'a> { _ => () } // The request got dispatched, limit the rate on the cache. - self.neighbor_cache.limit_rate(timestamp); + self.neighbor_cache.as_mut().unwrap().limit_rate(timestamp); Err(Error::Unaddressable) } @@ -1625,25 +1681,43 @@ impl<'a> InterfaceInner<'a> { let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; let caps = self.device_capabilities.clone(); - let (dst_hardware_addr, tx_token) = - self.lookup_hardware_addr(tx_token, timestamp, - &ip_repr.src_addr(), &ip_repr.dst_addr())?; + match self.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + let (dst_hardware_addr, tx_token) = + self.lookup_hardware_addr(tx_token, timestamp, + &ip_repr.src_addr(), &ip_repr.dst_addr())?; - self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| { - frame.set_dst_addr(dst_hardware_addr); - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), - _ => return + self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| { + frame.set_dst_addr(dst_hardware_addr); + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), + _ => return + } + + ip_repr.emit(frame.payload_mut(), &caps.checksum); + + let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; + packet.emit_payload(ip_repr, payload, &caps); + }) } + Medium::Ip => { + let tx_len = ip_repr.total_len(); + tx_token.consume(timestamp, tx_len, |mut tx_buffer| { + debug_assert!(tx_buffer.as_ref().len() == tx_len); - ip_repr.emit(frame.payload_mut(), &caps.checksum); + ip_repr.emit(&mut tx_buffer, &caps.checksum); - let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &caps); - }) + let payload = &mut tx_buffer[ip_repr.buffer_len()..]; + packet.emit_payload(ip_repr, payload, &caps); + + Ok(()) + }) + } + } } #[cfg(feature = "proto-igmp")] @@ -1687,42 +1761,44 @@ mod test { use std::vec::Vec; use std::collections::BTreeMap; + use super::*; + use crate::{Result, Error}; - use super::InterfaceBuilder; - use crate::iface::{NeighborCache, EthernetInterface}; - use crate::phy::{self, Loopback, ChecksumCapabilities}; + #[cfg(feature = "medium-ethernet")] + use crate::iface::NeighborCache; + use crate::iface::Interface; #[cfg(feature = "proto-igmp")] - use crate::phy::{Device, RxToken, TxToken}; use crate::time::Instant; use crate::socket::SocketSet; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{ArpOperation, ArpPacket, ArpRepr}; - use crate::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; - use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{Ipv4Address, Ipv4Repr, Ipv4Cidr}; - #[cfg(feature = "proto-igmp")] - use crate::wire::Ipv4Packet; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{Icmpv4Repr, Icmpv4DstUnreachable}; - #[cfg(feature = "proto-igmp")] - use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; - #[cfg(all(feature = "socket-udp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - use crate::wire::{UdpPacket, UdpRepr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Ipv6Address, Ipv6Repr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{NdiscNeighborFlags, NdiscRepr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr}; + use crate::phy::{Loopback, ChecksumCapabilities}; + + fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + // Create a basic device + let device = Loopback::new(Medium::Ip); + let ip_addrs = [ + #[cfg(feature = "proto-ipv4")] + IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + + let iface_builder = InterfaceBuilder::new(device) + .ip_addrs(ip_addrs); + #[cfg(feature = "proto-igmp")] + let iface_builder = iface_builder + .ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder + .finalize(); - use super::{EthernetPacket, IpPacket}; + (iface, SocketSet::new(vec![])) + } - fn create_loopback<'a>() -> (EthernetInterface<'a, Loopback>, SocketSet<'a>) { + #[cfg(all(feature = "ethernet"))] + fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device - let device = Loopback::new(); + let device = Loopback::new(Medium::Ethernet); let ip_addrs = [ #[cfg(feature = "proto-ipv4")] IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), @@ -1746,7 +1822,7 @@ mod test { } #[cfg(feature = "proto-igmp")] - fn recv_all(iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec> { + fn recv_all(iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); while let Some((rx, _tx)) = iface.device.receive() { rx.consume(timestamp, |pkt| { @@ -1760,7 +1836,7 @@ mod test { #[derive(Debug, PartialEq)] struct MockTxToken; - impl phy::TxToken for MockTxToken { + impl TxToken for MockTxToken { fn consume(self, _: Instant, _: usize, _: F) -> Result where F: FnOnce(&mut [u8]) -> Result { Err(Error::Unaddressable) @@ -1768,23 +1844,22 @@ mod test { } #[test] - #[should_panic(expected = "a required option was not set")] + #[should_panic(expected = "ethernet_addr required option was not set")] + #[cfg(all(feature = "ethernet"))] fn test_builder_initialization_panic() { - InterfaceBuilder::new(Loopback::new()).finalize(); + InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); } #[test] - fn test_no_icmp_no_unicast() { + #[cfg(feature = "proto-ipv4")] + fn test_no_icmp_no_unicast_ipv4() { let (mut iface, mut socket_set) = create_loopback(); - let mut eth_bytes = vec![0u8; 54]; - // Unknown Ipv4 Protocol // // Because the destination is the broadcast address // this should not trigger and Destination Unreachable // response. See RFC 1122 § 3.2.2. - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address::BROADCAST, @@ -1792,7 +1867,28 @@ mod test { payload_len: 0, hop_limit: 0x40 }); - #[cfg(feature = "proto-ipv6")] + + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // 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), + Ok(None)); + } + + #[test] + #[cfg(feature = "proto-ipv6")] + fn test_no_icmp_no_unicast_ipv6() { + let (mut iface, mut socket_set) = create_loopback(); + + // Unknown Ipv6 Protocol + // + // Because the destination is the broadcast address + // this should not trigger and Destination Unreachable + // response. See RFC 1122 § 3.2.2. let repr = IpRepr::Ipv6(Ipv6Repr { src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, @@ -1801,22 +1897,13 @@ mod test { hop_limit: 0x40 }); - let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_ethertype(EthernetProtocol::Ipv4); - repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) - }; + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv6Packet::new_unchecked(&bytes); // Ensure that the unknown protocol frame does not trigger an // ICMP error response when the destination address is a // broadcast address - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(None)); - #[cfg(feature = "proto-ipv6")] assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame), Ok(None)); } @@ -1827,8 +1914,6 @@ mod test { static NO_BYTES: [u8; 0] = []; let (mut iface, mut socket_set) = create_loopback(); - let mut eth_bytes = vec![0u8; 34]; - // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), @@ -1838,15 +1923,9 @@ mod test { hop_limit: 0x40 }); - // emit the above repr to a frame - let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_ethertype(EthernetProtocol::Ipv4); - repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) - }; + let mut bytes = vec![0u8; 34]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); // The expected Destination Unreachable response due to the // unknown protocol @@ -2094,21 +2173,19 @@ mod test { payload_len: icmpv4_repr.buffer_len(), }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to ip frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); icmpv4_repr.emit( &mut Icmpv4Packet::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; // Expected ICMPv4 echo reply @@ -2223,9 +2300,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv4")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -2268,9 +2345,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv6")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 86]; @@ -2328,9 +2405,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv4")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -2452,9 +2529,7 @@ mod test { let (mut iface, mut socket_set) = create_loopback(); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x01]); - let mut eth_bytes = vec![0; 66]; let payload = [0x12, 0x34, 0x56, 0x78]; let ipv6_repr = Ipv6Repr { @@ -2465,17 +2540,14 @@ mod test { hop_limit: 0x40, }; + let mut bytes = vec![0; 52]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); let ip_repr = IpRepr::Ipv6(ipv6_repr); - frame.set_dst_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Ipv6); - ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); + ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); let mut offset = ipv6_repr.buffer_len(); { let mut hbh_pkt = - Ipv6HopByHopHeader::new_unchecked(&mut frame.payload_mut()[offset..]); + Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); hbh_pkt.set_header_len(0); offset += 8; @@ -2488,8 +2560,8 @@ mod test { Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); } } - frame.payload_mut()[offset..].copy_from_slice(&payload); - EthernetFrame::new_unchecked(&*frame.into_inner()) + bytes[offset..].copy_from_slice(&payload); + Ipv6Packet::new_unchecked(&bytes) }; let reply_icmp_repr = Icmpv6Repr::ParamProblem { @@ -2511,24 +2583,17 @@ mod test { // error message to be sent to the sender. assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame), Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); - - // Ensure the address of the requestor was entered in the cache - assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0), - &IpAddress::Ipv6(Ipv6Address::LOOPBACK), - &IpAddress::Ipv6(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); } #[test] #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { - fn recv_igmp(mut iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + fn recv_igmp(mut iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { let checksum_caps = &iface.device.capabilities().checksum; recv_all(&mut iface, timestamp) .iter() .filter_map(|frame| { - let eth_frame = EthernetFrame::new_checked(frame).ok()?; - let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload()).ok()?; + let ipv4_packet = Ipv4Packet::new_checked(frame).ok()?; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; @@ -2566,14 +2631,12 @@ mod test { // General query let timestamp = Instant::now(); const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x0a, 0x14, - 0x48, 0x01, 0x21, 0x01, 0x08, 0x00, 0x46, 0xc0, - 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, - 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, 0xe0, 0x00, - 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, - 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, + 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, + 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, + 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]; { // Transmit GENERAL_QUERY_BYTES into loopback @@ -2640,23 +2703,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), @@ -2696,23 +2757,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; let frame = iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame); @@ -2772,23 +2831,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), diff --git a/src/iface/mod.rs b/src/iface/mod.rs index d38593f70..a21bc2536 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -7,8 +7,7 @@ provides lookup and caching of hardware addresses, and handles management packet #[cfg(feature = "ethernet")] mod neighbor; mod route; -#[cfg(feature = "ethernet")] -mod ethernet; +mod interface; #[cfg(feature = "ethernet")] pub use self::neighbor::Neighbor as Neighbor; @@ -17,6 +16,5 @@ pub(crate) use self::neighbor::Answer as NeighborAnswer; #[cfg(feature = "ethernet")] pub use self::neighbor::Cache as NeighborCache; pub use self::route::{Route, Routes}; -#[cfg(feature = "ethernet")] -pub use self::ethernet::{Interface as EthernetInterface, - InterfaceBuilder as EthernetInterfaceBuilder}; + +pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index b41535a62..beeba9f6b 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -10,13 +10,14 @@ use alloc::collections::VecDeque; use alloc::VecDeque; use crate::Result; -use crate::phy::{self, Device, DeviceCapabilities}; +use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; /// A loopback device. #[derive(Debug)] pub struct Loopback { queue: VecDeque>, + medium: Medium, } #[allow(clippy::new_without_default)] @@ -25,9 +26,10 @@ impl Loopback { /// /// Every packet transmitted through this device will be received through it /// in FIFO order. - pub fn new() -> Loopback { + pub fn new(medium: Medium) -> Loopback { Loopback { queue: VecDeque::new(), + medium, } } } @@ -39,6 +41,7 @@ impl<'a> Device<'a> for Loopback { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: 65535, + medium: self.medium, ..DeviceCapabilities::default() } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index e2962fdaa..99947afa4 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -10,7 +10,8 @@ and implementations of it: * _adapters_ [RawSocket](struct.RawSocket.html) and [TapInterface](struct.TapInterface.html), to transmit and receive frames on the host OS. - +*/ +#![cfg_attr(feature = "ethernet", doc = r##" # Examples An implementation of the [Device](trait.Device.html) trait for a simple hardware @@ -18,7 +19,7 @@ Ethernet controller could look as follows: ```rust use smoltcp::Result; -use smoltcp::phy::{self, DeviceCapabilities, Device}; +use smoltcp::phy::{self, DeviceCapabilities, Device, Medium}; use smoltcp::time::Instant; struct StmPhy { @@ -52,6 +53,7 @@ impl<'a> phy::Device<'a> for StmPhy { let mut caps = DeviceCapabilities::default(); caps.max_transmission_unit = 1536; caps.max_burst_size = Some(1); + caps.medium = Medium::Ethernet; caps } } @@ -82,7 +84,7 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { } } ``` -*/ +"##)] use crate::Result; use crate::time::Instant; @@ -192,6 +194,13 @@ impl ChecksumCapabilities { #[derive(Debug, Clone, Default)] #[non_exhaustive] pub struct DeviceCapabilities { + /// Medium of the device. + /// + /// This indicates what kind of packet the sent/received bytes are, and determines + /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done + /// for Ethernet mediums. + pub medium: Medium, + /// Maximum transmission unit. /// /// The network device is unable to send or receive frames larger than the value returned @@ -222,6 +231,33 @@ pub struct DeviceCapabilities { pub checksum: ChecksumCapabilities, } +/// Type of medium of a device. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum Medium { + /// Ethernet medium. Devices of this type send and receive Ethernet frames, + /// and interfaces using it must do neighbor discovery via ARP or NDISC. + /// + /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. + #[cfg(feature = "ethernet")] + Ethernet, + + /// IP medium. Devices of this type send and receive IP frames, without an + /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. + /// + /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. + Ip, +} + + +impl Default for Medium { + fn default() -> Medium { + #[cfg(feature = "ethernet")] + return Medium::Ethernet; + #[cfg(not(feature = "ethernet"))] + return Medium::Ip; + } +} + /// An interface for sending and receiving raw network frames. /// /// The interface is based on _tokens_, which are types that allow to receive/transmit a diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 3764551cf..b4d4e68fa 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -5,7 +5,7 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A socket that captures or transmits the complete frame. @@ -44,6 +44,7 @@ impl<'a> Device<'a> for RawSocket { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, + medium: Medium::Ethernet, ..DeviceCapabilities::default() } } diff --git a/src/phy/tap_interface.rs b/src/phy/tap_interface.rs index 95202ab9e..018c2c09d 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tap_interface.rs @@ -5,7 +5,7 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A virtual Ethernet interface. @@ -45,6 +45,7 @@ impl<'a> Device<'a> for TapInterface { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, + medium: Medium::Ethernet, ..DeviceCapabilities::default() } } From 4d8729088dc201003c8b6c1d52ca3689991fbc88 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Dec 2020 23:52:37 +0100 Subject: [PATCH 101/566] Add medium-ip, medium-ethernet feature flags. --- .github/workflows/test.yml | 24 ++++---- Cargo.toml | 5 +- src/iface/interface.rs | 123 +++++++++++++++++++++---------------- src/iface/mod.rs | 10 +-- src/lib.rs | 2 +- src/parsers.rs | 10 +-- src/phy/mod.rs | 13 ++-- src/wire/icmpv6.rs | 10 +-- src/wire/mod.rs | 16 ++--- 9 files changed, 119 insertions(+), 94 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80b7550cb..adf2bd136 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,23 +26,23 @@ jobs: - std proto-ipv4 # Test features chosen to be as orthogonal as possible. - - std ethernet phy-raw_socket proto-ipv6 socket-udp - - std ethernet phy-tap_interface proto-ipv6 socket-udp - - std ethernet proto-ipv4 proto-igmp socket-raw - - std ethernet proto-ipv4 socket-udp socket-tcp - - std ethernet proto-ipv4 proto-dhcpv4 socket-udp - - std ethernet proto-ipv6 socket-udp - - std ethernet proto-ipv6 socket-tcp - - std ethernet proto-ipv4 socket-icmp socket-tcp - - std ethernet proto-ipv6 socket-icmp socket-tcp + - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp + - std medium-ethernet phy-tap_interface proto-ipv6 socket-udp + - std medium-ethernet proto-ipv4 proto-igmp socket-raw + - std medium-ethernet proto-ipv4 socket-udp socket-tcp + - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp + - std medium-ethernet proto-ipv6 socket-udp + - std medium-ethernet proto-ipv6 socket-tcp + - std medium-ethernet proto-ipv4 socket-icmp socket-tcp + - std medium-ethernet proto-ipv6 socket-icmp socket-tcp # Test features chosen to be as aggressive as possible. - - std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async + - std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async include: # Test alloc feature which requires nightly. - rust: nightly - features: alloc ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + 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: @@ -69,7 +69,7 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 125c4ad26..a5b98338b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ url = "1.0" std = ["managed/std"] alloc = ["managed/alloc"] verbose = [] -ethernet = ["socket"] +"medium-ethernet" = ["socket"] +"medium-ip" = ["socket"] "phy-raw_socket" = ["std", "libc", "ethernet"] "phy-tap_interface" = ["std", "libc", "ethernet"] "proto-ipv4" = [] @@ -47,7 +48,7 @@ ethernet = ["socket"] "async" = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ - "ethernet", + "medium-ethernet", "medium-ip", "phy-raw_socket", "phy-tap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 5fd518f7e..2f43c1e49 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -10,7 +10,7 @@ use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken, Medium}; use crate::time::{Duration, Instant}; use crate::wire::*; use crate::socket::*; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] use crate::iface::{NeighborCache, NeighborAnswer}; use crate::iface::Routes; @@ -32,9 +32,9 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. struct InterfaceInner<'a> { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] neighbor_cache: Option>, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] ethernet_addr: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] @@ -51,9 +51,9 @@ struct InterfaceInner<'a> { /// A builder structure used for creating a network interface. pub struct InterfaceBuilder <'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] ethernet_addr: Option, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] neighbor_cache: Option>, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] @@ -68,7 +68,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Create a builder used for creating a network interface using the /// given device and address. - #[cfg_attr(feature = "ethernet", doc = r##" + #[cfg_attr(feature = "medium-ethernet", doc = r##" # Examples ``` @@ -95,9 +95,9 @@ let iface = InterfaceBuilder::new(device) pub fn new(device: DeviceT) -> Self { InterfaceBuilder { device: device, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] ethernet_addr: None, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] neighbor_cache: None, ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] @@ -115,7 +115,7 @@ let iface = InterfaceBuilder::new(device) /// This function panics if the address is not unicast. /// /// [ethernet_addr]: struct.Interface.html#method.ethernet_addr - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] pub fn ethernet_addr(mut self, addr: EthernetAddress) -> Self { InterfaceInner::check_ethernet_addr(&addr); self.ethernet_addr = Some(addr); @@ -187,7 +187,7 @@ let iface = InterfaceBuilder::new(device) } /// Set the Neighbor Cache the interface will use. - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { self.neighbor_cache = Some(neighbor_cache); self @@ -207,35 +207,31 @@ let iface = InterfaceBuilder::new(device) pub fn finalize(self) -> Interface<'a, DeviceT> { let device_capabilities = self.device.capabilities(); - #[cfg(feature = "ethernet")] - let mut ethernet_addr = None; - #[cfg(feature = "ethernet")] - let mut neighbor_cache = None; - match device_capabilities.medium { - #[cfg(feature = "ethernet")] - Medium::Ethernet => { - ethernet_addr = Some(self.ethernet_addr.expect("ethernet_addr required option was not set")); - neighbor_cache = Some(self.neighbor_cache.expect("neighbor_cache required option was not set")); - } + #[cfg(feature = "medium-ethernet")] + let (ethernet_addr, neighbor_cache) = match device_capabilities.medium { + Medium::Ethernet => ( + Some(self.ethernet_addr.expect("ethernet_addr required option was not set")), + Some(self.neighbor_cache.expect("neighbor_cache required option was not set")) + ), + #[cfg(feature = "medium-ip")] Medium::Ip => { - #[cfg(feature = "ethernet")] assert!(self.ethernet_addr.is_none(), "ethernet_addr is set, but device medium is IP"); - #[cfg(feature = "ethernet")] assert!(self.neighbor_cache.is_none(), "neighbor_cache is set, but device medium is IP"); + (None, None) } - } + }; Interface { device: self.device, inner: InterfaceInner { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] ethernet_addr, ip_addrs: self.ip_addrs, #[cfg(feature = "proto-ipv4")] any_ip: self.any_ip, routes: self.routes, device_capabilities, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] neighbor_cache, #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: self.ipv4_multicast_groups, @@ -247,7 +243,7 @@ let iface = InterfaceBuilder::new(device) } #[derive(Debug, PartialEq)] -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] enum EthernetPacket<'a> { #[cfg(feature = "proto-ipv4")] Arp(ArpRepr), @@ -371,7 +367,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> /// # Panics /// This function panics if if the interface's medium is not Ethernet. - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] pub fn ethernet_addr(&self) -> EthernetAddress { self.inner.ethernet_addr.unwrap() } @@ -381,7 +377,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> /// # Panics /// This function panics if the address is not unicast, or if the /// interface's medium is not Ethernet. - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] pub fn set_ethernet_addr(&mut self, addr: EthernetAddress) { assert!(self.device.capabilities().medium == Medium::Ethernet); InterfaceInner::check_ethernet_addr(&addr); @@ -591,7 +587,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> }; rx_token.consume(timestamp, |frame| { match inner.device_capabilities.medium { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { net_debug!("cannot process ingress packet: {}", err); @@ -611,6 +607,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } }) } + #[cfg(feature = "medium-ip")] Medium::Ip => { inner.process_ip(sockets, timestamp, &frame).map_err(|err| { net_debug!("cannot process ingress packet: {}", err); @@ -638,12 +635,12 @@ impl<'a, DeviceT> Interface<'a, DeviceT> fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { let mut caps = self.device.capabilities(); - match caps.medium { - #[cfg(feature = "ethernet")] - Medium::Ethernet => - caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len(), - _ => {} - } + caps.max_transmission_unit = 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 mut emitted_any = false; for mut socket in sockets.iter_mut() { @@ -768,7 +765,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } impl<'a> InterfaceInner<'a> { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn check_ethernet_addr(addr: &EthernetAddress) { if addr.is_multicast() { panic!("Ethernet address {} is not unicast", addr) @@ -836,7 +833,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn process_ethernet<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) -> Result>> @@ -887,6 +884,7 @@ 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) -> Result>> @@ -907,7 +905,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn process_arp<'frame, T: AsRef<[u8]>> (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> Result>> @@ -1215,7 +1213,7 @@ impl<'a> InterfaceInner<'a> { Icmpv6Repr::EchoReply { .. } => Ok(None), // Forward any NDISC packets to the ndisc packet handler - #[cfg(feature = "ethernet")] + #[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), _ => Ok(None) @@ -1231,7 +1229,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>) -> Result>> { match repr { @@ -1503,7 +1501,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, packet: EthernetPacket) -> Result<()> where Tx: TxToken @@ -1530,7 +1528,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn dispatch_ethernet(&mut self, tx_token: Tx, timestamp: Instant, buffer_len: usize, f: F) -> Result<()> where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>) @@ -1570,10 +1568,11 @@ impl<'a> InterfaceInner<'a> { match self.route(addr, timestamp) { Ok(_routed_addr) => { match self.device_capabilities.medium { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Medium::Ethernet => self.neighbor_cache.as_ref().unwrap() .lookup(&_routed_addr, timestamp) .found(), + #[cfg(feature = "medium-ip")] Medium::Ip => true, } } @@ -1581,7 +1580,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn lookup_hardware_addr(&mut self, tx_token: Tx, timestamp: Instant, src_addr: &IpAddress, dst_addr: &IpAddress) -> Result<(EthernetAddress, Tx)> @@ -1682,7 +1681,7 @@ impl<'a> InterfaceInner<'a> { let caps = self.device_capabilities.clone(); match self.device_capabilities.medium { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { let (dst_hardware_addr, tx_token) = self.lookup_hardware_addr(tx_token, timestamp, @@ -1704,6 +1703,7 @@ impl<'a> InterfaceInner<'a> { packet.emit_payload(ip_repr, payload, &caps); }) } + #[cfg(feature = "medium-ip")] Medium::Ip => { let tx_len = ip_repr.total_len(); tx_token.consume(timestamp, tx_len, |mut tx_buffer| { @@ -1773,6 +1773,15 @@ mod test { use crate::phy::{Loopback, ChecksumCapabilities}; fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + #[cfg(feature = "medium-ethernet")] + return create_loopback_ethernet(); + #[cfg(not(feature = "medium-ethernet"))] + return create_loopback_ip(); + } + + #[cfg(all(feature = "medium-ip"))] + #[allow(unused)] + fn create_loopback_ip<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device let device = Loopback::new(Medium::Ip); let ip_addrs = [ @@ -1795,7 +1804,7 @@ mod test { (iface, SocketSet::new(vec![])) } - #[cfg(all(feature = "ethernet"))] + #[cfg(all(feature = "medium-ethernet"))] fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device let device = Loopback::new(Medium::Ethernet); @@ -1845,7 +1854,7 @@ mod test { #[test] #[should_panic(expected = "ethernet_addr required option was not set")] - #[cfg(all(feature = "ethernet"))] + #[cfg(all(feature = "medium-ethernet"))] fn test_builder_initialization_panic() { InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); } @@ -2300,7 +2309,7 @@ mod test { } #[test] - #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { let (mut iface, mut socket_set) = create_loopback_ethernet(); @@ -2345,7 +2354,7 @@ mod test { } #[test] - #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { let (mut iface, mut socket_set) = create_loopback_ethernet(); @@ -2405,7 +2414,7 @@ mod test { } #[test] - #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { let (mut iface, mut socket_set) = create_loopback_ethernet(); @@ -2589,11 +2598,21 @@ mod test { #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { fn recv_igmp(mut iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { - let checksum_caps = &iface.device.capabilities().checksum; + let caps = iface.device.capabilities(); + let checksum_caps = &caps.checksum; recv_all(&mut iface, timestamp) .iter() .filter_map(|frame| { - let ipv4_packet = Ipv4Packet::new_checked(frame).ok()?; + + let ipv4_packet = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + let eth_frame = EthernetFrame::new_checked(frame).ok()?; + Ipv4Packet::new_checked(eth_frame.payload()).ok()? + } + #[cfg(feature = "medium-ip")] + Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()? + }; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; diff --git a/src/iface/mod.rs b/src/iface/mod.rs index a21bc2536..1b4f32d95 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,17 +4,19 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] mod neighbor; mod route; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] mod interface; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] pub use self::neighbor::Neighbor as Neighbor; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] pub(crate) use self::neighbor::Answer as NeighborAnswer; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] pub use self::neighbor::Cache as NeighborCache; pub use self::route::{Route, Routes}; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/lib.rs b/src/lib.rs index 624b6b92f..f143055ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(any(test, feature = "std")), no_std)] #![deny(unsafe_code)] -#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "ethernet"), deny(unused))] +#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "medium-ethernet"), deny(unused))] //! The _smoltcp_ library is built in a layered structure, with the layers corresponding //! to the levels of API abstraction. Only the highest layers would be used by a typical diff --git a/src/parsers.rs b/src/parsers.rs index bf87cdcae..136e58b80 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -3,7 +3,7 @@ use core::str::FromStr; use core::result; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] use crate::wire::EthernetAddress; use crate::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] @@ -118,7 +118,7 @@ impl<'a> Parser<'a> { } } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn accept_mac_joined_with(&mut self, separator: u8) -> Result { let mut octets = [0u8; 6]; for (n, octet) in octets.iter_mut().enumerate() { @@ -130,7 +130,7 @@ impl<'a> Parser<'a> { Ok(EthernetAddress(octets)) } - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] fn accept_mac(&mut self) -> Result { if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) { return Ok(mac) @@ -352,7 +352,7 @@ impl<'a> Parser<'a> { } } -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] impl FromStr for EthernetAddress { type Err = (); @@ -473,7 +473,7 @@ mod test { } #[test] - #[cfg(all(feature = "proto-ipv4", feature = "ethernet"))] + #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] fn test_mac() { assert_eq!(EthernetAddress::from_str(""), Err(())); assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"), diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 99947afa4..eadd0dc8c 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -11,7 +11,7 @@ and implementations of it: [TapInterface](struct.TapInterface.html), to transmit and receive frames on the host OS. */ -#![cfg_attr(feature = "ethernet", doc = r##" +#![cfg_attr(feature = "medium-ethernet", doc = r##" # Examples An implementation of the [Device](trait.Device.html) trait for a simple hardware @@ -117,7 +117,7 @@ pub use self::raw_socket::RawSocket; #[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] pub use self::tap_interface::TapInterface; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] /// A tracer device for Ethernet frames. pub type EthernetTracer = Tracer>; @@ -238,23 +238,26 @@ pub enum Medium { /// and interfaces using it must do neighbor discovery via ARP or NDISC. /// /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Ethernet, /// IP medium. Devices of this type send and receive IP frames, without an /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. /// /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. + #[cfg(feature = "medium-ip")] Ip, } impl Default for Medium { fn default() -> Medium { - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] return Medium::Ethernet; - #[cfg(not(feature = "ethernet"))] + #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))] return Medium::Ip; + #[cfg(all(not(feature = "medium-ip"), not(feature = "medium-ethernet")))] + panic!("No medium enabled"); } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 597414a80..a347a0311 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -6,7 +6,7 @@ use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; use crate::wire::MldRepr; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] use crate::wire::NdiscRepr; enum_with_unknown! { @@ -536,7 +536,7 @@ pub enum Repr<'a> { seq_no: u16, data: &'a [u8] }, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Ndisc(NdiscRepr<'a>), Mld(MldRepr<'a>), } @@ -617,7 +617,7 @@ impl<'a> Repr<'a> { data: packet.payload() }) }, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] (msg_type, 0) if msg_type.is_ndisc() => { NdiscRepr::parse(packet).map(Repr::Ndisc) }, @@ -639,7 +639,7 @@ impl<'a> Repr<'a> { &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() }, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] &Repr::Ndisc(ndisc) => { ndisc.buffer_len() }, @@ -710,7 +710,7 @@ impl<'a> Repr<'a> { packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) }, - #[cfg(feature = "ethernet")] + #[cfg(feature = "medium-ethernet")] Repr::Ndisc(ndisc) => { ndisc.emit(packet) }, diff --git a/src/wire/mod.rs b/src/wire/mod.rs index c813c7b9f..4fee202b4 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -77,9 +77,9 @@ mod field { pub mod pretty_print; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] mod ethernet; -#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] mod arp; pub(crate) mod ip; #[cfg(feature = "proto-ipv4")] @@ -102,9 +102,9 @@ mod icmpv6; mod icmp; #[cfg(feature = "proto-igmp")] mod igmp; -#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] mod ndisc; -#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] mod ndiscoption; #[cfg(feature = "proto-ipv6")] mod mld; @@ -115,14 +115,14 @@ pub(crate) mod dhcpv4; pub use self::pretty_print::PrettyPrinter; -#[cfg(feature = "ethernet")] +#[cfg(feature = "medium-ethernet")] pub use self::ethernet::{EtherType as EthernetProtocol, Address as EthernetAddress, Frame as EthernetFrame, HEADER_LEN as ETHERNET_HEADER_LEN, Repr as EthernetRepr}; -#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] pub use self::arp::{Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, @@ -193,12 +193,12 @@ pub use self::icmpv6::{Message as Icmpv6Message, pub use self::icmp::Repr as IcmpRepr; -#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] pub use self::ndisc::{Repr as NdiscRepr, RouterFlags as NdiscRouterFlags, NeighborFlags as NdiscNeighborFlags}; -#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))] +#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] pub use self::ndiscoption::{NdiscOption, Repr as NdiscOptionRepr, Type as NdiscOptionType, From 56c779ed639794207faac979279636f9b9c4ae72 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Dec 2020 00:23:57 +0100 Subject: [PATCH 102/566] Do not use DeviceCapabilities in sockets. DeviceCapabilities contains the `medium` field, so tests had to give it a value even if it was unused. This is impossible to do with no `medium-*` enabled, because it makes `Medium` uninhabited (empty enum). --- src/iface/interface.rs | 25 ++++++------ src/socket/icmp.rs | 92 +++++++++++++++++++++--------------------- src/socket/tcp.rs | 21 ++++------ 3 files changed, 66 insertions(+), 72 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 2f43c1e49..bdb48d6b8 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -634,13 +634,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { - let mut caps = self.device.capabilities(); - caps.max_transmission_unit = 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 _caps = self.device.capabilities(); let mut emitted_any = false; for mut socket in sockets.iter_mut() { @@ -667,11 +661,11 @@ impl<'a, DeviceT> Interface<'a, DeviceT> match *socket { #[cfg(feature = "socket-raw")] Socket::Raw(ref mut socket) => - socket.dispatch(&caps.checksum, |response| + socket.dispatch(&_caps.checksum, |response| respond!(IpPacket::Raw(response))), #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] Socket::Icmp(ref mut socket) => - socket.dispatch(&caps, |response| { + socket.dispatch(|response| { match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => @@ -687,9 +681,16 @@ impl<'a, DeviceT> Interface<'a, DeviceT> socket.dispatch(|response| respond!(IpPacket::Udp(response))), #[cfg(feature = "socket-tcp")] - Socket::Tcp(ref mut socket) => - socket.dispatch(timestamp, &caps, |response| - respond!(IpPacket::Tcp(response))), + Socket::Tcp(ref mut socket) => { + 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, + }; + socket.dispatch(timestamp, ip_mtu, |response| + respond!(IpPacket::Tcp(response))) + } }; match (device_result, socket_result) { diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index e6f655549..4fbb226f0 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -3,7 +3,7 @@ use core::cmp; use core::task::Waker; use crate::{Error, Result}; -use crate::phy::{ChecksumCapabilities, DeviceCapabilities}; +use crate::phy::ChecksumCapabilities; use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; #[cfg(feature = "async")] @@ -400,7 +400,7 @@ impl<'a> IcmpSocket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, _caps: &DeviceCapabilities, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()> { let handle = self.meta.handle; @@ -528,9 +528,9 @@ mod test_ipv4 { #[test] fn test_send_dispatch() { let mut socket = socket(buffer(0), buffer(1)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); - assert_eq!(socket.dispatch(&caps, |_| unreachable!()), + assert_eq!(socket.dispatch(|_| unreachable!()), Err(Error::Exhausted)); // This buffer is too long @@ -539,13 +539,13 @@ mod test_ipv4 { let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); - ECHOV4_REPR.emit(&mut packet, &caps.checksum); + ECHOV4_REPR.emit(&mut packet, &checksum); assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(())); assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted)); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); Err(Error::Unaddressable) @@ -553,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(&caps, |(ip_repr, icmp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); Ok(()) @@ -565,16 +565,16 @@ mod test_ipv4 { #[test] fn test_set_hop_limit_v4() { let mut s = socket(buffer(0), buffer(1)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); - ECHOV4_REPR.emit(&mut packet, &caps.checksum); + ECHOV4_REPR.emit(&mut packet, &checksum); s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(())); - assert_eq!(s.dispatch(&caps, |(ip_repr, _)| { + assert_eq!(s.dispatch(|(ip_repr, _)| { assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: REMOTE_IPV4, @@ -594,20 +594,20 @@ mod test_ipv4 { assert!(!socket.can_recv()); assert_eq!(socket.recv(), Err(Error::Exhausted)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); - ECHOV4_REPR.emit(&mut packet, &caps.checksum); + ECHOV4_REPR.emit(&mut packet, &checksum); let data = &packet.into_inner()[..]; - assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum)); - assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum), + assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum)); + assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum), Ok(())); assert!(socket.can_recv()); - assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum)); - assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum), + assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum)); + assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum), Err(Error::Exhausted)); assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into()))); @@ -619,7 +619,7 @@ mod test_ipv4 { let mut socket = socket(buffer(1), buffer(1)); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 20]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); let icmp_repr = Icmpv4Repr::EchoRequest { @@ -627,11 +627,11 @@ mod test_ipv4 { seq_no: 0x5678, data: &[0xff; 16] }; - icmp_repr.emit(&mut packet, &caps.checksum); + icmp_repr.emit(&mut packet, &checksum); // 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(), &caps.checksum)); + assert!(!socket.accepts(&REMOTE_IPV4_REPR, &icmp_repr.into(), &checksum)); } #[test] @@ -639,11 +639,11 @@ mod test_ipv4 { let mut socket = socket(buffer(1), buffer(1)); assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4)), Ok(())); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 18]; let mut packet = UdpPacket::new_unchecked(&mut bytes); - UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &caps.checksum); + UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &checksum); let data = &packet.into_inner()[..]; @@ -670,14 +670,14 @@ mod test_ipv4 { // Ensure we can accept ICMP error response to the bound // UDP port - assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum)); - assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum), + assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum)); + assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum), Ok(())); assert!(socket.can_recv()); let mut bytes = [0x00; 46]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); - icmp_repr.emit(&mut packet, &caps.checksum); + icmp_repr.emit(&mut packet, &checksum); assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into()))); assert!(!socket.can_recv()); } @@ -727,9 +727,9 @@ mod test_ipv6 { #[test] fn test_send_dispatch() { let mut socket = socket(buffer(0), buffer(1)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); - assert_eq!(socket.dispatch(&caps, |_| unreachable!()), + assert_eq!(socket.dispatch(|_| unreachable!()), Err(Error::Exhausted)); // This buffer is too long @@ -738,13 +738,13 @@ mod test_ipv6 { let mut bytes = vec![0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum); + ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(())); assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted)); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); Err(Error::Unaddressable) @@ -752,7 +752,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(&caps, |(ip_repr, icmp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); Ok(()) @@ -764,16 +764,16 @@ mod test_ipv6 { #[test] fn test_set_hop_limit() { let mut s = socket(buffer(0), buffer(1)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = vec![0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum); + ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(())); - assert_eq!(s.dispatch(&caps, |(ip_repr, _)| { + assert_eq!(s.dispatch(|(ip_repr, _)| { assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr { src_addr: Ipv6Address::UNSPECIFIED, dst_addr: REMOTE_IPV6, @@ -793,20 +793,20 @@ mod test_ipv6 { assert!(!socket.can_recv()); assert_eq!(socket.recv(), Err(Error::Exhausted)); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum); + 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(), &caps.checksum)); - assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum), + assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum)); + assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum), Ok(())); assert!(socket.can_recv()); - assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum)); - assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum), + assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum)); + assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum), Err(Error::Exhausted)); assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into()))); @@ -818,7 +818,7 @@ mod test_ipv6 { let mut socket = socket(buffer(1), buffer(1)); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 20]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); let icmp_repr = Icmpv6Repr::EchoRequest { @@ -826,11 +826,11 @@ mod test_ipv6 { seq_no: 0x5678, data: &[0xff; 16] }; - icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum); + icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); // 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(), &caps.checksum)); + assert!(!socket.accepts(&REMOTE_IPV6_REPR, &icmp_repr.into(), &checksum)); } #[test] @@ -838,11 +838,11 @@ mod test_ipv6 { let mut socket = socket(buffer(1), buffer(1)); assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6)), Ok(())); - let caps = DeviceCapabilities::default(); + let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 18]; let mut packet = UdpPacket::new_unchecked(&mut bytes); - UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &caps.checksum); + UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &checksum); let data = &packet.into_inner()[..]; @@ -869,14 +869,14 @@ mod test_ipv6 { // Ensure we can accept ICMP error response to the bound // UDP port - assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum)); - assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum), + assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum)); + assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum), Ok(())); assert!(socket.can_recv()); let mut bytes = [0x00; 66]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]); - icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum); + icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into()))); assert!(!socket.can_recv()); } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index d9be855db..afa5cae0b 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -7,7 +7,6 @@ use core::{cmp, fmt, mem}; use core::task::Waker; use crate::{Error, Result}; -use crate::phy::DeviceCapabilities; use crate::time::{Duration, Instant}; use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt}; use crate::storage::{Assembler, RingBuffer}; @@ -1662,7 +1661,7 @@ impl<'a> TcpSocket<'a> { } } - pub(crate) fn dispatch(&mut self, timestamp: Instant, caps: &DeviceCapabilities, + pub(crate) fn dispatch(&mut self, timestamp: Instant, ip_mtu: usize, emit: F) -> Result<()> where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> { if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) } @@ -1789,7 +1788,7 @@ impl<'a> TcpSocket<'a> { let offset = self.remote_last_seq - self.local_seq_no; let win_limit = self.local_seq_no + self.remote_win_len - self.remote_last_seq; let size = cmp::min(cmp::min(win_limit, self.remote_mss), - caps.max_transmission_unit - ip_repr.buffer_len() - repr.mss_header_len()); + ip_mtu - ip_repr.buffer_len() - repr.mss_header_len()); repr.payload = self.tx_buffer.get_allocated(offset, size); // If we've sent everything we had in the buffer, follow it with the PSH or FIN // flags, depending on whether the transmit half of the connection is open. @@ -1848,7 +1847,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 = caps.max_transmission_unit; + let mut max_segment_size = 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); @@ -2044,11 +2043,8 @@ mod test { fn recv(socket: &mut TcpSocket, timestamp: Instant, mut f: F) where F: FnMut(Result) { - let caps = DeviceCapabilities { - max_transmission_unit: 1520, - ..Default::default() - }; - let result = socket.dispatch(timestamp, &caps, |(ip_repr, tcp_repr)| { + let mtu = 1520; + let result = socket.dispatch(timestamp, mtu, |(ip_repr, tcp_repr)| { let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap(); assert_eq!(ip_repr.protocol(), IpProtocol::Tcp); @@ -4897,13 +4893,10 @@ mod test { #[test] fn test_set_hop_limit() { let mut s = socket_syn_received(); - let caps = DeviceCapabilities { - max_transmission_unit: 1520, - ..Default::default() - }; + let mtu = 1520; s.set_hop_limit(Some(0x2a)); - assert_eq!(s.dispatch(Instant::from_millis(0), &caps, |(ip_repr, _)| { + assert_eq!(s.dispatch(Instant::from_millis(0), mtu, |(ip_repr, _)| { assert_eq!(ip_repr.hop_limit(), 0x2a); Ok(()) }), Ok(())); From b869449b3139dd8055246ef3e86c6aa8231f9551 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Mar 2021 18:04:42 +0100 Subject: [PATCH 103/566] Add support for TUN interfaces. --- .github/workflows/test.yml | 2 +- Cargo.toml | 24 +++++------ README.md | 4 +- examples/benchmark.rs | 18 ++++---- examples/client.rs | 22 ++++++---- examples/dhcp_client.rs | 21 ++++++---- examples/httpclient.rs | 21 ++++++---- examples/multicast.rs | 4 +- examples/ping.rs | 21 ++++++---- examples/server.rs | 19 +++++---- examples/utils.rs | 24 +++++++---- src/phy/mod.rs | 15 +++---- src/phy/sys/linux.rs | 10 ++--- src/phy/sys/mod.rs | 14 +++---- .../{tap_interface.rs => tuntap_interface.rs} | 42 +++++++++++++------ .../{tap_interface.rs => tuntap_interface.rs} | 30 ++++++------- 16 files changed, 172 insertions(+), 119 deletions(-) rename src/phy/sys/{tap_interface.rs => tuntap_interface.rs} (65%) rename src/phy/{tap_interface.rs => tuntap_interface.rs} (78%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index adf2bd136..e1f45ad9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: # Test features chosen to be as orthogonal as possible. - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp - - std medium-ethernet phy-tap_interface proto-ipv6 socket-udp + - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp - std medium-ethernet proto-ipv4 proto-igmp socket-raw - std medium-ethernet proto-ipv4 socket-udp socket-tcp - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp diff --git a/Cargo.toml b/Cargo.toml index a5b98338b..dfa99178f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,11 +34,11 @@ alloc = ["managed/alloc"] verbose = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] -"phy-raw_socket" = ["std", "libc", "ethernet"] -"phy-tap_interface" = ["std", "libc", "ethernet"] +"phy-raw_socket" = ["std", "libc", "medium-ethernet"] +"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "proto-ipv4" = [] "proto-igmp" = ["proto-ipv4"] -"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"] +"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "medium-ethernet"] "proto-ipv6" = [] "socket" = [] "socket-raw" = ["socket"] @@ -49,7 +49,7 @@ verbose = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", - "phy-raw_socket", "phy-tap_interface", + "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "async" @@ -69,35 +69,35 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"] [[example]] name = "httpclient" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"] [[example]] name = "ping" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"] [[example]] name = "server" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] [[example]] name = "client" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] [[example]] name = "loopback" -required-features = ["log", "proto-ipv4", "socket-tcp"] +required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"] [[example]] name = "multicast" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-igmp", "socket-udp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "socket-udp"] [[example]] name = "benchmark" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-raw", "socket-udp"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-raw", "socket-udp"] [[example]] name = "dhcp_client" -required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"] +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"] [profile.release] debug = 2 diff --git a/README.md b/README.md index 331f7aa06..6098989b6 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,9 @@ or `BufWriter` is used, which are of course not available on heap-less systems. This feature is disabled by default. -### Features `phy-raw_socket` and `phy-tap_interface` +### Features `phy-raw_socket` and `phy-tuntap_interface` -Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TapInterface`, respectively. +Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respectively. These features are enabled by default. diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 9ce2f57bd..6527ba4ee 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -11,7 +11,7 @@ use std::net::TcpStream; use std::os::unix::io::AsRawFd; use log::debug; -use smoltcp::phy::wait as phy_wait; +use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; @@ -62,12 +62,12 @@ fn main() { utils::setup_logging("info"); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); free.push("MODE"); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let mode = match matches.free[0].as_ref() { @@ -90,11 +90,15 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; - let mut iface = InterfaceBuilder::new(device) + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) + .ip_addrs(ip_addrs); + if medium == Medium::Ethernet { + builder = builder .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); diff --git a/examples/client.rs b/examples/client.rs index ecce1c7ff..308ec991b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use log::debug; -use smoltcp::phy::wait as phy_wait; +use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; @@ -15,13 +15,14 @@ fn main() { utils::setup_logging(""); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); free.push("ADDRESS"); free.push("PORT"); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); + let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); @@ -39,12 +40,17 @@ fn main() { let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + if medium == Medium::Ethernet { + builder = builder + .ethernet_addr(ethernet_addr) + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 6e9a67096..81d685f26 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -3,7 +3,7 @@ mod utils; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use smoltcp::phy::wait as phy_wait; +use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; @@ -15,11 +15,11 @@ fn main() { utils::setup_logging(""); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); @@ -28,12 +28,17 @@ fn main() { let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; let mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + if medium == Medium::Ethernet { + builder = builder + .ethernet_addr(ethernet_addr) + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let dhcp_rx_buffer = RawSocketBuffer::new( diff --git a/examples/httpclient.rs b/examples/httpclient.rs index be523060e..f41bb3204 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd; use url::Url; use log::debug; -use smoltcp::phy::wait as phy_wait; +use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; @@ -16,13 +16,13 @@ fn main() { utils::setup_logging(""); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); free.push("ADDRESS"); free.push("URL"); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); @@ -45,12 +45,17 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + if medium == Medium::Ethernet { + builder = builder + .ethernet_addr(ethernet_addr) + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); diff --git a/examples/multicast.rs b/examples/multicast.rs index cffb73a47..33d24262c 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -20,11 +20,11 @@ fn main() { utils::setup_logging("warn"); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, diff --git a/examples/ping.rs b/examples/ping.rs index 69f3de4ad..816470c1e 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use log::debug; use byteorder::{ByteOrder, NetworkEndian}; -use smoltcp::time::{Duration, Instant}; +use smoltcp::{phy::Medium, time::{Duration, Instant}}; use smoltcp::phy::Device; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, @@ -55,7 +55,7 @@ fn main() { utils::setup_logging("warn"); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT"); opts.optopt("i", "interval", @@ -66,7 +66,7 @@ fn main() { free.push("ADDRESS"); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device_caps = device.capabilities(); @@ -98,12 +98,17 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .neighbor_cache(neighbor_cache) - .finalize(); + .routes(routes); + if medium == Medium::Ethernet { + builder = builder + .ethernet_addr(ethernet_addr) + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let icmp_handle = sockets.add(icmp_socket); diff --git a/examples/server.rs b/examples/server.rs index 832f97572..944cde403 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use std::os::unix::io::AsRawFd; use log::debug; -use smoltcp::phy::wait as phy_wait; +use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; @@ -18,11 +18,11 @@ fn main() { utils::setup_logging(""); let (mut opts, mut free) = utils::create_options(); - utils::add_tap_options(&mut opts, &mut free); + utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_tap_options(&mut matches); + let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); @@ -54,11 +54,16 @@ fn main() { IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64) ]; - let mut iface = InterfaceBuilder::new(device) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) + .ip_addrs(ip_addrs); + if medium == Medium::Ethernet { + builder = builder .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); diff --git a/examples/utils.rs b/examples/utils.rs index 02b2de0bb..497ad97ba 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -14,9 +14,9 @@ use log::{Level, LevelFilter, trace}; use env_logger::Builder; use getopts::{Options, Matches}; -use smoltcp::phy::{Device, EthernetTracer, FaultInjector}; -#[cfg(feature = "phy-tap_interface")] -use smoltcp::phy::TapInterface; +use smoltcp::phy::{Device, EthernetTracer, FaultInjector, Medium}; +#[cfg(feature = "phy-tuntap_interface")] +use smoltcp::phy::TunTapInterface; use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; use smoltcp::phy::RawSocket; use smoltcp::time::{Duration, Instant}; @@ -77,14 +77,20 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { } } -pub fn add_tap_options(_opts: &mut Options, free: &mut Vec<&str>) { - free.push("INTERFACE"); +pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) { + opts.optopt("", "tun", "TUN interface to use", "tun0"); + opts.optopt("", "tap", "TAP interface to use", "tap0"); } -#[cfg(feature = "phy-tap_interface")] -pub fn parse_tap_options(matches: &mut Matches) -> TapInterface { - let interface = matches.free.remove(0); - TapInterface::new(&interface).unwrap() +#[cfg(feature = "phy-tuntap_interface")] +pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { + let tun = matches.opt_str("tun"); + let tap = matches.opt_str("tap"); + match(tun,tap) { + (Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(), + (None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(), + _ => panic!("You must specify exactly one of --tun or --tap"), + } } pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket { diff --git a/src/phy/mod.rs b/src/phy/mod.rs index eadd0dc8c..df4a2e84d 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -8,7 +8,7 @@ and implementations of it: * _middleware_ [Tracer](struct.Tracer.html) and [FaultInjector](struct.FaultInjector.html), to facilitate debugging; * _adapters_ [RawSocket](struct.RawSocket.html) and - [TapInterface](struct.TapInterface.html), to transmit and receive frames + [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames on the host OS. */ #![cfg_attr(feature = "medium-ethernet", doc = r##" @@ -89,7 +89,7 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { use crate::Result; use crate::time::Instant; -#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] +#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))] mod sys; mod tracer; @@ -100,10 +100,10 @@ mod pcap_writer; mod loopback; #[cfg(all(feature = "phy-raw_socket", unix))] mod raw_socket; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -mod tap_interface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +mod tuntap_interface; -#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] +#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))] pub use self::sys::wait; pub use self::tracer::Tracer; @@ -114,8 +114,9 @@ pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; pub use self::loopback::Loopback; #[cfg(all(feature = "phy-raw_socket", unix))] pub use self::raw_socket::RawSocket; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub use self::tap_interface::TapInterface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub use self::tuntap_interface::TunTapInterface; + #[cfg(feature = "medium-ethernet")] /// A tracer device for Ethernet frames. diff --git a/src/phy/sys/linux.rs b/src/phy/sys/linux.rs index f582770ba..d42d83fc1 100644 --- a/src/phy/sys/linux.rs +++ b/src/phy/sys/linux.rs @@ -1,14 +1,10 @@ -#[cfg(any(feature = "phy-raw_socket", - feature = "phy-tap_interface"))] +#![allow(unused)] + pub const SIOCGIFMTU: libc::c_ulong = 0x8921; -#[cfg(any(feature = "phy-raw_socket"))] pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; -#[cfg(any(feature = "phy-raw_socket"))] pub const ETH_P_ALL: libc::c_short = 0x0003; -#[cfg(feature = "phy-tap_interface")] pub const TUNSETIFF: libc::c_ulong = 0x400454CA; -#[cfg(feature = "phy-tap_interface")] +pub const IFF_TUN: libc::c_int = 0x0001; pub const IFF_TAP: libc::c_int = 0x0002; -#[cfg(feature = "phy-tap_interface")] pub const IFF_NO_PI: libc::c_int = 0x1000; diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 0730d1baa..8021879a6 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -12,15 +12,15 @@ mod imp; pub mod raw_socket; #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub mod bpf; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub mod tap_interface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub mod tuntap_interface; #[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] pub use self::raw_socket::RawSocketDesc; #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub use self::bpf::BpfDevice as RawSocketDesc; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub use self::tap_interface::TapInterfaceDesc; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub use self::tuntap_interface::TunTapInterfaceDesc; /// Wait until given file descriptor becomes readable, but no longer than given timeout. pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { @@ -60,7 +60,7 @@ pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { } } -#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))] +#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))] #[repr(C)] #[derive(Debug)] struct ifreq { @@ -68,7 +68,7 @@ struct ifreq { ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */ } -#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))] +#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))] fn ifreq_for(name: &str) -> ifreq { let mut ifreq = ifreq { ifr_name: [0; libc::IF_NAMESIZE], @@ -81,7 +81,7 @@ fn ifreq_for(name: &str) -> ifreq { } #[cfg(all(any(target_os = "linux", target_os = "android"), - any(feature = "phy-tap_interface", feature = "phy-raw_socket")))] + any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))] fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, cmd: libc::c_ulong) -> io::Result { unsafe { diff --git a/src/phy/sys/tap_interface.rs b/src/phy/sys/tuntap_interface.rs similarity index 65% rename from src/phy/sys/tap_interface.rs rename to src/phy/sys/tuntap_interface.rs index 9608e6e21..6b42980b1 100644 --- a/src/phy/sys/tap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -1,22 +1,23 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use super::*; -use crate::wire::EthernetFrame; +use crate::{phy::Medium, wire::EthernetFrame}; #[derive(Debug)] -pub struct TapInterfaceDesc { +pub struct TunTapInterfaceDesc { lower: libc::c_int, - ifreq: ifreq + ifreq: ifreq, + medium: Medium, } -impl AsRawFd for TapInterfaceDesc { +impl AsRawFd for TunTapInterfaceDesc { fn as_raw_fd(&self) -> RawFd { self.lower } } -impl TapInterfaceDesc { - pub fn new(name: &str) -> io::Result { +impl TunTapInterfaceDesc { + pub fn new(name: &str, medium: Medium) -> io::Result { let lower = unsafe { let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char, libc::O_RDWR | libc::O_NONBLOCK); @@ -24,14 +25,21 @@ impl TapInterfaceDesc { lower }; - Ok(TapInterfaceDesc { - lower: lower, - ifreq: ifreq_for(name) + Ok(TunTapInterfaceDesc { + lower, + ifreq: ifreq_for(name), + medium, }) } pub fn attach_interface(&mut self) -> io::Result<()> { - self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI; + let mode = match self.medium { + #[cfg(feature = "medium-ip")] + Medium::Ip => imp::IFF_TUN, + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => imp::IFF_TAP, + }; + self.ifreq.ifr_data = mode | imp::IFF_NO_PI; ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ()) } @@ -46,9 +54,19 @@ impl TapInterfaceDesc { unsafe { libc::close(lower); } + // Propagate error after close, to ensure we always close. + let ip_mtu = ip_mtu?; + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. - Ok(ip_mtu? + EthernetFrame::<&[u8]>::header_len()) + let mtu = match self.medium { + #[cfg(feature = "medium-ip")] + Medium::Ip => ip_mtu, + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(), + }; + + Ok(mtu) } pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { @@ -70,7 +88,7 @@ impl TapInterfaceDesc { } } -impl Drop for TapInterfaceDesc { +impl Drop for TunTapInterfaceDesc { fn drop(&mut self) { unsafe { libc::close(self.lower); } } diff --git a/src/phy/tap_interface.rs b/src/phy/tuntap_interface.rs similarity index 78% rename from src/phy/tap_interface.rs rename to src/phy/tuntap_interface.rs index 018c2c09d..cfd4f2d92 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -8,44 +8,46 @@ use crate::Result; use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; -/// A virtual Ethernet interface. +/// A virtual TUN (IP) or TAP (Ethernet) interface. #[derive(Debug)] -pub struct TapInterface { - lower: Rc>, - mtu: usize +pub struct TunTapInterface { + lower: Rc>, + mtu: usize, + medium: Medium, } -impl AsRawFd for TapInterface { +impl AsRawFd for TunTapInterface { fn as_raw_fd(&self) -> RawFd { self.lower.borrow().as_raw_fd() } } -impl TapInterface { - /// Attaches to a TAP interface called `name`, or creates it if it does not exist. +impl TunTapInterface { + /// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist. /// /// If `name` is a persistent interface configured with UID of the current user, /// no special privileges are needed. Otherwise, this requires superuser privileges /// or a corresponding capability set on the executable. - pub fn new(name: &str) -> io::Result { - let mut lower = sys::TapInterfaceDesc::new(name)?; + pub fn new(name: &str, medium: Medium) -> io::Result { + let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?; lower.attach_interface()?; let mtu = lower.interface_mtu()?; - Ok(TapInterface { + Ok(TunTapInterface { lower: Rc::new(RefCell::new(lower)), - mtu: mtu + mtu, + medium, }) } } -impl<'a> Device<'a> for TapInterface { +impl<'a> Device<'a> for TunTapInterface { type RxToken = RxToken; type TxToken = TxToken; fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, - medium: Medium::Ethernet, + medium: self.medium, ..DeviceCapabilities::default() } } @@ -89,7 +91,7 @@ impl phy::RxToken for RxToken { #[doc(hidden)] pub struct TxToken { - lower: Rc>, + lower: Rc>, } impl phy::TxToken for TxToken { From 56bff5dcca3bb67b2e9b3906d380fd0d623a616f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 25 Mar 2021 00:20:58 +0100 Subject: [PATCH 104/566] Add IP medium support to PcapWriter and Tracer. --- README.md | 20 +++++----- examples/utils.rs | 18 +++++---- src/phy/mod.rs | 5 --- src/phy/pcap_writer.rs | 12 +++++- src/phy/tracer.rs | 84 ++++++++++++++++++++++++++++++------------ 5 files changed, 91 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 6098989b6..dc616ec09 100644 --- a/README.md +++ b/README.md @@ -270,19 +270,19 @@ The host is assigned the hardware address `02-00-00-00-00-02`, IPv4 address `192 Read its [source code](/examples/httpclient.rs), then run it as: ```sh -cargo run --example httpclient -- tap0 ADDRESS URL +cargo run --example httpclient -- --tap tap0 ADDRESS URL ``` For example: ```sh -cargo run --example httpclient -- tap0 93.184.216.34 http://example.org/ +cargo run --example httpclient -- --tap tap0 93.184.216.34 http://example.org/ ``` or: ```sh -cargo run --example httpclient -- tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/ +cargo run --example httpclient -- --tap tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/ ``` It connects to the given address (not a hostname) and URL, and prints any returned response data. @@ -297,7 +297,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address ` Read its [source code](/examples/ping.rs), then run it as: ```sh -cargo run --example ping -- tap0 ADDRESS +cargo run --example ping -- --tap tap0 ADDRESS ``` It sends a series of 4 ICMP ECHO\_REQUEST packets to the given address at one second intervals and @@ -319,7 +319,7 @@ The host is assigned the hardware address `02-00-00-00-00-01` and IPv4 address ` Read its [source code](/examples/server.rs), then run it as: ```sh -cargo run --example server -- tap0 +cargo run --example server -- --tap tap0 ``` It responds to: @@ -349,7 +349,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address ` Read its [source code](/examples/client.rs), then run it as: ```sh -cargo run --example client -- tap0 ADDRESS PORT +cargo run --example client -- --tap tap0 ADDRESS PORT ``` It connects to the given address (not a hostname) and port (e.g. `socat stdio tcp4-listen:1234`), @@ -362,7 +362,7 @@ _examples/benchmark.rs_ implements a simple throughput benchmark. Read its [source code](/examples/benchmark.rs), then run it as: ```sh -cargo run --release --example benchmark -- tap0 [reader|writer] +cargo run --release --example benchmark -- --tap tap0 [reader|writer] ``` It establishes a connection to itself from a different thread and reads or writes a large amount @@ -372,9 +372,9 @@ A typical result (achieved on a Intel Core i7-7500U CPU and a Linux 4.9.65 x86_6 on a Dell XPS 13 9360 laptop) is as follows: ``` -$ cargo run -q --release --example benchmark tap0 reader +$ cargo run -q --release --example benchmark -- --tap tap0 reader throughput: 2.556 Gbps -$ cargo run -q --release --example benchmark tap0 writer +$ cargo run -q --release --example benchmark -- --tap tap0 writer throughput: 5.301 Gbps ``` @@ -391,7 +391,7 @@ Although it does not require `std`, this example still requires the `alloc` feat Read its [source code](/examples/loopback.rs), then run it without `std`: ```sh -cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc" +cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc" ``` ... or with `std` (in this case the features don't have to be explicitly listed): diff --git a/examples/utils.rs b/examples/utils.rs index 497ad97ba..b4687a4ee 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -14,10 +14,10 @@ use log::{Level, LevelFilter, trace}; use env_logger::Builder; use getopts::{Options, Matches}; -use smoltcp::phy::{Device, EthernetTracer, FaultInjector, Medium}; +use smoltcp::phy::{Device, Tracer, FaultInjector, Medium}; #[cfg(feature = "phy-tuntap_interface")] use smoltcp::phy::TunTapInterface; -use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; +use smoltcp::phy::{PcapWriter, PcapSink, PcapMode}; use smoltcp::phy::RawSocket; use smoltcp::time::{Duration, Instant}; @@ -111,7 +111,7 @@ pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { } pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: bool) - -> FaultInjector>>> + -> FaultInjector>>> where D: for<'a> Device<'a> { let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap()) @@ -136,13 +136,17 @@ pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: b let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos(); - let device = PcapWriter::new(device, Rc::new(RefCell::new(pcap_writer)) as Rc, - if loopback { PcapMode::TxOnly } else { PcapMode::Both }, - PcapLinkType::Ethernet); - let device = EthernetTracer::new(device, |_timestamp, _printer| { + let device = PcapWriter::new( + device, + Rc::new(RefCell::new(pcap_writer)) as Rc, + if loopback { PcapMode::TxOnly } else { PcapMode::Both }, + ); + + let device = Tracer::new(device, |_timestamp, _printer| { #[cfg(feature = "log")] trace!("{}", _printer); }); + let mut device = FaultInjector::new(device, seed); device.set_drop_chance(drop_chance); device.set_corrupt_chance(corrupt_chance); diff --git a/src/phy/mod.rs b/src/phy/mod.rs index df4a2e84d..b6ae5ff66 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -117,11 +117,6 @@ pub use self::raw_socket::RawSocket; #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] pub use self::tuntap_interface::TunTapInterface; - -#[cfg(feature = "medium-ethernet")] -/// A tracer device for Ethernet frames. -pub type EthernetTracer = Tracer>; - /// A description of checksum behavior for a particular protocol. #[derive(Debug, Clone, Copy)] pub enum Checksum { diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index afee86691..c14118d60 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; #[cfg(feature = "std")] use std::io::Write; use byteorder::{ByteOrder, NativeEndian}; +use phy::Medium; use crate::Result; use crate::phy::{self, DeviceCapabilities, Device}; @@ -14,7 +15,7 @@ enum_with_unknown! { /// Ethernet frames Ethernet = 1, /// IPv4 or IPv6 packets (depending on the version field) - Ip = 101 + Ip = 101, } } @@ -128,7 +129,14 @@ pub struct PcapWriter impl Device<'a>, S: PcapSink + Clone> PcapWriter { /// Creates a packet capture writer. - pub fn new(lower: D, sink: S, mode: PcapMode, link_type: PcapLinkType) -> PcapWriter { + pub fn new(lower: D, sink: S, mode: PcapMode) -> PcapWriter { + let medium = lower.capabilities().medium; + let link_type = match medium { + #[cfg(feature = "medium-ip")] + Medium::Ip => PcapLinkType::Ip, + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => PcapLinkType::Ethernet, + }; sink.global_header(link_type); PcapWriter { lower, sink, mode } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index d9ecfd279..8a5e377bd 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -1,6 +1,7 @@ -use crate::Result; -use crate::wire::pretty_print::{PrettyPrint, PrettyPrinter}; -use crate::phy::{self, DeviceCapabilities, Device}; +use core::fmt; + +use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}}; +use crate::phy::{self, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A tracer device. @@ -8,14 +9,14 @@ use crate::time::Instant; /// A tracer is a device that pretty prints all packets traversing it /// using the provided writer function, and then passes them to another /// device. -pub struct Tracer Device<'a>, P: PrettyPrint> { +pub struct Tracer Device<'a>> { inner: D, - writer: fn(Instant, PrettyPrinter

), + writer: fn(Instant, Packet), } -impl Device<'a>, P: PrettyPrint> Tracer { +impl Device<'a>> Tracer { /// Create a tracer device. - pub fn new(inner: D, writer: fn(timestamp: Instant, printer: PrettyPrinter

)) -> Tracer { + pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer { Tracer { inner, writer } } @@ -40,65 +41,100 @@ impl Device<'a>, P: PrettyPrint> Tracer { } } -impl<'a, D, P> Device<'a> for Tracer +impl<'a, D> Device<'a> for Tracer where D: for<'b> Device<'b>, - P: PrettyPrint + 'a, { - type RxToken = RxToken<>::RxToken, P>; - type TxToken = TxToken<>::TxToken, P>; + type RxToken = RxToken<>::RxToken>; + type TxToken = TxToken<>::TxToken>; fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { let &mut Self { ref mut inner, writer, .. } = self; + let medium = inner.capabilities().medium; inner.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { token: rx_token, writer }; - let tx = TxToken { token: tx_token, writer }; + let rx = RxToken { token: rx_token, writer, medium }; + let tx = TxToken { token: tx_token, writer, medium }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { let &mut Self { ref mut inner, writer } = self; + let medium = inner.capabilities().medium; inner.transmit().map(|tx_token| { - TxToken { token: tx_token, writer } + TxToken { token: tx_token, medium, writer } }) } } #[doc(hidden)] -pub struct RxToken { +pub struct RxToken { token: Rx, - writer: fn(Instant, PrettyPrinter

) + writer: fn(Instant, Packet), + medium: Medium, } -impl phy::RxToken for RxToken { +impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result { - let Self { token, writer } = self; + let Self { token, writer, medium } = self; token.consume(timestamp, |buffer| { - writer(timestamp, PrettyPrinter::

::new("<- ", &buffer)); + writer(timestamp, Packet{ + buffer, + medium, + prefix: "<- ", + }); f(buffer) }) } } #[doc(hidden)] -pub struct TxToken { +pub struct TxToken { token: Tx, - writer: fn(Instant, PrettyPrinter

) + writer: fn(Instant, Packet), + medium: Medium, } -impl phy::TxToken for TxToken { +impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result { - let Self { token, writer } = self; + let Self { token, writer, medium } = self; token.consume(timestamp, len, |buffer| { let result = f(buffer); - writer(timestamp, PrettyPrinter::

::new("-> ", &buffer)); + writer(timestamp, Packet{ + buffer, + medium, + prefix: "-> ", + }); result }) } } + +pub struct Packet<'a> { + buffer: &'a [u8], + medium: Medium, + prefix: &'static str, +} + +impl<'a> fmt::Display for Packet<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut indent = PrettyIndent::new(self.prefix); + match self.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), + #[cfg(feature = "medium-ip")] + Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) { + #[cfg(feature = "proto-ipv4")] + Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), + #[cfg(feature = "proto-ipv6")] + Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), + _ => f.write_str("unrecognized IP version") + } + } + } +} \ No newline at end of file From 49fa987cdc906c8596abe8e96824f48737d192d6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Apr 2021 01:30:47 +0200 Subject: [PATCH 105/566] Add defmt logging support --- Cargo.toml | 8 ++++++++ examples/loopback.rs | 2 ++ fuzz/fuzz_targets/tcp_headers.rs | 1 + src/dhcp/clientv4.rs | 4 ++++ src/iface/interface.rs | 4 ++++ src/iface/neighbor.rs | 2 ++ src/iface/route.rs | 1 + src/lib.rs | 4 ++++ src/macros.rs | 9 ++++++++- src/phy/fault_injector.rs | 2 ++ src/phy/fuzz_injector.rs | 1 + src/phy/mod.rs | 4 ++++ src/phy/pcap_writer.rs | 2 ++ src/socket/icmp.rs | 1 + src/socket/meta.rs | 2 ++ src/socket/mod.rs | 1 + src/socket/set.rs | 1 + src/socket/tcp.rs | 2 ++ src/storage/assembler.rs | 2 ++ src/storage/packet_buffer.rs | 1 + src/time.rs | 2 ++ src/wire/arp.rs | 2 ++ src/wire/dhcpv4.rs | 3 +++ src/wire/ethernet.rs | 3 +++ src/wire/icmp.rs | 1 + src/wire/icmpv4.rs | 2 ++ src/wire/icmpv6.rs | 2 ++ src/wire/igmp.rs | 3 +++ src/wire/ip.rs | 34 ++++++++++++++++++++++++++++++++ src/wire/ipv4.rs | 16 +++++++++++++++ src/wire/ipv6.rs | 4 ++++ src/wire/ipv6fragment.rs | 2 ++ src/wire/ipv6hopbyhop.rs | 2 ++ src/wire/ipv6option.rs | 3 +++ src/wire/ipv6routing.rs | 2 ++ src/wire/mld.rs | 2 ++ src/wire/ndisc.rs | 3 +++ src/wire/ndiscoption.rs | 5 +++++ src/wire/pretty_print.rs | 1 + src/wire/tcp.rs | 5 +++++ src/wire/udp.rs | 2 ++ 41 files changed, 152 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dfa99178f..ae471652c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ byteorder = { version = "1.0", default-features = false } log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } +defmt = { version = "0.2.0", optional = true } [dev-dependencies] env_logger = "0.5" @@ -46,6 +47,13 @@ verbose = [] "socket-tcp" = ["socket"] "socket-icmp" = ["socket"] "async" = [] + +defmt-trace = [] +defmt-debug = [] +defmt-info = [] +defmt-warn = [] +defmt-error = [] + default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", diff --git a/examples/loopback.rs b/examples/loopback.rs index 13d0479b8..7eb6bc3d7 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -21,6 +21,7 @@ mod mock { use core::cell::Cell; #[derive(Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clock(Cell); impl Clock { @@ -46,6 +47,7 @@ mod mock { // should be AtomicU64 but that's unstable #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clock(Arc); impl Clock { diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index aec7a2523..86a274dfb 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -24,6 +24,7 @@ mod mock { // should be AtomicU64 but that's unstable #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clock(Arc); impl Clock { diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index e5029dfe9..33514a509 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -22,6 +22,7 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[ /// IPv4 configuration data returned by `client.poll()` #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { pub address: Option, pub router: Option, @@ -29,6 +30,7 @@ pub struct Config { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct RequestState { retry: u16, endpoint_ip: Ipv4Address, @@ -37,12 +39,14 @@ struct RequestState { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct RenewState { endpoint_ip: Ipv4Address, server_identifier: Ipv4Address, } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum ClientState { /// Discovering the DHCP server Discovering, diff --git a/src/iface/interface.rs b/src/iface/interface.rs index bdb48d6b8..0299f7174 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -243,6 +243,7 @@ let iface = InterfaceBuilder::new(device) } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(feature = "medium-ethernet")] enum EthernetPacket<'a> { #[cfg(feature = "proto-ipv4")] @@ -251,6 +252,7 @@ enum EthernetPacket<'a> { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum IpPacket<'a> { #[cfg(feature = "proto-ipv4")] Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)), @@ -591,6 +593,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Medium::Ethernet => { inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { net_debug!("cannot process ingress packet: {}", err); + #[cfg(not(feature = "defmt"))] net_debug!("packet dump follows:\n{}", PrettyPrinter::>::new("", &frame)); err @@ -1844,6 +1847,7 @@ mod test { } #[derive(Debug, PartialEq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct MockTxToken; impl TxToken for MockTxToken { diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index f251d1cb2..2f08017ce 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -11,6 +11,7 @@ use crate::time::{Duration, Instant}; /// A neighbor mapping translates from a protocol address to a hardware address, /// and contains the timestamp past which the mapping should be discarded. #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Neighbor { hardware_addr: EthernetAddress, expires_at: Instant, @@ -18,6 +19,7 @@ pub struct Neighbor { /// An answer to a neighbor cache lookup. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum Answer { /// The neighbor address is in the cache and not expired. Found(EthernetAddress), diff --git a/src/iface/route.rs b/src/iface/route.rs index 2ddc5fd6c..65e255be3 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -11,6 +11,7 @@ use crate::wire::{Ipv6Address, Ipv6Cidr}; /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Route { pub via_router: IpAddress, /// `None` means "forever". diff --git a/src/lib.rs b/src/lib.rs index f143055ec..dd4b1ed63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,9 @@ compile_error!("You must enable at least one of the following features: proto-ip ))] compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp"); +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You must enable at most one of the following features: defmt, log"); + use core::fmt; #[macro_use] @@ -116,6 +119,7 @@ pub mod dhcp; /// The error type for the networking stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// An operation cannot proceed because a buffer is empty or full. Exhausted, diff --git a/src/macros.rs b/src/macros.rs index 0f1cc98ef..63c7ca7e2 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,7 +5,13 @@ macro_rules! net_log { (debug, $($arg:expr),*) => { log::debug!($($arg),*); }; } -#[cfg(not(feature = "log"))] +#[cfg(feature = "defmt")] +macro_rules! net_log { + (trace, $($arg:expr),*) => { defmt::trace!($($arg),*); }; + (debug, $($arg:expr),*) => { defmt::debug!($($arg),*); }; +} + +#[cfg(not(any(feature = "log", feature = "defmt")))] #[macro_use] macro_rules! net_log { ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* } @@ -30,6 +36,7 @@ macro_rules! enum_with_unknown { } ) => { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] $( #[$enum_attr] )* pub enum $name { $( diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 5f79fec1a..3185f477a 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -19,6 +19,7 @@ fn xorshift32(state: &mut u32) -> u32 { const MTU: usize = 1536; #[derive(Debug, Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Config { corrupt_pct: u8, drop_pct: u8, @@ -30,6 +31,7 @@ struct Config { } #[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct State { rng_seed: u32, refilled_at: Instant, diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 9f11b1772..5e2023b7a 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -18,6 +18,7 @@ pub trait Fuzzer { /// smoltcp, and is not for production use. #[allow(unused)] #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FuzzInjector Device<'a>, FTx: Fuzzer, FRx: Fuzzer> { inner: D, fuzz_tx: FTx, diff --git a/src/phy/mod.rs b/src/phy/mod.rs index b6ae5ff66..c04867772 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -119,6 +119,7 @@ pub use self::tuntap_interface::TunTapInterface; /// A description of checksum behavior for a particular protocol. #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Checksum { /// Verify checksum when receiving and compute checksum when sending. Both, @@ -156,6 +157,7 @@ impl Checksum { /// A description of checksum behavior for every supported protocol. #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChecksumCapabilities { pub ipv4: Checksum, @@ -188,6 +190,7 @@ impl ChecksumCapabilities { /// Higher-level protocols may achieve higher throughput or lower latency if they consider /// the bandwidth or packet size limitations. #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct DeviceCapabilities { /// Medium of the device. @@ -229,6 +232,7 @@ pub struct DeviceCapabilities { /// Type of medium of a device. #[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Medium { /// Ethernet medium. Devices of this type send and receive Ethernet frames, /// and interfaces using it must do neighbor discovery via ARP or NDISC. diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index c14118d60..a2226bbd4 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -21,6 +21,7 @@ enum_with_unknown! { /// Packet capture mode. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PcapMode { /// Capture both received and transmitted packets. Both, @@ -118,6 +119,7 @@ impl PcapSink for RefCell { /// [libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat /// [sink]: trait.PcapSink.html #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PcapWriter where D: for<'a> Device<'a>, S: PcapSink + Clone, diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 4fbb226f0..e5779bddf 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -22,6 +22,7 @@ use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; /// /// [IcmpSocket::bind]: struct.IcmpSocket.html#method.bind #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Endpoint { Unspecified, Ident(u16), diff --git a/src/socket/meta.rs b/src/socket/meta.rs index b4e9fbb26..fe6873b5f 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -7,6 +7,7 @@ use crate::time::{Duration, Instant}; /// This enum tracks whether the socket should be polled based on the neighbor it is /// going to send packets to. #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum NeighborState { /// Socket can be polled immediately. Active, @@ -29,6 +30,7 @@ impl Default for NeighborState { /// This includes things that only external (to the socket, that is) code /// is interested in, but which are more conveniently stored inside the socket itself. #[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Meta { /// Handle of this socket within its enclosing `SocketSet`. /// Mainly useful for debug output. diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d7a971bd2..76fe30f5a 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -61,6 +61,7 @@ pub(crate) use self::ref_::Session as SocketSession; /// Gives an indication on the next time the socket should be polled. #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum PollAt { /// The socket needs to be polled immidiately. Now, diff --git a/src/socket/set.rs b/src/socket/set.rs index 826776348..686665658 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -17,6 +17,7 @@ pub struct Item<'a> { /// A handle, identifying a socket in a set. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Handle(usize); impl fmt::Display for Handle { diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index afa5cae0b..987916f85 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -21,6 +21,7 @@ pub type SocketBuffer<'a> = RingBuffer<'a, u8>; /// /// [RFC 793]: https://tools.ietf.org/html/rfc793 #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum State { Closed, Listen, @@ -147,6 +148,7 @@ impl RttEstimator { } #[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum Timer { Idle { keep_alive_at: Option, diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 159d93d2f..1eb97bcf8 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -5,6 +5,7 @@ pub struct TooManyHolesError; /// A contiguous chunk of absent data, followed by a contiguous chunk of present data. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Contig { hole_size: usize, data_size: usize @@ -80,6 +81,7 @@ const CONTIG_COUNT: usize = 4; /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer. #[derive(Debug)] #[cfg_attr(test, derive(PartialEq, Eq, Clone))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Assembler { #[cfg(not(any(feature = "std", feature = "alloc")))] contigs: [Contig; CONTIG_COUNT], diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index be9866bea..5502de2fe 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -5,6 +5,7 @@ use crate::storage::RingBuffer; /// Size and header of a packet. #[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PacketMetadata { size: usize, header: Option diff --git a/src/time.rs b/src/time.rs index 719ea8419..0dc1c79e1 100644 --- a/src/time.rs +++ b/src/time.rs @@ -22,6 +22,7 @@ use core::{ops, fmt}; /// * A value less than `0` indicates a time before the starting /// point. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Instant { pub millis: i64, } @@ -135,6 +136,7 @@ impl ops::Sub for Instant { /// A relative amount of time. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Duration { pub millis: u64, } diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 97fcf3677..3bf394749 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -22,6 +22,7 @@ enum_with_unknown! { /// A read/write wrapper around an Address Resolution Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -253,6 +254,7 @@ use crate::wire::{EthernetAddress, Ipv4Address}; /// A high-level representation of an Address Resolution Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr { /// An Ethernet and IPv4 Address Resolution Protocol packet. diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 37f6d9404..a9389431b 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -43,6 +43,7 @@ impl MessageType { /// A representation of a single DHCP option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DhcpOption<'a> { EndOfList, Pad, @@ -200,6 +201,7 @@ impl<'a> DhcpOption<'a> { /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -624,6 +626,7 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// /// The `options` field has a variable length. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this /// packet represents. diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 0c425f18b..f53094258 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -25,6 +25,7 @@ impl fmt::Display for EtherType { /// A six-octet Ethernet II address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Address(pub [u8; 6]); impl Address { @@ -78,6 +79,7 @@ impl fmt::Display for Address { /// A read/write wrapper around an Ethernet II frame buffer. #[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Frame> { buffer: T } @@ -246,6 +248,7 @@ impl> PrettyPrint for Frame { /// A high-level representation of an Internet Protocol version 4 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub src_addr: Address, pub dst_addr: Address, diff --git a/src/wire/icmp.rs b/src/wire/icmp.rs index cf8400c0a..6bbc574cc 100644 --- a/src/wire/icmp.rs +++ b/src/wire/icmp.rs @@ -4,6 +4,7 @@ use crate::wire::icmpv4; use crate::wire::icmpv6; #[derive(Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { #[cfg(feature = "proto-ipv4")] Ipv4(icmpv4::Repr<'a>), diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 955afb1b0..ddabb3f77 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -167,6 +167,7 @@ enum_with_unknown! { /// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -366,6 +367,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Control Message Protocol version 4 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr<'a> { EchoRequest { diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index a347a0311..5ab01db86 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -191,6 +191,7 @@ impl fmt::Display for TimeExceeded { /// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { pub(super) buffer: T } @@ -503,6 +504,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Control Message Protocol version 6 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr<'a> { DstUnreachable { diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 237f32b05..3c6da88a9 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -23,6 +23,7 @@ enum_with_unknown! { /// A read/write wrapper around an Internet Group Management Protocol v1/v2 packet buffer. #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, } @@ -171,6 +172,7 @@ impl + AsMut<[u8]>> Packet { /// A high-level representation of an Internet Group Management Protocol v1/v2 header. #[derive(Debug, PartialEq, Eq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr { MembershipQuery { max_resp_time: Duration, @@ -188,6 +190,7 @@ pub enum Repr { /// Type of IGMP membership report version #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum IgmpVersion { /// IGMPv1 Version1, diff --git a/src/wire/ip.rs b/src/wire/ip.rs index f6f67776e..bdfeee2a2 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -10,6 +10,7 @@ use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Version { Unspecified, @@ -260,6 +261,19 @@ impl fmt::Display for Address { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Address { + fn format(&self, f: defmt::Formatter) { + match self { + &Address::Unspecified => defmt::write!(f, "{:?}", "*"), + #[cfg(feature = "proto-ipv4")] + &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr), + #[cfg(feature = "proto-ipv6")] + &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr), + } + } +} + /// A specification of a CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] @@ -370,6 +384,18 @@ impl fmt::Display for Cidr { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Cidr { + fn format(&self, f: defmt::Formatter) { + match self { + #[cfg(feature = "proto-ipv4")] + &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr), + #[cfg(feature = "proto-ipv6")] + &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr), + } + } +} + /// An internet endpoint address. /// /// An endpoint can be constructed from a port, in which case the address is unspecified. @@ -430,6 +456,13 @@ impl fmt::Display for Endpoint { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Endpoint { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{:?}:{=u16}", self.addr, self.port); + } +} + impl From for Endpoint { fn from(port: u16) -> Endpoint { Endpoint { addr: Address::Unspecified, port } @@ -448,6 +481,7 @@ impl> From<(T, u16)> for Endpoint { /// high-level representation for some IP protocol version, or an unspecified representation, /// which permits the `IpAddress::Unspecified` addresses. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr { Unspecified { diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index a5369edb6..687b4b9ef 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -112,6 +112,13 @@ impl fmt::Display for Address { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Address { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{=u8}.{=u8}.{=u8}.{=u8}", self.0[0], self.0[1], self.0[2], self.0[3]) + } +} + /// A specification of an IPv4 CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] @@ -227,8 +234,16 @@ impl fmt::Display for Cidr { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Cidr { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{:?}/{=u8}", self.address, self.prefix_len); + } +} + /// A read/write wrapper around an Internet Protocol version 4 packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -552,6 +567,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Protocol version 4 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub src_addr: Address, pub dst_addr: Address, diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 731d288d7..6f54a0c10 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -17,6 +17,7 @@ pub const MIN_MTU: usize = 1280; /// A sixteen-octet IPv6 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Address(pub [u8; 16]); impl Address { @@ -267,6 +268,7 @@ impl From for Address { /// A specification of an IPv6 CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Cidr { address: Address, prefix_len: u8, @@ -328,6 +330,7 @@ impl fmt::Display for Cidr { /// A read/write wrapper around an Internet Protocol version 6 packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -593,6 +596,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Protocol version 6 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { /// IPv6 address of the source node. pub src_addr: Address, diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index 8ef385a72..ffa393617 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -7,6 +7,7 @@ pub use super::IpProtocol as Protocol; /// A read/write wrapper around an IPv6 Fragment Header. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T } @@ -158,6 +159,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { /// A high-level representation of an IPv6 Fragment header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { /// The type of header immediately following the Fragment header. pub next_header: Protocol, diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 6327ceaed..dde76aef4 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -6,6 +6,7 @@ pub use super::IpProtocol as Protocol; /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T } @@ -157,6 +158,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { /// A high-level representation of an IPv6 Hop-by-Hop Options header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { /// The type of header immediately following the Hop-by-Hop Options header. pub next_header: Protocol, diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 023f74ffb..adab8f42b 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -58,6 +58,7 @@ impl From for FailureType { /// A read/write wrapper around an IPv6 Extension Header Option. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv6Option> { buffer: T } @@ -214,6 +215,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> { /// A high-level representation of an IPv6 Extension Header Option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr<'a> { Pad1, @@ -278,6 +280,7 @@ impl<'a> Repr<'a> { /// A iterator for IPv6 options. #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv6OptionsIterator<'a> { pos: usize, length: usize, diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index a76d38efa..a00e44bc7 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -51,6 +51,7 @@ impl fmt::Display for Type { /// A read/write wrapper around an IPv6 Routing Header buffer. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T } @@ -389,6 +390,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { /// A high-level representation of an IPv6 Routing Header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr<'a> { Type2 { diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 8fe5e8dc6..eb2a32937 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -166,6 +166,7 @@ impl + AsMut<[u8]>> Packet { /// A read/write wrapper around an MLDv2 Listener Report Message Address Record. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AddressRecord> { buffer: T } @@ -295,6 +296,7 @@ impl + AsMut<[u8]>> AddressRecord { /// A high-level representation of an MLDv2 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { Query { max_resp_code: u16, diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 775f54816..b31cb1c6b 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -10,6 +10,7 @@ use crate::time::Duration; use crate::wire::Ipv6Address; bitflags! { + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RouterFlags: u8 { const MANAGED = 0b10000000; const OTHER = 0b01000000; @@ -17,6 +18,7 @@ bitflags! { } bitflags! { + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NeighborFlags: u8 { const ROUTER = 0b10000000; const SOLICITED = 0b01000000; @@ -189,6 +191,7 @@ impl + AsMut<[u8]>> Packet { /// A high-level representation of an Neighbor Discovery packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { RouterSolicit { lladdr: Option diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 6ea9721ed..b0426bdcd 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -36,6 +36,7 @@ impl fmt::Display for Type { } bitflags! { + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PrefixInfoFlags: u8 { const ON_LINK = 0b10000000; const ADDRCONF = 0b01000000; @@ -46,6 +47,7 @@ bitflags! { /// /// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6 #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NdiscOption> { buffer: T } @@ -393,6 +395,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PrefixInformation { pub prefix_len: u8, pub flags: PrefixInfoFlags, @@ -402,6 +405,7 @@ pub struct PrefixInformation { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RedirectedHeader<'a> { pub header: Ipv6Repr, pub data: &'a [u8] @@ -409,6 +413,7 @@ pub struct RedirectedHeader<'a> { /// A high-level representation of an NDISC Option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { SourceLinkLayerAddr(EthernetAddress), TargetLinkLayerAddr(EthernetAddress), diff --git a/src/wire/pretty_print.rs b/src/wire/pretty_print.rs index 60a6fe4fb..6d900de7c 100644 --- a/src/wire/pretty_print.rs +++ b/src/wire/pretty_print.rs @@ -34,6 +34,7 @@ use core::marker::PhantomData; /// Indentation state. #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PrettyIndent { prefix: &'static str, level: usize diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index faa3abcf3..c0fff417b 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -11,6 +11,7 @@ use crate::wire::ip::checksum; /// A sequence number is a monotonically advancing integer modulo 232. /// Sequence numbers do not have a discontiguity when compared pairwise across a signed overflow. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SeqNumber(pub i32); impl fmt::Display for SeqNumber { @@ -67,6 +68,7 @@ impl cmp::PartialOrd for SeqNumber { /// A read/write wrapper around a Transmission Control Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -550,6 +552,7 @@ impl> AsRef<[u8]> for Packet { /// A representation of a single TCP option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TcpOption<'a> { EndOfList, NoOperation, @@ -702,6 +705,7 @@ impl<'a> TcpOption<'a> { /// The possible control flags of a Transmission Control Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Control { None, Psh, @@ -731,6 +735,7 @@ impl Control { /// A high-level representation of a Transmission Control Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub src_port: u16, pub dst_port: u16, diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 1ce43b8e1..f28b149aa 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -8,6 +8,7 @@ use crate::wire::ip::checksum; /// A read/write wrapper around an User Datagram Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T } @@ -199,6 +200,7 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an User Datagram Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub src_port: u16, pub dst_port: u16, From 2644f89345975e59b24e01deadfc9c6a874f743b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Apr 2021 01:42:17 +0200 Subject: [PATCH 106/566] Add test with defmt feature. --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1f45ad9f..3c499fe66 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,7 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 From 4f1e7b668daf315e88a28c85f5ea0c379a4aff18 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Apr 2021 02:15:35 +0200 Subject: [PATCH 107/566] Document MSRV with the defmt exception, don't test defmt with 1.40 --- .github/workflows/test.yml | 7 ++++++- src/lib.rs | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c499fe66..8de0b1706 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,7 +70,12 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + include: + # defmt doesn't support 1.40 + - rust: stable + features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rust: nightly + features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/src/lib.rs b/src/lib.rs index dd4b1ed63..0fda13b5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,14 @@ //! feature ever defined, to ensure that, when the representation layer is unable to make sense //! of a packet, it is still logged correctly and in full. //! +//! # Minimum Supported Rust Version (MSRV) +//! +//! This crate is guaranteed to compile on stable Rust 1.40 and up with any valid set of features. +//! It *might* compile on older versions but that may change in any new patch release. +//! +//! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which +//! is higher than 1.40. +//! //! [wire]: wire/index.html //! [osi]: https://en.wikipedia.org/wiki/OSI_model //! [berk]: https://en.wikipedia.org/wiki/Berkeley_sockets From 93dc8922cb94c132d99e440a30cb01703bad4d02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 02:13:28 +0200 Subject: [PATCH 108/566] iface: check for ipv4 subnet broadcast addrs everywhere --- src/iface/interface.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 0299f7174..4a1961e1e 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1047,7 +1047,7 @@ impl<'a> InterfaceInner<'a> { let checksum_caps = self.device_capabilities.checksum.clone(); let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?; - if !ipv4_repr.src_addr.is_unicast() { + if !self.is_unicast_v4(ipv4_repr.src_addr) { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); return Err(Error::Malformed) @@ -1062,9 +1062,8 @@ impl<'a> InterfaceInner<'a> { let handled_by_raw_socket = false; if !self.has_ip_addr(ipv4_repr.dst_addr) && - !ipv4_repr.dst_addr.is_broadcast() && !self.has_multicast_group(ipv4_repr.dst_addr) && - !self.is_subnet_broadcast(ipv4_repr.dst_addr) { + !self.is_broadcast_v4(ipv4_repr.dst_addr) { // 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. @@ -1120,6 +1119,18 @@ impl<'a> InterfaceInner<'a> { }) .any(|broadcast_address| address == broadcast_address) } + + /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses + #[cfg(feature = "proto-ipv4")] + fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { + address.is_broadcast() || self.is_subnet_broadcast(address) + } + + /// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses + #[cfg(feature = "proto-ipv4")] + fn is_unicast_v4(&self, address: Ipv4Address) -> bool { + address.is_unicast() && !self.is_subnet_broadcast(address) + } /// Host duties of the **IGMPv2** protocol. /// @@ -1363,10 +1374,10 @@ impl<'a> InterfaceInner<'a> { (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) -> Option> { - if !ipv4_repr.src_addr.is_unicast() { + if !self.is_unicast_v4(ipv4_repr.src_addr) { // Do not send ICMP replies to non-unicast sources None - } else if ipv4_repr.dst_addr.is_unicast() { + } else if self.is_unicast_v4(ipv4_repr.dst_addr) { // Reply as normal when src_addr and dst_addr are both unicast let ipv4_reply_repr = Ipv4Repr { src_addr: ipv4_repr.dst_addr, @@ -1376,7 +1387,7 @@ impl<'a> InterfaceInner<'a> { hop_limit: 64 }; Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) - } else if ipv4_repr.dst_addr.is_broadcast() { + } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { // Only reply to broadcasts for echo replies and not other ICMP messages match icmp_repr { Icmpv4Repr::EchoReply {..} => match self.ipv4_address() { From 5dbeadaf5c3600905056aaaf66b98bb24d0132d0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Apr 2021 03:21:15 +0200 Subject: [PATCH 109/566] dhcp: always send parameter_request_list. Fixes #445. --- src/dhcp/clientv4.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 33514a509..0d1e00313 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -292,7 +292,7 @@ impl Client { requested_ip: None, client_identifier: Some(mac), server_identifier: None, - parameter_request_list: None, + parameter_request_list: Some(PARAMETER_REQUEST_LIST), max_size: Some(raw_socket.payload_recv_capacity() as u16), lease_duration: None, dns_servers: None, @@ -325,7 +325,6 @@ impl Client { dhcp_repr.broadcast = false; dhcp_repr.requested_ip = Some(r_state.requested_ip); dhcp_repr.server_identifier = Some(r_state.server_identifier); - dhcp_repr.parameter_request_list = Some(PARAMETER_REQUEST_LIST); net_trace!("DHCP send request to {} = {:?}", endpoint, dhcp_repr); send_packet(iface, endpoint, dhcp_repr) } From a7d519240ca22945c73b194d4de169f9eb48d96e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Apr 2021 19:49:39 +0200 Subject: [PATCH 110/566] dhcp: Clear expiration time on reset. --- src/dhcp/clientv4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 0d1e00313..a45487057 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -356,6 +356,7 @@ impl Client { net_trace!("DHCP reset"); self.state = ClientState::Discovering; self.next_egress = now; + self.lease_expiration = None; } } From 17cf2c8d478f7b4020d3c2cb12dedf36af7547a5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 02:37:58 +0200 Subject: [PATCH 111/566] phy: fix FaultInjector returning a too big buffer when simulating a drop on tx --- src/phy/fault_injector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 3185f477a..8c2e4aa27 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -297,7 +297,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { }; if drop { - return f(&mut self.junk); + return f(&mut self.junk[..len]); } let Self { token, state, config, .. } = self; From 26dcb555590473205a04cef23fa15319bb5e3caf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 2 Apr 2021 00:14:30 +0200 Subject: [PATCH 112/566] udp: do not include payload in UdpRepr This makes UdpRepr work like IpRepr, where it only emits the header, and the user must emit the payload. This makes it easier to emit UDP packets with payloads that come from protocol-specific reprs, like DHCP and in the future DNS. --- src/dhcp/clientv4.rs | 20 +++----- src/iface/interface.rs | 111 +++++++++++++++++++++++++---------------- src/socket/icmp.rs | 29 ++++++++--- src/socket/udp.rs | 46 ++++++++--------- src/wire/ip.rs | 2 +- src/wire/udp.rs | 32 ++++++------ 6 files changed, 135 insertions(+), 105 deletions(-) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index a45487057..167941321 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -1,9 +1,9 @@ -use crate::{Result, Error}; +use crate::{Error, Result}; use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, Ipv4Cidr, Ipv4Address, Ipv4Packet, Ipv4Repr, UdpPacket, UdpRepr, DhcpPacket, DhcpRepr, DhcpMessageType}; -use crate::wire::dhcpv4::field as dhcpv4_field; +use crate::wire::dhcpv4::{field as dhcpv4_field, Packet as Dhcpv4Packet}; use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; use crate::phy::{Device, ChecksumCapabilities}; use crate::iface::Interface; @@ -361,18 +361,10 @@ impl Client { } fn send_packet Device<'d>>(iface: &mut Interface, raw_socket: &mut RawSocket, endpoint: &IpEndpoint, dhcp_repr: &DhcpRepr, checksum_caps: &ChecksumCapabilities) -> Result<()> { - let mut dhcp_payload_buf = [0; 320]; - assert!(dhcp_repr.buffer_len() <= dhcp_payload_buf.len()); - let dhcp_payload = &mut dhcp_payload_buf[0..dhcp_repr.buffer_len()]; - { - let mut dhcp_packet = DhcpPacket::new_checked(&mut dhcp_payload[..])?; - dhcp_repr.emit(&mut dhcp_packet)?; - } let udp_repr = UdpRepr { src_port: UDP_CLIENT_PORT, dst_port: endpoint.port, - payload: dhcp_payload, }; let src_addr = iface.ipv4_addr().unwrap(); @@ -384,12 +376,12 @@ fn send_packet Device<'d>>(iface: &mut Interface, raw_ src_addr, dst_addr, protocol: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + dhcp_repr.buffer_len(), hop_limit: 64, }; let mut packet = raw_socket.send( - ipv4_repr.buffer_len() + udp_repr.buffer_len() + ipv4_repr.buffer_len() + udp_repr.header_len() + dhcp_repr.buffer_len() )?; { let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut packet); @@ -401,6 +393,8 @@ fn send_packet Device<'d>>(iface: &mut Interface, raw_ ); udp_repr.emit(&mut udp_packet, &src_addr.into(), &dst_addr.into(), + dhcp_repr.buffer_len(), + |buf| dhcp_repr.emit(&mut Dhcpv4Packet::new_unchecked(buf)).unwrap(), checksum_caps); } Ok(()) @@ -423,6 +417,6 @@ fn parse_udp<'a>(data: &'a [u8], checksum_caps: &ChecksumCapabilities) -> Result addr: ipv4_repr.dst_addr.into(), port: udp_repr.dst_port, }; - let data = udp_repr.payload; + let data = udp_packet.payload(); Ok((src, dst, data)) } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 4a1961e1e..d569067ba 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -263,7 +263,7 @@ pub(crate) enum IpPacket<'a> { #[cfg(feature = "socket-raw")] Raw((IpRepr, &'a [u8])), #[cfg(feature = "socket-udp")] - Udp((IpRepr, UdpRepr<'a>)), + Udp((IpRepr, UdpRepr, &'a [u8])), #[cfg(feature = "socket-tcp")] Tcp((IpRepr, TcpRepr<'a>)) } @@ -280,7 +280,7 @@ impl<'a> IpPacket<'a> { #[cfg(feature = "socket-raw")] IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), #[cfg(feature = "socket-udp")] - IpPacket::Udp((ip_repr, _)) => ip_repr.clone(), + IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), } @@ -289,7 +289,7 @@ impl<'a> IpPacket<'a> { pub(crate) fn emit_payload(&self, _ip_repr: IpRepr, payload: &mut [u8], caps: &DeviceCapabilities) { match self { #[cfg(feature = "proto-ipv4")] - IpPacket::Icmpv4((_, icmpv4_repr)) => + IpPacket::Icmpv4((_, icmpv4_repr)) => icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum), #[cfg(feature = "proto-igmp")] IpPacket::Igmp((_, igmp_repr)) => @@ -302,9 +302,11 @@ impl<'a> IpPacket<'a> { IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udp_repr)) => + IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit(&mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), &_ip_repr.dst_addr(), &caps.checksum), + &_ip_repr.src_addr(), &_ip_repr.dst_addr(), + inner_payload.len(), |buf| buf.copy_from_slice(inner_payload), + &caps.checksum), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((_, mut tcp_repr)) => { // This is a terrible hack to make TCP performance more acceptable on systems @@ -880,7 +882,7 @@ impl<'a> InterfaceInner<'a> { self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), timestamp); } } - + self.process_ipv6(sockets, timestamp, &ipv6_packet).map(|o| o.map(EthernetPacket::Ip)) } // Drop all other traffic. @@ -1439,11 +1441,12 @@ impl<'a> InterfaceInner<'a> { 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_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) { + match udp_socket.process(&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. @@ -1787,6 +1790,13 @@ mod test { use crate::socket::SocketSet; use crate::phy::{Loopback, ChecksumCapabilities}; + #[allow(unused)] + fn fill_slice(s: &mut [u8], val: u8) { + for x in s.iter_mut() { + *x = val + } + } + fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { #[cfg(feature = "medium-ethernet")] return create_loopback_ethernet(); @@ -2035,20 +2045,21 @@ mod test { let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &UDP_PAYLOAD }; let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), protocol: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64 }); // Emit the representations to a packet udp_repr.emit(&mut packet_unicast, &ip_repr.src_addr(), - &ip_repr.dst_addr(), &ChecksumCapabilities::default()); + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), + &ChecksumCapabilities::default()); let data = packet_unicast.into_inner(); @@ -2060,7 +2071,7 @@ mod test { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), protocol: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64 }, data: &data @@ -2085,13 +2096,14 @@ mod test { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address::BROADCAST, protocol: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64 }); // Emit the representations to a packet udp_repr.emit(&mut packet_broadcast, &ip_repr.src_addr(), &IpAddress::Ipv4(Ipv4Address::BROADCAST), + UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), &ChecksumCapabilities::default()); // Ensure that the port unreachable error does not trigger an @@ -2129,7 +2141,6 @@ mod test { let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &UDP_PAYLOAD }; #[cfg(feature = "proto-ipv6")] @@ -2137,7 +2148,7 @@ mod test { src_addr: src_ip, dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, next_header: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 0x40 }); #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] @@ -2145,7 +2156,7 @@ mod test { src_addr: src_ip, dst_addr: Ipv4Address::BROADCAST, protocol: IpProtocol::Udp, - payload_len: udp_repr.buffer_len(), + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 0x40 }); @@ -2158,6 +2169,7 @@ mod test { } udp_repr.emit(&mut packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), &ChecksumCapabilities::default()); // Packet should be handled by bound UDP socket @@ -2260,18 +2272,19 @@ mod test { let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &[0x2a; MAX_PAYLOAD_LEN] }; - let mut bytes = vec![0xff; udp_repr.buffer_len()]; + let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); + udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), + MAX_PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default()); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let ip_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.buffer_len() + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN }; #[cfg(feature = "proto-ipv6")] let ip_repr = Ipv6Repr { @@ -2279,7 +2292,7 @@ mod test { dst_addr: dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.buffer_len() + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN }; let payload = packet.into_inner(); @@ -2619,7 +2632,7 @@ mod test { recv_all(&mut iface, timestamp) .iter() .filter_map(|frame| { - + let ipv4_packet = match caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { @@ -2666,11 +2679,11 @@ mod test { // General query let timestamp = Instant::now(); const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, - 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, - 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, - 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, + 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, + 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, + 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]; { @@ -2722,25 +2735,28 @@ mod test { let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); + const PAYLOAD_LEN: usize = 10; + let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &[0x2a; 10] }; - let mut bytes = vec![0xff; udp_repr.buffer_len()]; + let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); + udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), + PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default()); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.buffer_len() + payload_len: udp_repr.header_len() + PAYLOAD_LEN }; // Emit to frame let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.buffer_len() + ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN ]; let frame = { ipv4_repr.emit( @@ -2751,6 +2767,7 @@ mod test { &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), + PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default()); Ipv4Packet::new_unchecked(&bytes) }; @@ -2776,25 +2793,28 @@ mod test { let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); + const PAYLOAD_LEN: usize = 49; // 49 > 48, hence packet will be truncated + let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &[0x2a; 49] // 49 > 48, hence packet will be truncated }; - let mut bytes = vec![0xff; udp_repr.buffer_len()]; + let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); + udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), + PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default()); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.buffer_len() + payload_len: udp_repr.header_len() + PAYLOAD_LEN }; // Emit to frame let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.buffer_len() + ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN ]; let frame = { ipv4_repr.emit( @@ -2802,9 +2822,10 @@ mod test { &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), + PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default()); Ipv4Packet::new_unchecked(&bytes) }; @@ -2812,7 +2833,7 @@ mod test { let frame = iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame); // because the packet could not be handled we should send an Icmp message - assert!(match frame { + assert!(match frame { Ok(Some(IpPacket::Icmpv4(_))) => true, _ => false, }); @@ -2853,22 +2874,23 @@ mod test { let udp_repr = UdpRepr { src_port: 67, dst_port: 68, - payload: &UDP_PAYLOAD }; - let mut bytes = vec![0xff; udp_repr.buffer_len()]; + let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); + udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), + UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), + &ChecksumCapabilities::default()); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.buffer_len() + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len() }; // Emit to frame let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.buffer_len() + ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len() ]; let frame = { ipv4_repr.emit( @@ -2876,9 +2898,10 @@ mod test { &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), + UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), &ChecksumCapabilities::default()); Ipv4Packet::new_unchecked(&bytes) }; diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index e5779bddf..9208ca7c5 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -94,12 +94,12 @@ impl<'a> IcmpSocket<'a> { /// /// The waker is woken on state changes that might affect the return value /// of `recv` method calls, such as receiving data, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has /// necessarily changed. #[cfg(feature = "async")] @@ -112,12 +112,12 @@ impl<'a> IcmpSocket<'a> { /// The waker is woken on state changes that might affect the return value /// of `send` method calls, such as space becoming available in the transmit /// buffer, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has /// necessarily changed. #[cfg(feature = "async")] @@ -440,7 +440,7 @@ impl<'a> IcmpSocket<'a> { _ => Err(Error::Unaddressable) } })?; - + #[cfg(feature = "async")] self.tx_waker.wake(); @@ -482,8 +482,9 @@ mod tests_common { pub static UDP_REPR: UdpRepr = UdpRepr { src_port: 53, dst_port: 9090, - payload: &[0xff; 10] }; + + pub static UDP_PAYLOAD: &[u8] = &[0xff; 10]; } #[cfg(all(test, feature = "proto-ipv4"))] @@ -644,7 +645,13 @@ mod test_ipv4 { let mut bytes = [0xff; 18]; let mut packet = UdpPacket::new_unchecked(&mut bytes); - UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &checksum); + UDP_REPR.emit( + &mut packet, + &REMOTE_IPV4.into(), + &LOCAL_IPV4.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(UDP_PAYLOAD), + &checksum); let data = &packet.into_inner()[..]; @@ -843,7 +850,13 @@ mod test_ipv6 { let mut bytes = [0xff; 18]; let mut packet = UdpPacket::new_unchecked(&mut bytes); - UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &checksum); + UDP_REPR.emit( + &mut packet, + &REMOTE_IPV6.into(), + &LOCAL_IPV6.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(UDP_PAYLOAD), + &checksum); let data = &packet.into_inner()[..]; diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 88f5be4ca..f62d94f71 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -54,12 +54,12 @@ impl<'a> UdpSocket<'a> { /// /// The waker is woken on state changes that might affect the return value /// of `recv` method calls, such as receiving data, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has /// necessarily changed. #[cfg(feature = "async")] @@ -72,12 +72,12 @@ impl<'a> UdpSocket<'a> { /// The waker is woken on state changes that might affect the return value /// of `send` method calls, such as space becoming available in the transmit /// buffer, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has /// necessarily changed. #[cfg(feature = "async")] @@ -277,13 +277,13 @@ impl<'a> UdpSocket<'a> { true } - pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr) -> Result<()> { + pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8]) -> Result<()> { debug_assert!(self.accepts(ip_repr, repr)); - let size = repr.payload.len(); + let size = payload.len(); let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port }; - self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(repr.payload); + self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(payload); net_trace!("{}:{}:{}: receiving {} octets", self.meta.handle, self.endpoint, @@ -296,7 +296,7 @@ impl<'a> UdpSocket<'a> { } pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> - where F: FnOnce((IpRepr, UdpRepr)) -> Result<()> { + where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> { let handle = self.handle(); let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); @@ -309,16 +309,15 @@ impl<'a> UdpSocket<'a> { let repr = UdpRepr { src_port: endpoint.port, dst_port: remote_endpoint.port, - payload: payload_buf, }; let ip_repr = IpRepr::Unspecified { src_addr: endpoint.addr, dst_addr: remote_endpoint.addr, protocol: IpProtocol::Udp, - payload_len: repr.buffer_len(), + payload_len: repr.header_len() + payload_buf.len(), hop_limit: hop_limit, }; - emit((ip_repr, repr)) + emit((ip_repr, repr, payload_buf)) })?; #[cfg(feature = "async")] @@ -379,15 +378,15 @@ mod test { const LOCAL_UDP_REPR: UdpRepr = UdpRepr { src_port: LOCAL_PORT, dst_port: REMOTE_PORT, - payload: b"abcdef" }; const REMOTE_UDP_REPR: UdpRepr = UdpRepr { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, - payload: b"abcdef" }; + const PAYLOAD: &[u8] = b"abcdef"; + fn remote_ip_repr() -> IpRepr { match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_1) { #[cfg(feature = "proto-ipv4")] @@ -457,16 +456,18 @@ mod test { assert_eq!(socket.send_slice(b"123456", REMOTE_END), Err(Error::Exhausted)); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(|(ip_repr, udp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); + assert_eq!(payload, PAYLOAD); Err(Error::Unaddressable) }), Err(Error::Unaddressable)); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(|(ip_repr, udp_repr)| { + assert_eq!(socket.dispatch(|(ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); + assert_eq!(payload, PAYLOAD); Ok(()) }), Ok(())); assert!(socket.can_send()); @@ -481,12 +482,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), + assert_eq!(socket.process(&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), + assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Err(Error::Exhausted)); assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); assert!(!socket.can_recv()); @@ -499,7 +500,7 @@ mod test { assert_eq!(socket.peek(), Err(Error::Exhausted)); - assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR), + assert_eq!(socket.process(&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))); @@ -512,7 +513,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), + assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(())); let mut slice = [0; 4]; @@ -525,7 +526,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), + assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(())); let mut slice = [0; 4]; @@ -543,7 +544,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(|(ip_repr, _, _)| { assert_eq!(ip_repr, IpRepr::Unspecified{ src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, @@ -619,9 +620,8 @@ mod test { let repr = UdpRepr { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, - payload: &[] }; - assert_eq!(socket.process(&remote_ip_repr(), &repr), Ok(())); + assert_eq!(socket.process(&remote_ip_repr(), &repr, &[]), Ok(())); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index bdfeee2a2..67a25e320 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -887,7 +887,7 @@ pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &m &repr.dst_addr(), &checksum_caps) { Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err), Ok(udp_repr) => { - write!(f, "{}{}", indent, udp_repr)?; + write!(f, "{}{} len={}", indent, udp_repr, udp_packet.payload().len())?; let valid = udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); format_checksum(f, valid) diff --git a/src/wire/udp.rs b/src/wire/udp.rs index f28b149aa..ef60ed200 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -201,16 +201,15 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an User Datagram Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Repr<'a> { +pub struct Repr { pub src_port: u16, pub dst_port: u16, - pub payload: &'a [u8] } -impl<'a> Repr<'a> { +impl Repr { /// Parse an User Datagram Protocol packet and return a high-level representation. - pub fn parse(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress, - checksum_caps: &ChecksumCapabilities) -> Result> + pub fn parse(packet: &Packet<&T>, src_addr: &IpAddress, dst_addr: &IpAddress, + checksum_caps: &ChecksumCapabilities) -> Result where T: AsRef<[u8]> + ?Sized { // Destination port cannot be omitted (but source port can be). if packet.dst_port() == 0 { return Err(Error::Malformed) } @@ -230,25 +229,26 @@ impl<'a> Repr<'a> { Ok(Repr { src_port: packet.src_port(), dst_port: packet.dst_port(), - payload: packet.payload() }) } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { - field::CHECKSUM.end + self.payload.len() + pub fn header_len(&self) -> usize { + field::CHECKSUM.end } /// Emit a high-level representation into an User Datagram Protocol packet. pub fn emit(&self, packet: &mut Packet<&mut T>, src_addr: &IpAddress, dst_addr: &IpAddress, + payload_len: usize, + emit_payload: impl FnOnce(&mut [u8]), checksum_caps: &ChecksumCapabilities) where T: AsRef<[u8]> + AsMut<[u8]> { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); - packet.set_len((field::CHECKSUM.end + self.payload.len()) as u16); - packet.payload_mut().copy_from_slice(self.payload); + packet.set_len((field::CHECKSUM.end + payload_len) as u16); + emit_payload(packet.payload_mut()); if checksum_caps.udp.tx() { packet.fill_checksum(src_addr, dst_addr) @@ -268,10 +268,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { } } -impl<'a> fmt::Display for Repr<'a> { +impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UDP src={} dst={} len={}", - self.src_port, self.dst_port, self.payload.len()) + write!(f, "UDP src={} dst={}", self.src_port, self.dst_port) } } @@ -361,11 +360,10 @@ mod test { } #[cfg(feature = "proto-ipv4")] - fn packet_repr() -> Repr<'static> { + fn packet_repr() -> Repr { Repr { src_port: 48896, dst_port: 53, - payload: &PAYLOAD_BYTES } } @@ -382,9 +380,11 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_emit() { let repr = packet_repr(); - let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), + PAYLOAD_BYTES.len(), + |payload| payload.copy_from_slice(&PAYLOAD_BYTES), &ChecksumCapabilities::default()); assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); } From 22e4c65f5801aa93847567395b4a69d15af1fb19 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 00:47:41 +0200 Subject: [PATCH 113/566] route: add remove_default_ipvX_route --- src/iface/route.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/iface/route.rs b/src/iface/route.rs index 65e255be3..e37c6a129 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -106,6 +106,24 @@ impl<'a> Routes<'a> { } } + /// Remove the default ipv4 gateway + /// + /// On success, returns the previous default route, if any. + #[cfg(feature = "proto-ipv4")] + pub fn remove_default_ipv4_route(&mut self) -> Option { + let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); + self.storage.remove(&cidr) + } + + /// Remove the default ipv6 gateway + /// + /// On success, returns the previous default route, if any. + #[cfg(feature = "proto-ipv6")] + pub fn remove_default_ipv6_route(&mut self) -> Option { + let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0); + self.storage.remove(&cidr) + } + pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option { assert!(addr.is_unicast()); From b791ef535ebfa1c8027baa278c7d573802bf582f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 01:31:53 +0200 Subject: [PATCH 114/566] dhcp: convert to socket --- Cargo.toml | 5 +- examples/dhcp_client.rs | 101 +++++----- src/dhcp/clientv4.rs | 422 ---------------------------------------- src/dhcp/mod.rs | 5 - src/iface/interface.rs | 62 +++++- src/lib.rs | 2 - src/socket/dhcpv4.rs | 422 ++++++++++++++++++++++++++++++++++++++++ src/socket/mod.rs | 11 ++ src/socket/ref_.rs | 18 +- src/socket/set.rs | 3 + src/wire/dhcpv4.rs | 3 + src/wire/mod.rs | 7 +- src/wire/udp.rs | 2 + 13 files changed, 554 insertions(+), 509 deletions(-) delete mode 100644 src/dhcp/clientv4.rs delete mode 100644 src/dhcp/mod.rs create mode 100644 src/socket/dhcpv4.rs diff --git a/Cargo.toml b/Cargo.toml index ae471652c..c0d2166b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,13 +39,14 @@ verbose = [] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "proto-ipv4" = [] "proto-igmp" = ["proto-ipv4"] -"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "medium-ethernet"] +"proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] "socket" = [] "socket-raw" = ["socket"] "socket-udp" = ["socket"] "socket-tcp" = ["socket"] "socket-icmp" = ["socket"] +"socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"] "async" = [] defmt-trace = [] @@ -59,7 +60,7 @@ default = [ "medium-ethernet", "medium-ip", "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", + "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "async" ] diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 81d685f26..06b308097 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -3,12 +3,13 @@ mod utils; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; +use log::*; + use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; -use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes}; +use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event}; use smoltcp::time::Instant; -use smoltcp::dhcp::Dhcpv4Client; fn main() { #[cfg(feature = "log")] @@ -41,65 +42,53 @@ fn main() { let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); - let dhcp_rx_buffer = RawSocketBuffer::new( - [RawPacketMetadata::EMPTY; 1], - vec![0; 900] - ); - let dhcp_tx_buffer = RawSocketBuffer::new( - [RawPacketMetadata::EMPTY; 1], - vec![0; 600] - ); - let mut dhcp = Dhcpv4Client::new(&mut sockets, dhcp_rx_buffer, dhcp_tx_buffer, Instant::now()); - let mut prev_cidr = Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0); + let dhcp_handle = sockets.add(Dhcpv4Socket::new()); + loop { let timestamp = Instant::now(); - iface.poll(&mut sockets, timestamp) - .map(|_| ()) - .unwrap_or_else(|e| println!("Poll: {:?}", e)); - let config = dhcp.poll(&mut iface, &mut sockets, timestamp) - .unwrap_or_else(|e| { - println!("DHCP: {:?}", e); - None - }); - config.map(|config| { - println!("DHCP config: {:?}", config); - if let Some(cidr) = config.address { - if cidr != prev_cidr { - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next() - .map(|addr| { - *addr = IpCidr::Ipv4(cidr); - }); - }); - prev_cidr = cidr; - println!("Assigned a new IPv4 address: {}", cidr); + if let Err(e) = iface.poll(&mut sockets, timestamp) { + debug!("poll error: {}", e); + } + + match sockets.get::(dhcp_handle).poll() { + Dhcpv4Event::NoChange => {} + Dhcpv4Event::Configured(config) => { + debug!("DHCP config acquired!"); + + debug!("IP address: {}", config.address); + set_ipv4_addr(&mut iface, config.address); + + if let Some(router) = config.router { + debug!("Default gateway: {}", router); + iface.routes_mut().add_default_ipv4_route(router).unwrap(); + } else { + debug!("Default gateway: None"); + iface.routes_mut().remove_default_ipv4_route(); } - } - config.router.map(|router| iface.routes_mut() - .add_default_ipv4_route(router) - .unwrap() - ); - iface.routes_mut() - .update(|routes_map| { - routes_map.get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) - .map(|default_route| { - println!("Default gateway: {}", default_route.via_router); - }); - }); - - if config.dns_servers.iter().any(|s| s.is_some()) { - println!("DNS servers:"); - for dns_server in config.dns_servers.iter().filter_map(|s| *s) { - println!("- {}", dns_server); + for (i, s) in config.dns_servers.iter().enumerate() { + if let Some(s) = s { + debug!("DNS server {}: {}", i, s); + } } } - }); + Dhcpv4Event::Deconfigured => { + debug!("DHCP lost config!"); + set_ipv4_addr(&mut iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + iface.routes_mut().remove_default_ipv4_route(); + } + } - let mut timeout = dhcp.next_poll(timestamp); - iface.poll_delay(&sockets, timestamp) - .map(|sockets_timeout| timeout = sockets_timeout); - phy_wait(fd, Some(timeout)) - .unwrap_or_else(|e| println!("Wait: {:?}", e)); + phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); } } + +fn set_ipv4_addr(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr) + where DeviceT: for<'d> Device<'d> +{ + iface.update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next().unwrap(); + *dest = IpCidr::Ipv4(cidr); + }); +} + diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs deleted file mode 100644 index 167941321..000000000 --- a/src/dhcp/clientv4.rs +++ /dev/null @@ -1,422 +0,0 @@ -use crate::{Error, Result}; -use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, - Ipv4Cidr, Ipv4Address, Ipv4Packet, Ipv4Repr, - UdpPacket, UdpRepr, - DhcpPacket, DhcpRepr, DhcpMessageType}; -use crate::wire::dhcpv4::{field as dhcpv4_field, Packet as Dhcpv4Packet}; -use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; -use crate::phy::{Device, ChecksumCapabilities}; -use crate::iface::Interface; -use crate::time::{Instant, Duration}; -use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT}; - -const DISCOVER_TIMEOUT: u64 = 10; -const REQUEST_TIMEOUT: u64 = 1; -const REQUEST_RETRIES: u16 = 15; -const DEFAULT_RENEW_INTERVAL: u32 = 60; -const PARAMETER_REQUEST_LIST: &[u8] = &[ - dhcpv4_field::OPT_SUBNET_MASK, - dhcpv4_field::OPT_ROUTER, - dhcpv4_field::OPT_DOMAIN_NAME_SERVER, -]; - -/// IPv4 configuration data returned by `client.poll()` -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Config { - pub address: Option, - pub router: Option, - pub dns_servers: [Option; 3], -} - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct RequestState { - retry: u16, - endpoint_ip: Ipv4Address, - server_identifier: Ipv4Address, - requested_ip: Ipv4Address, -} - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct RenewState { - endpoint_ip: Ipv4Address, - server_identifier: Ipv4Address, -} - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum ClientState { - /// Discovering the DHCP server - Discovering, - /// Requesting an address - Requesting(RequestState), - /// Having an address, refresh it periodically - Renew(RenewState), -} - -pub struct Client { - state: ClientState, - raw_handle: SocketHandle, - /// When to send next request - next_egress: Instant, - /// When any existing DHCP address will expire. - lease_expiration: Option, - transaction_id: u32, -} - -/// DHCP client with a RawSocket. -/// -/// To provide memory for the dynamic IP address, configure your -/// `Interface` with one of `ip_addrs` and the `ipv4_gateway` being -/// `Ipv4Address::UNSPECIFIED`. You must also assign this `0.0.0.0/0` -/// while the client's state is `Discovering`. Hence, the `poll()` -/// method returns a corresponding `Config` struct in this case. -/// -/// You must call `dhcp_client.poll()` after `iface.poll()` to send -/// and receive DHCP packets. -impl Client { - /// # Usage - /// ```rust - /// use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; - /// use smoltcp::dhcp::Dhcpv4Client; - /// use smoltcp::time::Instant; - /// - /// let mut sockets = SocketSet::new(vec![]); - /// let dhcp_rx_buffer = RawSocketBuffer::new( - /// [RawPacketMetadata::EMPTY; 1], - /// vec![0; 600] - /// ); - /// let dhcp_tx_buffer = RawSocketBuffer::new( - /// [RawPacketMetadata::EMPTY; 1], - /// vec![0; 600] - /// ); - /// let mut dhcp = Dhcpv4Client::new( - /// &mut sockets, - /// dhcp_rx_buffer, dhcp_tx_buffer, - /// Instant::now() - /// ); - /// ``` - pub fn new<'a>(sockets: &mut SocketSet<'a>, rx_buffer: RawSocketBuffer<'a>, tx_buffer: RawSocketBuffer<'a>, now: Instant) -> Self - { - let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - let raw_handle = sockets.add(raw_socket); - - Client { - state: ClientState::Discovering, - raw_handle, - next_egress: now, - transaction_id: 1, - lease_expiration: None, - } - } - - /// When to send next packet - /// - /// Useful for suspending execution after polling. - pub fn next_poll(&self, now: Instant) -> Duration { - self.next_egress - now - } - - /// Process incoming packets on the contained RawSocket, and send - /// DHCP requests when timeouts are ready. - /// - /// Applying the obtained network configuration is left to the - /// user. - /// - /// A Config can be returned from any valid DHCP reply. The client - /// performs no bookkeeping on configuration or their changes. - pub fn poll(&mut self, - iface: &mut Interface, sockets: &mut SocketSet, - now: Instant - ) -> Result> - where - DeviceT: for<'d> Device<'d>, - { - let checksum_caps = iface.device().capabilities().checksum; - let mut raw_socket = sockets.get::(self.raw_handle); - - // Process incoming - let config = { - match raw_socket.recv() - .and_then(|packet| parse_udp(packet, &checksum_caps)) { - Ok((IpEndpoint { - addr: IpAddress::Ipv4(src_ip), - port: UDP_SERVER_PORT, - }, IpEndpoint { - addr: _, - port: UDP_CLIENT_PORT, - }, payload)) => - self.ingress(iface, now, payload, &src_ip), - Ok(_) => - return Err(Error::Unrecognized), - Err(Error::Exhausted) => - None, - Err(e) => - return Err(e), - } - }; - - if config.is_some() { - // Return a new config immediately so that addresses can - // be configured that are required by egress(). - Ok(config) - } else { - // Send requests - if raw_socket.can_send() && now >= self.next_egress { - self.egress(iface, &mut *raw_socket, &checksum_caps, now) - } else { - Ok(None) - } - } - } - - fn ingress(&mut self, - iface: &mut Interface, now: Instant, - data: &[u8], src_ip: &Ipv4Address - ) -> Option - where - DeviceT: for<'d> Device<'d>, - { - let dhcp_packet = match DhcpPacket::new_checked(data) { - Ok(dhcp_packet) => dhcp_packet, - Err(e) => { - net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e); - return None; - } - }; - let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) { - Ok(dhcp_repr) => dhcp_repr, - Err(e) => { - net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e); - return None; - } - }; - let mac = iface.ethernet_addr(); - if dhcp_repr.client_hardware_address != mac { return None } - if dhcp_repr.transaction_id != self.transaction_id { return None } - let server_identifier = match dhcp_repr.server_identifier { - Some(server_identifier) => server_identifier, - None => return None, - }; - net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); - - // once we receive the ack, we can pass the config to the user - let config = if dhcp_repr.message_type == DhcpMessageType::Ack { - let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2); - self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into())); - - // RFC 2131 indicates clients should renew a lease halfway through its expiration. - self.next_egress = now + Duration::from_secs((lease_duration / 2).into()); - - let address = dhcp_repr.subnet_mask - .and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len()) - .map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len)); - let router = dhcp_repr.router; - let dns_servers = dhcp_repr.dns_servers - .unwrap_or([None; 3]); - Some(Config { address, router, dns_servers }) - } else { - None - }; - - match self.state { - ClientState::Discovering - if dhcp_repr.message_type == DhcpMessageType::Offer => - { - self.next_egress = now; - let r_state = RequestState { - retry: 0, - endpoint_ip: *src_ip, - server_identifier, - requested_ip: dhcp_repr.your_ip // use the offered ip - }; - Some(ClientState::Requesting(r_state)) - } - ClientState::Requesting(ref r_state) - if dhcp_repr.message_type == DhcpMessageType::Ack && - server_identifier == r_state.server_identifier => - { - let p_state = RenewState { - endpoint_ip: *src_ip, - server_identifier, - }; - Some(ClientState::Renew(p_state)) - } - _ => None - }.map(|new_state| self.state = new_state); - - config - } - - fn egress Device<'d>>(&mut self, iface: &mut Interface, raw_socket: &mut RawSocket, checksum_caps: &ChecksumCapabilities, now: Instant) -> Result> { - // Reset after maximum amount of retries - let retries_exceeded = match self.state { - ClientState::Requesting(ref mut r_state) if r_state.retry >= REQUEST_RETRIES => { - net_debug!("DHCP request retries exceeded, restarting discovery"); - true - } - _ => false - }; - - let lease_expired = self.lease_expiration.map_or(false, |expiration| now >= expiration); - - if lease_expired || retries_exceeded { - self.reset(now); - // Return a config now so that user code assigns the - // 0.0.0.0/0 address, which will be used sending a DHCP - // discovery packet in the next call to egress(). - return Ok(Some(Config { - address: Some(Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)), - router: None, - dns_servers: [None; 3], - })); - } - - // Prepare sending next packet - self.transaction_id += 1; - let mac = iface.ethernet_addr(); - - let mut dhcp_repr = DhcpRepr { - message_type: DhcpMessageType::Discover, - transaction_id: self.transaction_id, - client_hardware_address: mac, - client_ip: Ipv4Address::UNSPECIFIED, - your_ip: Ipv4Address::UNSPECIFIED, - server_ip: Ipv4Address::UNSPECIFIED, - router: None, - subnet_mask: None, - relay_agent_ip: Ipv4Address::UNSPECIFIED, - broadcast: true, - requested_ip: None, - client_identifier: Some(mac), - server_identifier: None, - parameter_request_list: Some(PARAMETER_REQUEST_LIST), - max_size: Some(raw_socket.payload_recv_capacity() as u16), - lease_duration: None, - dns_servers: None, - }; - let mut send_packet = |iface, endpoint, dhcp_repr| { - send_packet(iface, raw_socket, &endpoint, &dhcp_repr, checksum_caps) - .map(|()| None) - }; - - - match self.state { - ClientState::Discovering => { - self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT); - let endpoint = IpEndpoint { - addr: Ipv4Address::BROADCAST.into(), - port: UDP_SERVER_PORT, - }; - net_trace!("DHCP send discover to {}: {:?}", endpoint, dhcp_repr); - send_packet(iface, endpoint, dhcp_repr) - } - ClientState::Requesting(ref mut r_state) => { - r_state.retry += 1; - self.next_egress = now + Duration::from_secs(REQUEST_TIMEOUT); - - let endpoint = IpEndpoint { - addr: Ipv4Address::BROADCAST.into(), - port: UDP_SERVER_PORT, - }; - dhcp_repr.message_type = DhcpMessageType::Request; - dhcp_repr.broadcast = false; - dhcp_repr.requested_ip = Some(r_state.requested_ip); - dhcp_repr.server_identifier = Some(r_state.server_identifier); - net_trace!("DHCP send request to {} = {:?}", endpoint, dhcp_repr); - send_packet(iface, endpoint, dhcp_repr) - } - ClientState::Renew(ref mut p_state) => { - self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into()); - - let endpoint = IpEndpoint { - addr: p_state.endpoint_ip.into(), - port: UDP_SERVER_PORT, - }; - let client_ip = iface.ipv4_addr().unwrap_or(Ipv4Address::UNSPECIFIED); - dhcp_repr.message_type = DhcpMessageType::Request; - dhcp_repr.client_ip = client_ip; - dhcp_repr.broadcast = false; - net_trace!("DHCP send renew to {}: {:?}", endpoint, dhcp_repr); - send_packet(iface, endpoint, dhcp_repr) - } - } - } - - /// Reset state and restart discovery phase. - /// - /// Use this to speed up acquisition of an address in a new - /// network if a link was down and it is now back up. - /// - /// You *must* configure a `0.0.0.0` address on your interface - /// before the next call to `poll()`! - pub fn reset(&mut self, now: Instant) { - net_trace!("DHCP reset"); - self.state = ClientState::Discovering; - self.next_egress = now; - self.lease_expiration = None; - } -} - -fn send_packet Device<'d>>(iface: &mut Interface, raw_socket: &mut RawSocket, endpoint: &IpEndpoint, dhcp_repr: &DhcpRepr, checksum_caps: &ChecksumCapabilities) -> Result<()> { - - let udp_repr = UdpRepr { - src_port: UDP_CLIENT_PORT, - dst_port: endpoint.port, - }; - - let src_addr = iface.ipv4_addr().unwrap(); - let dst_addr = match endpoint.addr { - IpAddress::Ipv4(addr) => addr, - _ => return Err(Error::Illegal), - }; - let ipv4_repr = Ipv4Repr { - src_addr, - dst_addr, - protocol: IpProtocol::Udp, - payload_len: udp_repr.header_len() + dhcp_repr.buffer_len(), - hop_limit: 64, - }; - - let mut packet = raw_socket.send( - ipv4_repr.buffer_len() + udp_repr.header_len() + dhcp_repr.buffer_len() - )?; - { - let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut packet); - ipv4_repr.emit(&mut ipv4_packet, &checksum_caps); - } - { - let mut udp_packet = UdpPacket::new_unchecked( - &mut packet[ipv4_repr.buffer_len()..] - ); - udp_repr.emit(&mut udp_packet, - &src_addr.into(), &dst_addr.into(), - dhcp_repr.buffer_len(), - |buf| dhcp_repr.emit(&mut Dhcpv4Packet::new_unchecked(buf)).unwrap(), - checksum_caps); - } - Ok(()) -} - -fn parse_udp<'a>(data: &'a [u8], checksum_caps: &ChecksumCapabilities) -> Result<(IpEndpoint, IpEndpoint, &'a [u8])> { - let ipv4_packet = Ipv4Packet::new_checked(data)?; - let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?; - let udp_packet = UdpPacket::new_checked(ipv4_packet.payload())?; - let udp_repr = UdpRepr::parse( - &udp_packet, - &ipv4_repr.src_addr.into(), &ipv4_repr.dst_addr.into(), - checksum_caps - )?; - let src = IpEndpoint { - addr: ipv4_repr.src_addr.into(), - port: udp_repr.src_port, - }; - let dst = IpEndpoint { - addr: ipv4_repr.dst_addr.into(), - port: udp_repr.dst_port, - }; - let data = udp_packet.payload(); - Ok((src, dst, data)) -} diff --git a/src/dhcp/mod.rs b/src/dhcp/mod.rs deleted file mode 100644 index 2c1b47a99..000000000 --- a/src/dhcp/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub const UDP_SERVER_PORT: u16 = 67; -pub const UDP_CLIENT_PORT: u16 = 68; - -mod clientv4; -pub use self::clientv4::{Client as Dhcpv4Client, Config as Dhcpv4Config}; diff --git a/src/iface/interface.rs b/src/iface/interface.rs index d569067ba..e1acc5891 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -265,7 +265,9 @@ pub(crate) enum IpPacket<'a> { #[cfg(feature = "socket-udp")] Udp((IpRepr, UdpRepr, &'a [u8])), #[cfg(feature = "socket-tcp")] - Tcp((IpRepr, TcpRepr<'a>)) + Tcp((IpRepr, TcpRepr<'a>)), + #[cfg(feature = "socket-dhcpv4")] + Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)), } impl<'a> IpPacket<'a> { @@ -283,6 +285,8 @@ impl<'a> IpPacket<'a> { IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), + #[cfg(feature = "socket-dhcpv4")] + IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr), } } @@ -331,6 +335,13 @@ impl<'a> IpPacket<'a> { &_ip_repr.src_addr(), &_ip_repr.dst_addr(), &caps.checksum); } + #[cfg(feature = "socket-dhcpv4")] + IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => + udp_repr.emit(&mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), &_ip_repr.dst_addr(), + dhcp_repr.buffer_len(), + |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), + &caps.checksum), } } } @@ -662,6 +673,14 @@ impl<'a, DeviceT> Interface<'a, DeviceT> }) } + 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")] @@ -687,15 +706,14 @@ impl<'a, DeviceT> Interface<'a, DeviceT> respond!(IpPacket::Udp(response))), #[cfg(feature = "socket-tcp")] Socket::Tcp(ref mut socket) => { - 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, - }; - socket.dispatch(timestamp, ip_mtu, |response| + socket.dispatch(timestamp, _ip_mtu, |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| + respond!(IpPacket::Dhcpv4(response))), }; match (device_result, socket_result) { @@ -1063,6 +1081,34 @@ impl<'a> InterfaceInner<'a> { #[cfg(not(feature = "socket-raw"))] let handled_by_raw_socket = false; + + #[cfg(feature = "socket-dhcpv4")] + { + if ipv4_repr.protocol == IpProtocol::Udp && self.ethernet_addr.is_some() { + // First check for source and dest ports, then do `UdpRepr::parse` if they match. + // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) + let udp_packet = UdpPacket::new_checked(ip_payload)?; + 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_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) { + // The packet is valid and handled by socket. + Ok(()) => return Ok(None), + // The packet is malformed, or the socket buffer is full. + Err(e) => return Err(e) + } + } + } + } + } + if !self.has_ip_addr(ipv4_repr.dst_addr) && !self.has_multicast_group(ipv4_repr.dst_addr) && !self.is_broadcast_v4(ipv4_repr.dst_addr) { diff --git a/src/lib.rs b/src/lib.rs index 0fda13b5a..028edf89c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,8 +121,6 @@ pub mod iface; #[cfg(feature = "socket")] pub mod socket; pub mod time; -#[cfg(feature = "proto-dhcpv4")] -pub mod dhcp; /// The error type for the networking stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs new file mode 100644 index 000000000..8137ff6f9 --- /dev/null +++ b/src/socket/dhcpv4.rs @@ -0,0 +1,422 @@ +use crate::{Error, Result}; +use crate::wire::{EthernetAddress, IpProtocol, IpAddress, + Ipv4Cidr, Ipv4Address, Ipv4Repr, + UdpRepr, UDP_HEADER_LEN, + DhcpPacket, DhcpRepr, DhcpMessageType, DHCP_CLIENT_PORT, DHCP_SERVER_PORT}; +use crate::wire::dhcpv4::{field as dhcpv4_field}; +use crate::socket::SocketMeta; +use crate::time::{Instant, Duration}; + +use super::{PollAt, Socket}; + +const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10); + +const REQUEST_TIMEOUT: Duration = Duration::from_secs(1); +const REQUEST_RETRIES: u16 = 15; + +const MIN_RENEW_TIMEOUT: Duration = Duration::from_secs(60); + +const DEFAULT_LEASE_DURATION: u32 = 120; + +const PARAMETER_REQUEST_LIST: &[u8] = &[ + dhcpv4_field::OPT_SUBNET_MASK, + dhcpv4_field::OPT_ROUTER, + dhcpv4_field::OPT_DOMAIN_NAME_SERVER, +]; + +/// IPv4 configuration data provided by the DHCP server. +#[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// IP address + pub address: Ipv4Cidr, + /// Router address, also known as default gateway. Does not necessarily + /// match the DHCP server's address. + pub router: Option, + /// DNS servers + pub dns_servers: [Option; 3], +} + +/// Information on how to reach a DHCP server. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct ServerInfo { + /// IP address to use as destination in outgoing packets + address: Ipv4Address, + /// Server identifier to use in outgoing packets. Usually equal to server_address, + /// but may differ in some situations (eg DHCP relays) + identifier: Ipv4Address, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct DiscoverState { + /// When to send next request + retry_at: Instant, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct RequestState { + /// When to send next request + retry_at: Instant, + /// How many retries have been done + retry: u16, + /// Server we're trying to request from + server: ServerInfo, + /// IP address that we're trying to request. + requested_ip: Ipv4Address, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct RenewState { + /// Server that gave us the lease + server: ServerInfo, + /// Active networkc config + config: Config, + + /// Renew timer. When reached, we will start attempting + /// to renew this lease with the DHCP server. + /// Must be less or equal than `expires_at`. + renew_at: Instant, + /// Expiration timer. When reached, this lease is no longer valid, so it must be + /// thrown away and the ethernet interface deconfigured. + expires_at: Instant, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum ClientState { + /// Discovering the DHCP server + Discovering(DiscoverState), + /// Requesting an address + Requesting(RequestState), + /// Having an address, refresh it periodically. + Renewing(RenewState), +} + +/// Return value for the `Dhcpv4Socket::poll` function +pub enum Event<'a> { + /// No change has occured to the configuration. + NoChange, + /// Configuration has been lost (for example, the lease has expired) + Deconfigured, + /// Configuration has been newly acquired, or modified. + Configured(&'a Config), +} + +#[derive(Debug)] +pub struct Dhcpv4Socket { + pub(crate) meta: SocketMeta, + /// State of the DHCP client. + state: ClientState, + /// Set to true on config/state change, cleared back to false by the `config` function. + config_changed: bool, + /// xid of the last sent message. + transaction_id: u32, +} + +/// DHCP client socket. +/// +/// The socket acquires an IP address configuration through DHCP autonomously. +/// You must query the configuration with `.poll()` after every call to `Interface::poll()`, +/// and apply the configuration to the `Interface`. +impl Dhcpv4Socket { + /// Create a DHCPv4 socket + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Dhcpv4Socket { + meta: SocketMeta::default(), + state: ClientState::Discovering(DiscoverState{ + retry_at: Instant::from_millis(0), + }), + config_changed: true, + transaction_id: 1, + } + } + + pub(crate) fn poll_at(&self) -> PollAt { + let t = match &self.state { + ClientState::Discovering(state) => state.retry_at, + ClientState::Requesting(state) => state.retry_at, + ClientState::Renewing(state) => state.renew_at.min(state.expires_at), + }; + PollAt::Time(t) + } + + pub(crate) fn process(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> { + let src_ip = ip_repr.src_addr; + + if repr.src_port != DHCP_SERVER_PORT || repr.dst_port != DHCP_CLIENT_PORT { + return Ok(()) + } + + let dhcp_packet = match DhcpPacket::new_checked(payload) { + Ok(dhcp_packet) => dhcp_packet, + Err(e) => { + net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e); + return Ok(()); + } + }; + let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) { + Ok(dhcp_repr) => dhcp_repr, + Err(e) => { + net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e); + return Ok(()); + } + }; + if dhcp_repr.client_hardware_address != ethernet_addr { 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, + None => { + net_debug!("DHCP ignoring {:?} because missing server_identifier", dhcp_repr.message_type); + return Ok(()); + } + }; + + net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); + + match (&mut self.state, dhcp_repr.message_type){ + (ClientState::Discovering(_state), DhcpMessageType::Offer) => { + if !dhcp_repr.your_ip.is_unicast() { + net_debug!("DHCP ignoring OFFER because your_ip is not unicast"); + return Ok(()) + } + + self.state = ClientState::Requesting(RequestState { + retry_at: now, + retry: 0, + server: ServerInfo { + address: src_ip, + identifier: server_identifier, + }, + requested_ip: dhcp_repr.your_ip // use the offered ip + }); + } + (ClientState::Requesting(state), DhcpMessageType::Ack) => { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) { + self.config_changed = true; + self.state = ClientState::Renewing(RenewState{ + server: state.server, + config, + renew_at, + expires_at, + }); + } + } + (ClientState::Renewing(state), DhcpMessageType::Ack) => { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) { + state.renew_at = renew_at; + state.expires_at = expires_at; + if state.config != config { + self.config_changed = true; + state.config = config; + } + } + } + _ => { + net_debug!("DHCP ignoring {:?}: unexpected in current state", dhcp_repr.message_type); + } + } + + Ok(()) + } + + fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr) -> Option<(Config, Instant, Instant)> { + let subnet_mask = match dhcp_repr.subnet_mask { + Some(subnet_mask) => subnet_mask, + None => { + net_debug!("DHCP ignoring ACK because missing subnet_mask"); + return None + } + }; + + let prefix_len = match IpAddress::Ipv4(subnet_mask).to_prefix_len() { + Some(prefix_len) => prefix_len, + None => { + net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask"); + return None + } + }; + + if !dhcp_repr.your_ip.is_unicast() { + net_debug!("DHCP ignoring ACK because your_ip is not unicast"); + return None + } + + let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_LEASE_DURATION); + + let config = Config{ + address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), + router: dhcp_repr.router, + dns_servers: dhcp_repr.dns_servers.unwrap_or([None; 3]), + }; + + // RFC 2131 indicates clients should renew a lease halfway through its expiration. + let renew_at = now + Duration::from_secs((lease_duration / 2).into()); + let expires_at = now + Duration::from_secs(lease_duration.into()); + + Some((config, renew_at, expires_at)) + } + + pub(crate) fn dispatch(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_mtu: usize, emit: F) -> Result<()> + where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> { + + // Worst case biggest IPv4 header length. + // 0x0f * 4 = 60 bytes. + const MAX_IPV4_HEADER_LEN: usize = 60; + + // We don't directly increment transaction_id because sending the packet + // may fail. We only want to update state after succesfully sending. + let next_transaction_id = self.transaction_id + 1; + + let mut dhcp_repr = DhcpRepr { + message_type: DhcpMessageType::Discover, + transaction_id: next_transaction_id, + client_hardware_address: ethernet_addr, + client_ip: Ipv4Address::UNSPECIFIED, + your_ip: Ipv4Address::UNSPECIFIED, + server_ip: Ipv4Address::UNSPECIFIED, + router: None, + subnet_mask: None, + relay_agent_ip: Ipv4Address::UNSPECIFIED, + broadcast: true, + requested_ip: None, + 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), + lease_duration: None, + dns_servers: None, + }; + + let udp_repr = UdpRepr { + src_port: DHCP_CLIENT_PORT, + dst_port: DHCP_SERVER_PORT, + }; + + let mut ipv4_repr = Ipv4Repr { + src_addr: Ipv4Address::UNSPECIFIED, + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Udp, + payload_len: 0, // filled right before emit + hop_limit: 64, + }; + + match &mut self.state { + ClientState::Discovering(state) => { + if now < state.retry_at { + return Err(Error::Exhausted) + } + + // send packet + net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); + ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); + emit((ipv4_repr, udp_repr, dhcp_repr))?; + + // Update state AFTER the packet has been successfully sent. + state.retry_at = now + DISCOVER_TIMEOUT; + self.transaction_id = next_transaction_id; + Ok(()) + } + ClientState::Requesting(state) => { + if now < state.retry_at { + return Err(Error::Exhausted) + } + + if state.retry >= REQUEST_RETRIES { + net_debug!("DHCP request retries exceeded, restarting discovery"); + self.reset(); + // return Ok so we get polled again + return Ok(()) + } + + dhcp_repr.message_type = DhcpMessageType::Request; + dhcp_repr.broadcast = false; + dhcp_repr.requested_ip = Some(state.requested_ip); + dhcp_repr.server_identifier = Some(state.server.identifier); + + net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); + ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); + emit((ipv4_repr, udp_repr, dhcp_repr))?; + + // Exponential backoff + state.retry_at = now + REQUEST_TIMEOUT; + state.retry += 1; + + self.transaction_id = next_transaction_id; + Ok(()) + } + ClientState::Renewing(state) => { + if state.expires_at <= now { + net_debug!("DHCP lease expired"); + self.reset(); + // return Ok so we get polled again + return Ok(()) + } + + if now < state.renew_at { + return Err(Error::Exhausted) + } + + ipv4_repr.src_addr = state.config.address.address(); + ipv4_repr.dst_addr = state.server.address; + dhcp_repr.message_type = DhcpMessageType::Request; + dhcp_repr.client_ip = state.config.address.address(); + dhcp_repr.broadcast = false; + + net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); + ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); + emit((ipv4_repr, udp_repr, dhcp_repr))?; + + // In both RENEWING and REBINDING states, if the client receives no + // response to its DHCPREQUEST message, the client SHOULD wait one-half + // 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); + + self.transaction_id = next_transaction_id; + Ok(()) + } + } + } + + /// Reset state and restart discovery phase. + /// + /// Use this to speed up acquisition of an address in a new + /// network if a link was down and it is now back up. + pub fn reset(&mut self) { + net_trace!("DHCP reset"); + if let ClientState::Renewing(_) = &self.state { + self.config_changed = true; + } + self.state = ClientState::Discovering(DiscoverState{ + retry_at: Instant::from_millis(0), + }); + } + + /// Query the socket for configuration changes. + /// + /// The socket has an internal "configuration changed" flag. If + /// set, this function returns the configuration and resets the flag. + pub fn poll(&mut self) -> Event<'_> { + if !self.config_changed { + Event::NoChange + } else if let ClientState::Renewing(state) = &self.state { + self.config_changed = false; + Event::Configured(&state.config) + } else { + self.config_changed = false; + Event::Deconfigured + } + } +} + +impl<'a> Into> for Dhcpv4Socket { + fn into(self) -> Socket<'a> { + Socket::Dhcpv4(self) + } +} diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 76fe30f5a..ebd0c4273 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -22,6 +22,8 @@ mod icmp; mod udp; #[cfg(feature = "socket-tcp")] mod tcp; +#[cfg(feature = "socket-dhcpv4")] +mod dhcpv4; mod set; mod ref_; @@ -53,6 +55,9 @@ pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket}; +#[cfg(feature = "socket-dhcpv4")] +pub use self::dhcpv4::{Dhcpv4Socket, Config as Dhcpv4Config, Event as Dhcpv4Event}; + pub use self::set::{Set as SocketSet, Item as SocketSetItem, Handle as SocketHandle}; pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; @@ -91,6 +96,8 @@ pub enum Socket<'a> { Udp(UdpSocket<'a>), #[cfg(feature = "socket-tcp")] Tcp(TcpSocket<'a>), + #[cfg(feature = "socket-dhcpv4")] + Dhcpv4(Dhcpv4Socket), } macro_rules! dispatch_socket { @@ -110,6 +117,8 @@ macro_rules! dispatch_socket { &$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code, #[cfg(feature = "socket-tcp")] &$( $mut_ )* Socket::Tcp(ref $( $mut_ )* $socket) => $code, + #[cfg(feature = "socket-dhcpv4")] + &$( $mut_ )* Socket::Dhcpv4(ref $( $mut_ )* $socket) => $code, } }; } @@ -169,3 +178,5 @@ from_socket!(IcmpSocket<'a>, Icmp); from_socket!(UdpSocket<'a>, Udp); #[cfg(feature = "socket-tcp")] from_socket!(TcpSocket<'a>, Tcp); +#[cfg(feature = "socket-dhcpv4")] +from_socket!(Dhcpv4Socket, Dhcpv4); diff --git a/src/socket/ref_.rs b/src/socket/ref_.rs index 9e030ff95..3a52a779a 100644 --- a/src/socket/ref_.rs +++ b/src/socket/ref_.rs @@ -1,13 +1,5 @@ use core::ops::{Deref, DerefMut}; -#[cfg(feature = "socket-raw")] -use crate::socket::RawSocket; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use crate::socket::IcmpSocket; -#[cfg(feature = "socket-udp")] -use crate::socket::UdpSocket; -#[cfg(feature = "socket-tcp")] -use crate::socket::TcpSocket; /// A trait for tracking a socket usage session. /// @@ -20,13 +12,15 @@ pub trait Session { } #[cfg(feature = "socket-raw")] -impl<'a> Session for RawSocket<'a> {} +impl<'a> Session for crate::socket::RawSocket<'a> {} #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -impl<'a> Session for IcmpSocket<'a> {} +impl<'a> Session for crate::socket::IcmpSocket<'a> {} #[cfg(feature = "socket-udp")] -impl<'a> Session for UdpSocket<'a> {} +impl<'a> Session for crate::socket::UdpSocket<'a> {} #[cfg(feature = "socket-tcp")] -impl<'a> Session for TcpSocket<'a> {} +impl<'a> Session for crate::socket::TcpSocket<'a> {} +#[cfg(feature = "socket-dhcpv4")] +impl Session for crate::socket::Dhcpv4Socket {} /// A smart pointer to a socket. /// diff --git a/src/socket/set.rs b/src/socket/set.rs index 686665658..74af3177f 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -156,6 +156,9 @@ impl<'a> Set<'a> { } else { socket.close() }, + #[cfg(feature = "socket-dhcpv4")] + Socket::Dhcpv4(_) => + may_remove = true, } } if may_remove { diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index a9389431b..0e3bfdd63 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -6,6 +6,9 @@ use crate::{Error, Result}; use crate::wire::{EthernetAddress, Ipv4Address}; use crate::wire::arp::Hardware; +pub const SERVER_PORT: u16 = 67; +pub const CLIENT_PORT: u16 = 68; + const DHCP_MAGIC_NUMBER: u32 = 0x63825363; enum_with_unknown! { diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 4fee202b4..ca1f2440b 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -211,7 +211,8 @@ pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr}; pub use self::udp::{Packet as UdpPacket, - Repr as UdpRepr}; + Repr as UdpRepr, + HEADER_LEN as UDP_HEADER_LEN}; pub use self::tcp::{SeqNumber as TcpSeqNumber, Packet as TcpPacket, @@ -222,4 +223,6 @@ pub use self::tcp::{SeqNumber as TcpSeqNumber, #[cfg(feature = "proto-dhcpv4")] pub use self::dhcpv4::{Packet as DhcpPacket, Repr as DhcpRepr, - MessageType as DhcpMessageType}; + MessageType as DhcpMessageType, + CLIENT_PORT as DHCP_CLIENT_PORT, + SERVER_PORT as DHCP_SERVER_PORT}; diff --git a/src/wire/udp.rs b/src/wire/udp.rs index ef60ed200..ad1c1a2f0 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -28,6 +28,8 @@ mod field { } } +pub const HEADER_LEN: usize = field::CHECKSUM.end; + #[allow(clippy::len_without_is_empty)] impl> Packet { /// Imbue a raw octet buffer with UDP packet structure. From 9ce3d9505ef5455bb049713b9262561d78ebf330 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 01:32:04 +0200 Subject: [PATCH 115/566] dhcp: handle NAK packets --- src/socket/dhcpv4.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 8137ff6f9..fa26e962d 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -206,6 +206,9 @@ impl Dhcpv4Socket { }); } } + (ClientState::Requesting(_), DhcpMessageType::Nak) => { + self.reset(); + } (ClientState::Renewing(state), DhcpMessageType::Ack) => { if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) { state.renew_at = renew_at; @@ -216,6 +219,9 @@ impl Dhcpv4Socket { } } } + (ClientState::Renewing(_), DhcpMessageType::Nak) => { + self.reset(); + } _ => { net_debug!("DHCP ignoring {:?}: unexpected in current state", dhcp_repr.message_type); } From 1c334384454b12d321a1ae5a09676b165c10992c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 21:52:26 +0200 Subject: [PATCH 116/566] dhcp: retry REQUEST slower and with exponential backoff. Fixes #464 --- src/socket/dhcpv4.rs | 10 ++++++---- src/time.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index fa26e962d..84e7ade3b 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -11,8 +11,10 @@ use super::{PollAt, Socket}; const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10); -const REQUEST_TIMEOUT: Duration = Duration::from_secs(1); -const REQUEST_RETRIES: u16 = 15; +// timeout doubles every 2 tries. +// total time 5 + 5 + 10 + 10 + 20 = 50s +const REQUEST_TIMEOUT: Duration = Duration::from_secs(5); +const REQUEST_RETRIES: u16 = 5; const MIN_RENEW_TIMEOUT: Duration = Duration::from_secs(60); @@ -348,8 +350,8 @@ impl Dhcpv4Socket { ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); emit((ipv4_repr, udp_repr, dhcp_repr))?; - // Exponential backoff - state.retry_at = now + REQUEST_TIMEOUT; + // Exponential backoff: Double every 2 retries. + state.retry_at = now + (REQUEST_TIMEOUT << (state.retry as u32 / 2)); state.retry += 1; self.transaction_id = next_transaction_id; diff --git a/src/time.rs b/src/time.rs index 0dc1c79e1..724f80403 100644 --- a/src/time.rs +++ b/src/time.rs @@ -232,6 +232,34 @@ impl ops::DivAssign for Duration { } } +impl ops::Shl for Duration { + type Output = Duration; + + fn shl(self, rhs: u32) -> Duration { + Duration::from_millis(self.millis << rhs) + } +} + +impl ops::ShlAssign for Duration { + fn shl_assign(&mut self, rhs: u32) { + self.millis <<= rhs; + } +} + +impl ops::Shr for Duration { + type Output = Duration; + + fn shr(self, rhs: u32) -> Duration { + Duration::from_millis(self.millis >> rhs) + } +} + +impl ops::ShrAssign for Duration { + fn shr_assign(&mut self, rhs: u32) { + self.millis >>= rhs; + } +} + impl From<::core::time::Duration> for Duration { fn from(other: ::core::time::Duration) -> Duration { Duration::from_millis( From 43d582504fd8529cf03e4971f9abe1244e11ccc9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Apr 2021 01:33:04 +0200 Subject: [PATCH 117/566] dhcp: remove 0.0.0.0s from the DNS serevr list. tp-link routers pad the DNS server list with 0.0.0.0 to a fixed size :( --- src/socket/dhcpv4.rs | 21 ++++++++++++++++++--- src/wire/dhcpv4.rs | 5 +++-- src/wire/mod.rs | 3 ++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 84e7ade3b..ca5bc38f0 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -2,7 +2,7 @@ use crate::{Error, Result}; use crate::wire::{EthernetAddress, IpProtocol, IpAddress, Ipv4Cidr, Ipv4Address, Ipv4Repr, UdpRepr, UDP_HEADER_LEN, - DhcpPacket, DhcpRepr, DhcpMessageType, DHCP_CLIENT_PORT, DHCP_SERVER_PORT}; + 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::time::{Instant, Duration}; @@ -36,7 +36,7 @@ pub struct Config { /// match the DHCP server's address. pub router: Option, /// DNS servers - pub dns_servers: [Option; 3], + pub dns_servers: [Option; DHCP_MAX_DNS_SERVER_COUNT], } /// Information on how to reach a DHCP server. @@ -256,10 +256,25 @@ impl Dhcpv4Socket { let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_LEASE_DURATION); + // Cleanup the DNS servers list, keeping only unicasts/ + // TP-Link TD-W8970 sends 0.0.0.0 as second DNS server if there's only one configured :( + let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT]; + if let Some(received) = dhcp_repr.dns_servers { + let mut i = 0; + for addr in received.iter() { + if let Some(addr) = addr{ + if addr.is_unicast() { + // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT + dns_servers[i] = Some(*addr); + i += 1; + } + } + } + } let config = Config{ address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), router: dhcp_repr.router, - dns_servers: dhcp_repr.dns_servers.unwrap_or([None; 3]), + dns_servers: dns_servers }; // RFC 2131 indicates clients should renew a lease halfway through its expiration. diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 0e3bfdd63..7ef39d91a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -8,6 +8,7 @@ use crate::wire::arp::Hardware; pub const SERVER_PORT: u16 = 67; pub const CLIENT_PORT: u16 = 68; +pub const MAX_DNS_SERVER_COUNT: usize = 3; const DHCP_MAGIC_NUMBER: u32 = 0x63825363; @@ -686,7 +687,7 @@ pub struct Repr<'a> { /// the client is interested in. pub parameter_request_list: Option<&'a [u8]>, /// DNS servers - pub dns_servers: Option<[Option; 3]>, + pub dns_servers: Option<[Option; MAX_DNS_SERVER_COUNT]>, /// The maximum size dhcp packet the interface can receive pub max_size: Option, /// The DHCP IP lease duration, specified in seconds. @@ -780,7 +781,7 @@ impl<'a> Repr<'a> { parameter_request_list = Some(data); } DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => { - let mut servers = [None; 3]; + let mut servers = [None; MAX_DNS_SERVER_COUNT]; for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) { *server = Some(Ipv4Address::from_bytes(chunk)); } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index ca1f2440b..7fb37b54e 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -225,4 +225,5 @@ pub use self::dhcpv4::{Packet as DhcpPacket, Repr as DhcpRepr, MessageType as DhcpMessageType, CLIENT_PORT as DHCP_CLIENT_PORT, - SERVER_PORT as DHCP_SERVER_PORT}; + SERVER_PORT as DHCP_SERVER_PORT, + MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT}; From e82ef25e9108425ea8ea6ff9c4682187e9dbe421 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Apr 2021 20:23:28 +0200 Subject: [PATCH 118/566] dhcp: add max_lease_duration option --- examples/dhcp_client.rs | 12 ++++++++++-- src/socket/dhcpv4.rs | 27 ++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 06b308097..78fd562e9 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use log::*; -use smoltcp::phy::{Device, Medium, wait as phy_wait}; +use smoltcp::{phy::{Device, Medium, wait as phy_wait}, time::Duration}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes}; use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event}; @@ -42,7 +42,15 @@ fn main() { let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); - let dhcp_handle = sockets.add(Dhcpv4Socket::new()); + let mut dhcp_socket = Dhcpv4Socket::new(); + + // Set a ridiculously short max lease time to show DHCP renews work properly. + // This will cause the DHCP client to start renewing after 5 seconds, and give up the + // lease after 10 seconds if renew hasn't succeeded. + // IMPORTANT: This should be removed in production. + dhcp_socket.set_max_lease_duration(Some(Duration::from_secs(10))); + + let dhcp_handle = sockets.add(dhcp_socket); loop { let timestamp = Instant::now(); diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index ca5bc38f0..f89526db1 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -117,6 +117,10 @@ pub struct Dhcpv4Socket { config_changed: bool, /// xid of the last sent message. transaction_id: u32, + + /// Max lease duration. If set, it sets a maximum cap to the server-provided lease duration. + /// Useful to react faster to IP configuration changes and to test whether renews work correctly. + max_lease_duration: Option, } /// DHCP client socket. @@ -135,9 +139,18 @@ impl Dhcpv4Socket { }), config_changed: true, transaction_id: 1, + max_lease_duration: None, } } + pub fn max_lease_duration(&self) -> Option { + self.max_lease_duration + } + + pub fn set_max_lease_duration(&mut self, max_lease_duration: Option) { + self.max_lease_duration = max_lease_duration; + } + pub(crate) fn poll_at(&self) -> PollAt { let t = match &self.state { ClientState::Discovering(state) => state.retry_at, @@ -198,7 +211,7 @@ impl Dhcpv4Socket { }); } (ClientState::Requesting(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) { self.config_changed = true; self.state = ClientState::Renewing(RenewState{ server: state.server, @@ -212,7 +225,7 @@ impl Dhcpv4Socket { self.reset(); } (ClientState::Renewing(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr) { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) { state.renew_at = renew_at; state.expires_at = expires_at; if state.config != config { @@ -232,7 +245,7 @@ impl Dhcpv4Socket { Ok(()) } - fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr) -> Option<(Config, Instant, Instant)> { + fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr, max_lease_duration: Option) -> Option<(Config, Instant, Instant)> { let subnet_mask = match dhcp_repr.subnet_mask { Some(subnet_mask) => subnet_mask, None => { @@ -255,6 +268,10 @@ impl Dhcpv4Socket { } let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_LEASE_DURATION); + let mut lease_duration = Duration::from_secs(lease_duration as _); + if let Some(max_lease_duration) = max_lease_duration { + lease_duration = lease_duration.min(max_lease_duration); + } // Cleanup the DNS servers list, keeping only unicasts/ // TP-Link TD-W8970 sends 0.0.0.0 as second DNS server if there's only one configured :( @@ -278,8 +295,8 @@ impl Dhcpv4Socket { }; // RFC 2131 indicates clients should renew a lease halfway through its expiration. - let renew_at = now + Duration::from_secs((lease_duration / 2).into()); - let expires_at = now + Duration::from_secs(lease_duration.into()); + let renew_at = now + lease_duration / 2; + let expires_at = now + lease_duration; Some((config, renew_at, expires_at)) } From 9ccc26bf76e3d52ff6158f3e790c9e3801347da9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 17:47:02 +0200 Subject: [PATCH 119/566] wire/udp: clearer HEADER_LEN usage --- src/wire/udp.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wire/udp.rs b/src/wire/udp.rs index ad1c1a2f0..49433495e 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -57,13 +57,13 @@ impl> Packet { /// [set_len]: #method.set_len pub fn check_len(&self) -> Result<()> { let buffer_len = self.buffer.as_ref().len(); - if buffer_len < field::CHECKSUM.end { + if buffer_len < HEADER_LEN { Err(Error::Truncated) } else { let field_len = self.len() as usize; if buffer_len < field_len { Err(Error::Truncated) - } else if field_len < field::CHECKSUM.end { + } else if field_len < HEADER_LEN { Err(Error::Malformed) } else { Ok(()) @@ -234,9 +234,9 @@ impl Repr { }) } - /// Return the length of a packet that will be emitted from this high-level representation. + /// Return the length of the packet header that will be emitted from this high-level representation. pub fn header_len(&self) -> usize { - field::CHECKSUM.end + HEADER_LEN } /// Emit a high-level representation into an User Datagram Protocol packet. @@ -249,7 +249,7 @@ impl Repr { where T: AsRef<[u8]> + AsMut<[u8]> { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); - packet.set_len((field::CHECKSUM.end + payload_len) as u16); + packet.set_len((HEADER_LEN + payload_len) as u16); emit_payload(packet.payload_mut()); if checksum_caps.udp.tx() { From adb3b30cd7897b66e31f0eb51ae93a34d372512e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 18:23:50 +0200 Subject: [PATCH 120/566] dhcp: address review comments. --- examples/dhcp_client.rs | 6 +++--- src/socket/dhcpv4.rs | 23 ++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 78fd562e9..d274caebd 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -59,8 +59,8 @@ fn main() { } match sockets.get::(dhcp_handle).poll() { - Dhcpv4Event::NoChange => {} - Dhcpv4Event::Configured(config) => { + None => {} + Some(Dhcpv4Event::Configured(config)) => { debug!("DHCP config acquired!"); debug!("IP address: {}", config.address); @@ -80,7 +80,7 @@ fn main() { } } } - Dhcpv4Event::Deconfigured => { + Some(Dhcpv4Event::Deconfigured) => { debug!("DHCP lost config!"); set_ipv4_addr(&mut iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); iface.routes_mut().remove_default_ipv4_route(); diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index f89526db1..1f278e60c 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -18,7 +18,7 @@ const REQUEST_RETRIES: u16 = 5; const MIN_RENEW_TIMEOUT: Duration = Duration::from_secs(60); -const DEFAULT_LEASE_DURATION: u32 = 120; +const DEFAULT_LEASE_DURATION: Duration = Duration::from_secs(120); const PARAMETER_REQUEST_LIST: &[u8] = &[ dhcpv4_field::OPT_SUBNET_MASK, @@ -75,7 +75,7 @@ struct RequestState { struct RenewState { /// Server that gave us the lease server: ServerInfo, - /// Active networkc config + /// Active network config config: Config, /// Renew timer. When reached, we will start attempting @@ -100,8 +100,6 @@ enum ClientState { /// Return value for the `Dhcpv4Socket::poll` function pub enum Event<'a> { - /// No change has occured to the configuration. - NoChange, /// Configuration has been lost (for example, the lease has expired) Deconfigured, /// Configuration has been newly acquired, or modified. @@ -211,7 +209,7 @@ impl Dhcpv4Socket { }); } (ClientState::Requesting(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, &dhcp_repr, self.max_lease_duration) { self.config_changed = true; self.state = ClientState::Renewing(RenewState{ server: state.server, @@ -225,7 +223,7 @@ impl Dhcpv4Socket { self.reset(); } (ClientState::Renewing(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, ip_repr, &dhcp_repr, self.max_lease_duration) { + if let Some((config, renew_at, expires_at)) = Self::parse_ack(now, &dhcp_repr, self.max_lease_duration) { state.renew_at = renew_at; state.expires_at = expires_at; if state.config != config { @@ -245,7 +243,7 @@ impl Dhcpv4Socket { Ok(()) } - fn parse_ack(now: Instant, _ip_repr: &Ipv4Repr, dhcp_repr: &DhcpRepr, max_lease_duration: Option) -> Option<(Config, Instant, Instant)> { + fn parse_ack(now: Instant, dhcp_repr: &DhcpRepr, max_lease_duration: Option) -> Option<(Config, Instant, Instant)> { let subnet_mask = match dhcp_repr.subnet_mask { Some(subnet_mask) => subnet_mask, None => { @@ -267,8 +265,7 @@ impl Dhcpv4Socket { return None } - let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_LEASE_DURATION); - let mut lease_duration = Duration::from_secs(lease_duration as _); + let mut lease_duration = dhcp_repr.lease_duration.map(|d| Duration::from_secs(d as _)).unwrap_or(DEFAULT_LEASE_DURATION); if let Some(max_lease_duration) = max_lease_duration { lease_duration = lease_duration.min(max_lease_duration); } @@ -442,15 +439,15 @@ impl Dhcpv4Socket { /// /// The socket has an internal "configuration changed" flag. If /// set, this function returns the configuration and resets the flag. - pub fn poll(&mut self) -> Event<'_> { + pub fn poll(&mut self) -> Option> { if !self.config_changed { - Event::NoChange + None } else if let ClientState::Renewing(state) = &self.state { self.config_changed = false; - Event::Configured(&state.config) + Some(Event::Configured(&state.config)) } else { self.config_changed = false; - Event::Deconfigured + Some(Event::Deconfigured) } } } From 47316aaf04964fed317194b2b11d590ba6fa1777 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 18:26:08 +0200 Subject: [PATCH 121/566] Remove unused macro_use --- src/macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 63c7ca7e2..f24187191 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -12,7 +12,6 @@ macro_rules! net_log { } #[cfg(not(any(feature = "log", feature = "defmt")))] -#[macro_use] macro_rules! net_log { ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* } } From bdc0d31b87dfbc1a6b9075b8c3b98147b339f5e7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 18:43:52 +0200 Subject: [PATCH 122/566] dhcp: convert port check to hard assert. --- src/socket/dhcpv4.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 1f278e60c..cc61373eb 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -161,9 +161,8 @@ impl Dhcpv4Socket { pub(crate) fn process(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> { let src_ip = ip_repr.src_addr; - if repr.src_port != DHCP_SERVER_PORT || repr.dst_port != DHCP_CLIENT_PORT { - return Ok(()) - } + // This is enforced in interface.rs. + assert!(repr.src_port == DHCP_SERVER_PORT && repr.dst_port == DHCP_CLIENT_PORT); let dhcp_packet = match DhcpPacket::new_checked(payload) { Ok(dhcp_packet) => dhcp_packet, From db2789239296711fdaf083a77b183714c65c7fc6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 18:49:07 +0200 Subject: [PATCH 123/566] Test with defmt-trace --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8de0b1706..91eccf24d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,9 +73,9 @@ jobs: include: # defmt doesn't support 1.40 - rust: stable - features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + features: defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - rust: nightly - features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + features: defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 From 1ee7a0564566bca9dc85da52f05811dd44ee1ec0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 21:02:10 +0200 Subject: [PATCH 124/566] tcp rtte: fix "attempt to multiply with overflow". Fixes #468 --- src/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 987916f85..eb623e0ce 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -140,7 +140,7 @@ impl RttEstimator { // all packets sent would incur a retransmit. To avoid this, force an estimate // increase if we see 3 consecutive retransmissions without any successful sample. self.rto_count = 0; - self.rtt *= 2; + self.rtt = RTTE_MAX_RTO.min(self.rtt*2); let rto = self.retransmission_timeout().millis(); net_trace!("rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", self.rtt, self.deviation, rto); } From 24f94d24f1c2866bb2374f91829ac167040b2a4f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 May 2021 21:09:36 +0200 Subject: [PATCH 125/566] tcp: LastAck should only change to Closed on ack of fin. Fixes #470 --- src/socket/tcp.rs | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index eb623e0ce..dbdf8ff39 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1439,10 +1439,14 @@ impl<'a> TcpSocket<'a> { // ACK packets in LAST-ACK state change it to CLOSED. (State::LastAck, TcpControl::None) => { - // Clear the remote endpoint, or we'll send an RST there. - self.set_state(State::Closed); - self.local_endpoint = IpEndpoint::default(); - self.remote_endpoint = IpEndpoint::default(); + if ack_of_fin { + // Clear the remote endpoint, or we'll send an RST there. + self.set_state(State::Closed); + self.local_endpoint = IpEndpoint::default(); + self.remote_endpoint = IpEndpoint::default(); + } else { + self.timer.set_for_idle(timestamp, self.keep_alive); + } } _ => { @@ -3487,6 +3491,34 @@ mod test { assert_eq!(s.state, State::Closed); } + #[test] + fn test_last_ack_ack_not_of_fin() { + let mut s = socket_last_ack(); + recv!(s, [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }]); + assert_eq!(s.state, State::LastAck); + + // ACK received that doesn't ack the FIN: socket should stay in LastAck. + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::LastAck); + + // ACK received of fin: socket should change to Closed. + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + }); + assert_eq!(s.state, State::Closed); + } + #[test] fn test_last_ack_close() { let mut s = socket_last_ack(); From 63a6269c53dde610af0ded22c35d02ee7ab46309 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 28 May 2021 23:11:39 +0000 Subject: [PATCH 126/566] Account for lease time, router and subnet options in DhcpRepr::buffer_len --- src/wire/dhcpv4.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 7ef39d91a..36149379e 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -704,6 +704,9 @@ impl<'a> Repr<'a> { if self.client_identifier.is_some() { len += 9; } if self.server_identifier.is_some() { len += 6; } if self.max_size.is_some() { len += 4; } + if self.router.is_some() { len += 6; } + if self.subnet_mask.is_some() { len += 6; } + if self.lease_duration.is_some() { len += 6; } if let Some(list) = self.parameter_request_list { len += list.len() + 2; } len @@ -1019,6 +1022,28 @@ mod test { assert_eq!(packet, DISCOVER_BYTES); } + fn offer_repr() -> Repr<'static> { + Repr { + message_type: MessageType::Offer, + transaction_id: 0x3d1d, + client_hardware_address: CLIENT_MAC, + client_ip: IP_NULL, + your_ip: IP_NULL, + server_ip: IP_NULL, + router: Some(IP_NULL), + subnet_mask: Some(IP_NULL), + relay_agent_ip: IP_NULL, + broadcast: false, + requested_ip: None, + client_identifier: Some(CLIENT_MAC), + server_identifier: None, + parameter_request_list: None, + dns_servers: None, + max_size: None, + lease_duration: Some(u32::MAX), // Infinite lease + } + } + fn discover_repr() -> Repr<'static> { Repr { message_type: MessageType::Discover, @@ -1062,6 +1087,14 @@ mod test { } } + #[test] + fn test_emit_offer() { + let repr = offer_repr(); + let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut packet = Packet::new_unchecked(&mut bytes); + repr.emit(&mut packet).unwrap(); + } + #[test] fn test_emit_dhcp_option() { static DATA: &[u8] = &[1, 3, 6]; From ca3badd62db5584950fbc4561d5e7b41b0f0180f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 29 May 2021 01:24:57 +0200 Subject: [PATCH 127/566] Fix u32::MAX --- src/wire/dhcpv4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 36149379e..48170fb4a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -1040,7 +1040,7 @@ mod test { parameter_request_list: None, dns_servers: None, max_size: None, - lease_duration: Some(u32::MAX), // Infinite lease + lease_duration: Some(0xffff_ffff), // Infinite lease } } From 8172d99aec3e3c354e1f9493aae5cf21cccfaeed Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 29 May 2021 01:44:27 +0200 Subject: [PATCH 128/566] wire/dhcp: Simplify how options are emitted. --- src/wire/dhcpv4.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 48170fb4a..8cb1c11ae 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -827,31 +827,30 @@ impl<'a> Repr<'a> { { let mut options = packet.options_mut()?; - let tmp = options; options = DhcpOption::MessageType(self.message_type).emit(tmp); + options = DhcpOption::MessageType(self.message_type).emit(options); if let Some(eth_addr) = self.client_identifier { - let tmp = options; options = DhcpOption::ClientIdentifier(eth_addr).emit(tmp); + options = DhcpOption::ClientIdentifier(eth_addr).emit(options); } if let Some(ip) = self.server_identifier { - let tmp = options; options = DhcpOption::ServerIdentifier(ip).emit(tmp); + options = DhcpOption::ServerIdentifier(ip).emit(options); } if let Some(ip) = self.router { - let tmp = options; options = DhcpOption::Router(ip).emit(tmp); + options = DhcpOption::Router(ip).emit(options); } if let Some(ip) = self.subnet_mask { - let tmp = options; options = DhcpOption::SubnetMask(ip).emit(tmp); + options = DhcpOption::SubnetMask(ip).emit(options); } if let Some(ip) = self.requested_ip { - let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp); + options = DhcpOption::RequestedIp(ip).emit(options); } if let Some(size) = self.max_size { - let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp); + options = DhcpOption::MaximumDhcpMessageSize(size).emit(options); } if let Some(duration) = self.lease_duration { - let tmp = options; options = DhcpOption::IpLeaseTime(duration).emit(tmp); + options = DhcpOption::IpLeaseTime(duration).emit(options); } if let Some(list) = self.parameter_request_list { - let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }; - let tmp = options; options = option.emit(tmp); + options = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }.emit(options); } DhcpOption::EndOfList.emit(options); } @@ -1003,14 +1002,14 @@ mod test { { let mut options = packet.options_mut().unwrap(); - let tmp = options; options = DhcpOption::MessageType(MessageType::Discover).emit(tmp); - let tmp = options; options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(tmp); - let tmp = options; options = DhcpOption::RequestedIp(IP_NULL).emit(tmp); - let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(tmp); + options = DhcpOption::MessageType(MessageType::Discover).emit(options); + options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(options); + options = DhcpOption::RequestedIp(IP_NULL).emit(options); + options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options); let option = DhcpOption::Other { kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42], }; - let tmp = options; options = option.emit(tmp); + options = option.emit(options); DhcpOption::EndOfList.emit(options); } From 42692f93349bfa3d924e711d1f70864d8e6369a6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 29 May 2021 04:30:38 +0200 Subject: [PATCH 129/566] Add v0.7.2 changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c0f62dc6..14fcc329a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) +## [0.7.2] - 2021-05-29 + +- iface: check for ipv4 subnet broadcast addrs everywhere (#462) +- dhcp: always send parameter_request_list. (#456) +- dhcp: Clear expiration time on reset. (#456) +- phy: fix FaultInjector returning a too big buffer when simulating a drop on tx (#463) +- tcp rtte: fix "attempt to multiply with overflow". (#476) +- tcp: LastAck should only change to Closed on ack of fin. (#477) +- wire/dhcpv4: account for lease time, router and subnet options in DhcpRepr::buffer_len (#478) + ## [0.7.1] - 2021-03-27 - ndisc: Fix NeighborSolicit incorrectly asking for src addr instead of dst addr ([419](https://github.com/smoltcp-rs/smoltcp/pull/419)) From 7fa5f6f9cc6a59392b3750b4e2e731ebc7a056c1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 29 May 2021 04:41:38 +0200 Subject: [PATCH 130/566] Add changelog for v0.7.3 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14fcc329a..4be1d9655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) +## [0.7.3] - 2021-05-29 + +- Fix "unused attribute" error in recent nightlies. + ## [0.7.2] - 2021-05-29 - iface: check for ipv4 subnet broadcast addrs everywhere (#462) @@ -67,5 +71,7 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.7.3]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.2...v0.7.3 +[0.7.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.1...v0.7.2 [0.7.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.6.0...v0.7.0 From 1521d9ce2852d5c69704fa4e0e254c14202b7a12 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 14:25:52 +0200 Subject: [PATCH 131/566] Adding UDP socket close funcionality --- CHANGELOG.md | 1 + src/socket/udp.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4be1d9655..dad0d6a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) +- udp: Add `close()` method to unbind socket. ## [0.7.3] - 2021-05-29 diff --git a/src/socket/udp.rs b/src/socket/udp.rs index f62d94f71..34071f03f 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -146,6 +146,11 @@ impl<'a> UdpSocket<'a> { Ok(()) } + /// Close the socket. + pub fn close(&mut self) { + self.endpoint = IpEndpoint::default(); + } + /// Check whether the socket is open. #[inline] pub fn is_open(&self) -> bool { @@ -624,4 +629,15 @@ mod test { assert_eq!(socket.process(&remote_ip_repr(), &repr, &[]), Ok(())); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); } + + #[test] + fn test_closing() { + let recv_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![]); + let mut socket = socket(recv_buffer, buffer(0)); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); + + assert!(socket.is_open()); + socket.close(); + assert!(!socket.is_open()); + } } From d0e3ebd4f0a3024c66a5c211a51aa44f368caab6 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:25:04 +0200 Subject: [PATCH 132/566] Adding prototype fix for fail-free ingress --- src/iface/interface.rs | 147 ++++++++++++++++++++--------------- src/phy/fault_injector.rs | 15 ++-- src/phy/fuzz_injector.rs | 4 +- src/phy/loopback.rs | 8 +- src/phy/mod.rs | 4 +- src/phy/pcap_writer.rs | 4 +- src/phy/raw_socket.rs | 8 +- src/phy/tracer.rs | 6 +- src/phy/tuntap_interface.rs | 8 +- src/socket/dhcpv4.rs | 8 +- src/socket/icmp.rs | 21 ++++- src/socket/raw.rs | 4 +- src/socket/tcp.rs | 4 +- src/socket/udp.rs | 2 +- src/socket/waker.rs | 2 +- src/storage/packet_buffer.rs | 9 +-- src/storage/ring_buffer.rs | 16 ++-- 17 files changed, 147 insertions(+), 123 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index e1acc5891..edc0d9796 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -537,13 +537,13 @@ impl<'a, DeviceT> Interface<'a, DeviceT> pub fn poll(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { 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(sockets, timestamp); + let emitted_any = self.socket_egress(sockets, timestamp); #[cfg(feature = "proto-igmp")] - self.igmp_egress(timestamp)?; + self.igmp_egress(timestamp); - if processed_any || emitted_any { + if processed_any || emitted_any? { readiness_may_have_changed = true; } else { break @@ -592,7 +592,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } } - fn socket_ingress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { + fn socket_ingress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> bool { let mut processed_any = false; loop { let &mut Self { ref mut device, ref mut inner } = self; @@ -600,53 +600,47 @@ impl<'a, DeviceT> Interface<'a, DeviceT> None => break, Some(tokens) => tokens, }; - rx_token.consume(timestamp, |frame| { + match rx_token.consume(timestamp, |frame| { match inner.device_capabilities.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { - net_debug!("cannot process ingress packet: {}", err); - #[cfg(not(feature = "defmt"))] - net_debug!("packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame)); - err - }).and_then(|response| { - processed_any = true; - match response { - Some(packet) => { - inner.dispatch(tx_token, timestamp, packet).map_err(|err| { - net_debug!("cannot dispatch response packet: {}", err); - err - }) + match inner.process_ethernet(sockets, timestamp, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + inner.dispatch(tx_token, timestamp, packet); } - None => Ok(()) } - }) + Err(err) => { + net_debug!("cannot process ingress packet: {}", err); + #[cfg(not(feature = "defmt"))] + net_debug!("packet dump follows:\n{}", + PrettyPrinter::>::new("", &frame)); + } + } } #[cfg(feature = "medium-ip")] Medium::Ip => { - inner.process_ip(sockets, timestamp, &frame).map_err(|err| { - net_debug!("cannot process ingress packet: {}", err); - //net_debug!("packet dump follows:\n{}", - // PrettyPrinter::>::new("", &frame)); - err - }).and_then(|response| { - processed_any = true; - match response { - Some(packet) => { - inner.dispatch_ip(tx_token, timestamp, packet).map_err(|err| { - net_debug!("cannot dispatch response packet: {}", err); - err - }) + match inner.process_ip(sockets, timestamp, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + match inner.dispatch_ip(tx_token, timestamp, packet) { + Err(err) => net_debug!("Failed to send response: {}", err), + _ => {}, + }; } - None => Ok(()) } - }) + Err(err) => net_debug!("cannot process ingress packet: {}", err), + } } } - })?; + }) { + Err(err) => net_debug!("Failed to consume RX token: {}", err), + Ok(_) => {}, + }; } - Ok(processed_any) + processed_any } fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { @@ -663,13 +657,16 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let mut device_result = Ok(()); let &mut Self { ref mut device, ref mut inner } = self; + let tx_token = match device.transmit() { + Some(token) => token, + None => break, + }; + macro_rules! respond { ($response:expr) => ({ 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 }) } @@ -697,7 +694,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))), - _ => Err(Error::Unaddressable) + _ => panic!("Invalid ICMP represnetation"), } }), #[cfg(feature = "socket-udp")] @@ -742,18 +739,27 @@ 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 { + fn igmp_egress(&mut self, timestamp: Instant) -> 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)?; + let tx_token = match self.device.transmit() { + Some(token) => token, + None => { + net_debug!("IGMP egress failure: Exhausted"); + return false + } + }; + match self.inner.dispatch_ip(tx_token, timestamp, pkt) { + Err(err) => net_debug!("Failed to egress IGMP: {}", err), + _ => {}, + }; } self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(true) + true } IgmpReportState::ToGeneralQuery { version, timeout, interval, next_index } if timestamp >= timeout => { @@ -766,24 +772,34 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Some(addr) => { 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)?; + let tx_token = match self.device.transmit() { + Some(token) => token, + None => { + net_debug!("IGMP egress failure: Exhausted"); + return false + } + }; + + match self.inner.dispatch_ip(tx_token, timestamp, pkt) { + Err(err) => net_debug!("Failed to egress IGMP: {}", err), + _ => {}, + }; } let next_timeout = (timeout + interval).max(timestamp); self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { version, timeout: next_timeout, interval, next_index: next_index + 1 }; - Ok(true) + true } None => { self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(false) + false } } } - _ => Ok(false) + _ => false } } } @@ -1567,7 +1583,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ethernet")] fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, - packet: EthernetPacket) -> Result<()> + packet: EthernetPacket) where Tx: TxToken { match packet { @@ -1584,29 +1600,33 @@ impl<'a> InterfaceInner<'a> { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); arp_repr.emit(&mut packet); - }) + }); }, EthernetPacket::Ip(packet) => { - self.dispatch_ip(tx_token, timestamp, packet) + match self.dispatch_ip(tx_token, timestamp, packet) { + Err(err) => net_debug!("Failed to egress: {}", err), + _ => {}, + }; }, } } #[cfg(feature = "medium-ethernet")] fn dispatch_ethernet(&mut self, tx_token: Tx, timestamp: Instant, - buffer_len: usize, f: F) -> Result<()> + buffer_len: usize, f: F) -> () 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| { + match tx_token.consume(timestamp, 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()); f(frame); - - Ok(()) - }) + }) { + Err(err) => net_debug!("Failed to consume TX token: {}", err), + Ok(_) => {}, + }; } fn in_same_network(&self, addr: &IpAddress) -> bool { @@ -1705,7 +1725,7 @@ impl<'a> InterfaceInner<'a> { frame.set_ethertype(EthernetProtocol::Arp); arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) - })?; + }); } #[cfg(feature = "proto-ipv6")] @@ -1765,7 +1785,8 @@ impl<'a> InterfaceInner<'a> { let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; packet.emit_payload(ip_repr, payload, &caps); - }) + }); + Ok(()) } #[cfg(feature = "medium-ip")] Medium::Ip => { @@ -1777,8 +1798,6 @@ impl<'a> InterfaceInner<'a> { let payload = &mut tx_buffer[ip_repr.buffer_len()..]; packet.emit_payload(ip_repr, payload, &caps); - - Ok(()) }) } } @@ -2746,7 +2765,7 @@ 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).unwrap(); + iface.socket_ingress(&mut socket_set, timestamp); // Leave multicast groups let timestamp = Instant::now(); diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 8c2e4aa27..646443644 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -242,7 +242,7 @@ pub struct RxToken<'a, Rx: phy::RxToken> { impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("rx: randomly dropping a packet"); @@ -254,10 +254,11 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { } let Self { token, config, state, mut corrupt } = self; token.consume(timestamp, |buffer| { - if config.max_size > 0 && buffer.as_ref().len() > config.max_size { - net_trace!("rx: dropping a packet that is too large"); - return Err(Error::Exhausted) - } + // TODO: Implement this in a new mechanism. Token consumption is infallible. + //if config.max_size > 0 && buffer.as_ref().len() > config.max_size { + // net_trace!("rx: dropping a packet that is too large"); + // return Error::Exhausted + //} if state.borrow_mut().maybe(config.corrupt_pct) { net_trace!("rx: randomly corrupting a packet"); let mut corrupt = &mut corrupt[..buffer.len()]; @@ -281,7 +282,7 @@ pub struct TxToken<'a, Tx: phy::TxToken> { impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { fn consume(mut self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("tx: randomly dropping a packet"); @@ -297,7 +298,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { }; if drop { - return f(&mut self.junk[..len]); + return Ok(f(&mut self.junk[..len])); } let Self { token, state, config, .. } = self; diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 5e2023b7a..a5e285b25 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -86,7 +86,7 @@ pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let Self { fuzzer, token } = self; token.consume(timestamp, |buffer| { @@ -104,7 +104,7 @@ pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> { impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let Self { fuzzer, token } = self; token.consume(timestamp, len, |mut buf| { diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index beeba9f6b..f18113faf 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -68,9 +68,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { - f(&mut self.buffer) + Ok(f(&mut self.buffer)) } } @@ -81,12 +81,12 @@ pub struct TxToken<'a> { impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let mut buffer = Vec::new(); buffer.resize(len, 0); let result = f(&mut buffer); self.queue.push_back(buffer); - result + Ok(result) } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index c04867772..8b3693c6a 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -295,7 +295,7 @@ pub trait RxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result; + where F: FnOnce(&mut [u8]) -> R; } /// A token to transmit a single network packet. @@ -310,5 +310,5 @@ pub trait TxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result; + where F: FnOnce(&mut [u8]) -> R; } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index a2226bbd4..6518a80fa 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -178,7 +178,7 @@ pub struct RxToken { } impl phy::RxToken for RxToken { - fn consume Result>(self, timestamp: Instant, f: F) -> Result { + fn consume R>(self, timestamp: Instant, f: F) -> Result { let Self { token, sink, mode } = self; token.consume(timestamp, |buffer| { match mode { @@ -200,7 +200,7 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let Self { token, sink, mode } = self; token.consume(timestamp, len, |buffer| { diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index b4d4e68fa..bf640b4f8 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -80,9 +80,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { - f(&mut self.buffer[..]) + Ok(f(&mut self.buffer[..])) } } @@ -93,12 +93,12 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); lower.send(&buffer[..]).unwrap(); - result + Ok(result) } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 8a5e377bd..c50f8cbe2 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -77,7 +77,7 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let Self { token, writer, medium } = self; token.consume(timestamp, |buffer| { @@ -100,7 +100,7 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let Self { token, writer, medium } = self; token.consume(timestamp, len, |buffer| { @@ -137,4 +137,4 @@ impl<'a> fmt::Display for Packet<'a> { } } } -} \ No newline at end of file +} diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index cfd4f2d92..62ecb8f67 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -83,9 +83,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { - f(&mut self.buffer[..]) + Ok(f(&mut self.buffer[..])) } } @@ -96,12 +96,12 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where F: FnOnce(&mut [u8]) -> R { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); lower.send(&buffer[..]).unwrap(); - result + Ok(result) } } diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index cc61373eb..fd8c0f48d 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -298,7 +298,7 @@ impl Dhcpv4Socket { } pub(crate) fn dispatch(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_mtu: usize, emit: F) -> Result<()> - where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> { + where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) { // Worst case biggest IPv4 header length. // 0x0f * 4 = 60 bytes. @@ -350,7 +350,7 @@ impl Dhcpv4Socket { // send packet net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit((ipv4_repr, udp_repr, dhcp_repr)); // Update state AFTER the packet has been successfully sent. state.retry_at = now + DISCOVER_TIMEOUT; @@ -376,7 +376,7 @@ impl Dhcpv4Socket { net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit((ipv4_repr, udp_repr, dhcp_repr)); // Exponential backoff: Double every 2 retries. state.retry_at = now + (REQUEST_TIMEOUT << (state.retry as u32 / 2)); @@ -405,7 +405,7 @@ impl Dhcpv4Socket { net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit((ipv4_repr, udp_repr, dhcp_repr)); // In both RENEWING and REBINDING states, if the client receives no // response to its DHCPREQUEST message, the client SHOULD wait one-half diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 9208ca7c5..e126b3995 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -402,7 +402,7 @@ impl<'a> IcmpSocket<'a> { } pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> - where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()> + where F: FnOnce((IpRepr, IcmpRepr)) { let handle = self.meta.handle; let hop_limit = self.hop_limit.unwrap_or(64); @@ -413,7 +413,13 @@ impl<'a> IcmpSocket<'a> { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(ipv4_addr) => { let packet = Icmpv4Packet::new_unchecked(&*packet_buf); - let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; + let repr = match Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored()) { + Ok(repr) => repr, + Err(err) => { + net_debug!("ICMPv4 represnetation invalid: {}", err); + return + }, + }; let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::default(), dst_addr: ipv4_addr, @@ -427,7 +433,14 @@ impl<'a> IcmpSocket<'a> { IpAddress::Ipv6(ipv6_addr) => { let packet = Icmpv6Packet::new_unchecked(&*packet_buf); let src_addr = Ipv6Address::default(); - let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored())?; + let repr = match Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, + &ChecksumCapabilities::ignored()) { + Ok(repr) => repr, + Err(err) => { + net_debug!("ICMPv6 represnetation invalid: {}", err); + return + }, + }; let ip_repr = IpRepr::Ipv6(Ipv6Repr { src_addr: src_addr, dst_addr: ipv6_addr, @@ -437,7 +450,7 @@ impl<'a> IcmpSocket<'a> { }); emit((ip_repr, IcmpRepr::Ipv6(repr))) }, - _ => Err(Error::Unaddressable) + _ => net_debug!("ICMP destination unaddressable"), } })?; diff --git a/src/socket/raw.rs b/src/socket/raw.rs index e4e60edb6..3dcc45cf0 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -228,7 +228,7 @@ impl<'a> RawSocket<'a> { pub(crate) fn dispatch(&mut self, checksum_caps: &ChecksumCapabilities, emit: F) -> Result<()> - where F: FnOnce((IpRepr, &[u8])) -> Result<()> { + where F: FnOnce((IpRepr, &[u8])) { fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8], _checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> { match IpVersion::of_packet(buffer)? { @@ -275,8 +275,6 @@ impl<'a> RawSocket<'a> { net_debug!("{}:{}:{}: dropping outgoing packet ({})", handle, ip_version, ip_protocol, error); - // Return Ok(()) so the packet is dequeued. - Ok(()) } } })?; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index dbdf8ff39..4a371a28c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1669,7 +1669,7 @@ impl<'a> TcpSocket<'a> { pub(crate) fn dispatch(&mut self, timestamp: Instant, ip_mtu: usize, emit: F) -> Result<()> - where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> { + where F: FnOnce((IpRepr, TcpRepr)) { if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) } if self.remote_last_ts.is_none() { @@ -1867,7 +1867,7 @@ impl<'a> TcpSocket<'a> { // to not waste time waiting for the retransmit timer on packets that we know // for sure will not be successfully transmitted. ip_repr.set_payload_len(repr.buffer_len()); - emit((ip_repr, repr))?; + emit((ip_repr, repr)); // We've sent something, whether useful data or a keep-alive packet, so rewind // the keep-alive timer. diff --git a/src/socket/udp.rs b/src/socket/udp.rs index f62d94f71..f04c026fe 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -296,7 +296,7 @@ impl<'a> UdpSocket<'a> { } pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> - where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> { + where F: FnOnce((IpRepr, UdpRepr, &[u8])) { let handle = self.handle(); let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); diff --git a/src/socket/waker.rs b/src/socket/waker.rs index 4dfb44b4e..4f4219788 100644 --- a/src/socket/waker.rs +++ b/src/socket/waker.rs @@ -30,4 +30,4 @@ impl WakerRegistration { pub fn wake(&mut self) { self.waker.take().map(|w| w.wake()); } -} \ No newline at end of file +} diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 5502de2fe..e9f19b520 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -125,7 +125,7 @@ impl<'a, H> PacketBuffer<'a, H> { /// Call `f` with a single packet from the buffer, and dequeue the packet if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result - where F: FnOnce(&mut H, &'c mut [u8]) -> Result { + where F: FnOnce(&mut H, &'c mut [u8]) -> R { self.dequeue_padding(); let Self { ref mut metadata_ring, ref mut payload_ring } = *self; @@ -136,12 +136,9 @@ impl<'a, H> PacketBuffer<'a, H> { payload_ring.dequeue_many_with(|payload_buf| { debug_assert!(payload_buf.len() >= size); - match f(header.as_mut().unwrap(), &mut payload_buf[..size]) { - Ok(val) => (size, Ok(val)), - Err(err) => (0, Err(err)), - } + (size, Ok(f(header.as_mut().unwrap(), &mut payload_buf[..size]))) }).1 - }) + })? } /// Dequeue a single packet from the buffer, and return a reference to its payload diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 063c9f3ae..e171ef1d6 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -136,18 +136,14 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Call `f` with a single buffer element, and dequeue the element if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut T) -> Result { + where F: FnOnce(&'b mut T) -> R { if self.is_empty() { return Err(Error::Exhausted) } let next_at = self.get_idx_unchecked(1); - match f(&mut self.storage[self.read_at]) { - Ok(result) => { - self.length -= 1; - self.read_at = next_at; - Ok(result) - } - Err(error) => Err(error) - } + let result = f(&mut self.storage[self.read_at]); + self.length -= 1; + self.read_at = next_at; + Ok(result) } /// Dequeue an element from the buffer, and return a reference to it, @@ -155,7 +151,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// /// This function is a shortcut for `ring_buf.dequeue_one_with(Ok)`. pub fn dequeue_one(&mut self) -> Result<&mut T> { - self.dequeue_one_with(Ok) + self.dequeue_one_with(|buf| buf) } } From 6091350f57df76e5e87cdfc383b60cb95c4d5546 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:30:45 +0200 Subject: [PATCH 133/566] Fail-free ingress --- src/iface/interface.rs | 3 ++- src/socket/dhcpv4.rs | 8 ++++---- src/socket/icmp.rs | 21 ++++----------------- src/socket/raw.rs | 4 +++- src/socket/tcp.rs | 4 ++-- src/socket/udp.rs | 2 +- src/socket/waker.rs | 2 +- src/storage/packet_buffer.rs | 9 ++++++--- src/storage/ring_buffer.rs | 16 ++++++++++------ 9 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index edc0d9796..1b3ef3e78 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -667,6 +667,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let response = $response; neighbor_addr = Some(response.ip_repr().dst_addr()); device_result = inner.dispatch_ip(tx_token, timestamp, response); + device_result }) } @@ -694,7 +695,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))), - _ => panic!("Invalid ICMP represnetation"), + _ => Err(Error::Unaddressable), } }), #[cfg(feature = "socket-udp")] diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index fd8c0f48d..cc61373eb 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -298,7 +298,7 @@ impl Dhcpv4Socket { } pub(crate) fn dispatch(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_mtu: usize, emit: F) -> Result<()> - where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) { + where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> { // Worst case biggest IPv4 header length. // 0x0f * 4 = 60 bytes. @@ -350,7 +350,7 @@ impl Dhcpv4Socket { // send packet net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr)); + emit((ipv4_repr, udp_repr, dhcp_repr))?; // Update state AFTER the packet has been successfully sent. state.retry_at = now + DISCOVER_TIMEOUT; @@ -376,7 +376,7 @@ impl Dhcpv4Socket { net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr)); + emit((ipv4_repr, udp_repr, dhcp_repr))?; // Exponential backoff: Double every 2 retries. state.retry_at = now + (REQUEST_TIMEOUT << (state.retry as u32 / 2)); @@ -405,7 +405,7 @@ impl Dhcpv4Socket { net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr)); + emit((ipv4_repr, udp_repr, dhcp_repr))?; // In both RENEWING and REBINDING states, if the client receives no // response to its DHCPREQUEST message, the client SHOULD wait one-half diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index e126b3995..9208ca7c5 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -402,7 +402,7 @@ impl<'a> IcmpSocket<'a> { } pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> - where F: FnOnce((IpRepr, IcmpRepr)) + where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()> { let handle = self.meta.handle; let hop_limit = self.hop_limit.unwrap_or(64); @@ -413,13 +413,7 @@ impl<'a> IcmpSocket<'a> { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(ipv4_addr) => { let packet = Icmpv4Packet::new_unchecked(&*packet_buf); - let repr = match Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored()) { - Ok(repr) => repr, - Err(err) => { - net_debug!("ICMPv4 represnetation invalid: {}", err); - return - }, - }; + let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::default(), dst_addr: ipv4_addr, @@ -433,14 +427,7 @@ impl<'a> IcmpSocket<'a> { IpAddress::Ipv6(ipv6_addr) => { let packet = Icmpv6Packet::new_unchecked(&*packet_buf); let src_addr = Ipv6Address::default(); - let repr = match Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, - &ChecksumCapabilities::ignored()) { - Ok(repr) => repr, - Err(err) => { - net_debug!("ICMPv6 represnetation invalid: {}", err); - return - }, - }; + let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored())?; let ip_repr = IpRepr::Ipv6(Ipv6Repr { src_addr: src_addr, dst_addr: ipv6_addr, @@ -450,7 +437,7 @@ impl<'a> IcmpSocket<'a> { }); emit((ip_repr, IcmpRepr::Ipv6(repr))) }, - _ => net_debug!("ICMP destination unaddressable"), + _ => Err(Error::Unaddressable) } })?; diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 3dcc45cf0..e4e60edb6 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -228,7 +228,7 @@ impl<'a> RawSocket<'a> { pub(crate) fn dispatch(&mut self, checksum_caps: &ChecksumCapabilities, emit: F) -> Result<()> - where F: FnOnce((IpRepr, &[u8])) { + where F: FnOnce((IpRepr, &[u8])) -> Result<()> { fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8], _checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> { match IpVersion::of_packet(buffer)? { @@ -275,6 +275,8 @@ impl<'a> RawSocket<'a> { net_debug!("{}:{}:{}: dropping outgoing packet ({})", handle, ip_version, ip_protocol, error); + // Return Ok(()) so the packet is dequeued. + Ok(()) } } })?; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 4a371a28c..dbdf8ff39 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1669,7 +1669,7 @@ impl<'a> TcpSocket<'a> { pub(crate) fn dispatch(&mut self, timestamp: Instant, ip_mtu: usize, emit: F) -> Result<()> - where F: FnOnce((IpRepr, TcpRepr)) { + where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> { if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) } if self.remote_last_ts.is_none() { @@ -1867,7 +1867,7 @@ impl<'a> TcpSocket<'a> { // to not waste time waiting for the retransmit timer on packets that we know // for sure will not be successfully transmitted. ip_repr.set_payload_len(repr.buffer_len()); - emit((ip_repr, repr)); + emit((ip_repr, repr))?; // We've sent something, whether useful data or a keep-alive packet, so rewind // the keep-alive timer. diff --git a/src/socket/udp.rs b/src/socket/udp.rs index f04c026fe..f62d94f71 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -296,7 +296,7 @@ impl<'a> UdpSocket<'a> { } pub(crate) fn dispatch(&mut self, emit: F) -> Result<()> - where F: FnOnce((IpRepr, UdpRepr, &[u8])) { + where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> { let handle = self.handle(); let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); diff --git a/src/socket/waker.rs b/src/socket/waker.rs index 4f4219788..4dfb44b4e 100644 --- a/src/socket/waker.rs +++ b/src/socket/waker.rs @@ -30,4 +30,4 @@ impl WakerRegistration { pub fn wake(&mut self) { self.waker.take().map(|w| w.wake()); } -} +} \ No newline at end of file diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index e9f19b520..5502de2fe 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -125,7 +125,7 @@ impl<'a, H> PacketBuffer<'a, H> { /// Call `f` with a single packet from the buffer, and dequeue the packet if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result - where F: FnOnce(&mut H, &'c mut [u8]) -> R { + where F: FnOnce(&mut H, &'c mut [u8]) -> Result { self.dequeue_padding(); let Self { ref mut metadata_ring, ref mut payload_ring } = *self; @@ -136,9 +136,12 @@ impl<'a, H> PacketBuffer<'a, H> { payload_ring.dequeue_many_with(|payload_buf| { debug_assert!(payload_buf.len() >= size); - (size, Ok(f(header.as_mut().unwrap(), &mut payload_buf[..size]))) + match f(header.as_mut().unwrap(), &mut payload_buf[..size]) { + Ok(val) => (size, Ok(val)), + Err(err) => (0, Err(err)), + } }).1 - })? + }) } /// Dequeue a single packet from the buffer, and return a reference to its payload diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index e171ef1d6..063c9f3ae 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -136,14 +136,18 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Call `f` with a single buffer element, and dequeue the element if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut T) -> R { + where F: FnOnce(&'b mut T) -> Result { if self.is_empty() { return Err(Error::Exhausted) } let next_at = self.get_idx_unchecked(1); - let result = f(&mut self.storage[self.read_at]); - self.length -= 1; - self.read_at = next_at; - Ok(result) + match f(&mut self.storage[self.read_at]) { + Ok(result) => { + self.length -= 1; + self.read_at = next_at; + Ok(result) + } + Err(error) => Err(error) + } } /// Dequeue an element from the buffer, and return a reference to it, @@ -151,7 +155,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// /// This function is a shortcut for `ring_buf.dequeue_one_with(Ok)`. pub fn dequeue_one(&mut self) -> Result<&mut T> { - self.dequeue_one_with(|buf| buf) + self.dequeue_one_with(Ok) } } From f4d4ba0f38625f314ae0107d2dc783456410391e Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:33:52 +0200 Subject: [PATCH 134/566] cleaning up implementation --- src/iface/interface.rs | 51 ++++++++++++------------------------------ 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 1b3ef3e78..b8c73d117 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -538,12 +538,12 @@ impl<'a, DeviceT> Interface<'a, DeviceT> 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 emitted_any = self.socket_egress(sockets, timestamp)?; #[cfg(feature = "proto-igmp")] - self.igmp_egress(timestamp); + self.igmp_egress(timestamp)?; - if processed_any || emitted_any? { + if processed_any || emitted_any { readiness_may_have_changed = true; } else { break @@ -657,15 +657,11 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let mut device_result = Ok(()); let &mut Self { ref mut device, ref mut inner } = self; - let tx_token = match device.transmit() { - Some(token) => token, - None => break, - }; - macro_rules! respond { ($response:expr) => ({ 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 }) @@ -695,7 +691,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))), - _ => Err(Error::Unaddressable), + _ => Err(Error::Unaddressable) } }), #[cfg(feature = "socket-udp")] @@ -740,27 +736,18 @@ 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) -> bool { + fn igmp_egress(&mut self, timestamp: Instant) -> Result { 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 = match self.device.transmit() { - Some(token) => token, - None => { - net_debug!("IGMP egress failure: Exhausted"); - return false - } - }; - match self.inner.dispatch_ip(tx_token, timestamp, pkt) { - Err(err) => net_debug!("Failed to egress IGMP: {}", err), - _ => {}, - }; + let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } self.inner.igmp_report_state = IgmpReportState::Inactive; - true + Ok(true) } IgmpReportState::ToGeneralQuery { version, timeout, interval, next_index } if timestamp >= timeout => { @@ -773,34 +760,24 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Some(addr) => { if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { // Send initial membership report - let tx_token = match self.device.transmit() { - Some(token) => token, - None => { - net_debug!("IGMP egress failure: Exhausted"); - return false - } - }; - - match self.inner.dispatch_ip(tx_token, timestamp, pkt) { - Err(err) => net_debug!("Failed to egress IGMP: {}", err), - _ => {}, - }; + let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } let next_timeout = (timeout + interval).max(timestamp); self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { version, timeout: next_timeout, interval, next_index: next_index + 1 }; - true + Ok(true) } None => { self.inner.igmp_report_state = IgmpReportState::Inactive; - false + Ok(false) } } } - _ => false + _ => Ok(false) } } } From d86d639bdb59d7f24f188c3ad10197d4266df5b5 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:36:26 +0200 Subject: [PATCH 135/566] Reverting phy changes --- src/iface/interface.rs | 4 ++++ src/phy/fault_injector.rs | 15 +++++++-------- src/phy/fuzz_injector.rs | 4 ++-- src/phy/loopback.rs | 8 ++++---- src/phy/mod.rs | 4 ++-- src/phy/pcap_writer.rs | 4 ++-- src/phy/raw_socket.rs | 8 ++++---- src/phy/tracer.rs | 6 +++--- src/phy/tuntap_interface.rs | 8 ++++---- 9 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index b8c73d117..791cf6b52 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -635,6 +635,8 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } } } + + Ok(()) }) { Err(err) => net_debug!("Failed to consume RX token: {}", err), Ok(_) => {}, @@ -1601,6 +1603,7 @@ impl<'a> InterfaceInner<'a> { frame.set_src_addr(self.ethernet_addr.unwrap()); f(frame); + Ok(()) }) { Err(err) => net_debug!("Failed to consume TX token: {}", err), Ok(_) => {}, @@ -1776,6 +1779,7 @@ impl<'a> InterfaceInner<'a> { let payload = &mut tx_buffer[ip_repr.buffer_len()..]; packet.emit_payload(ip_repr, payload, &caps); + Ok(()) }) } } diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 646443644..8c2e4aa27 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -242,7 +242,7 @@ pub struct RxToken<'a, Rx: phy::RxToken> { impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("rx: randomly dropping a packet"); @@ -254,11 +254,10 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { } let Self { token, config, state, mut corrupt } = self; token.consume(timestamp, |buffer| { - // TODO: Implement this in a new mechanism. Token consumption is infallible. - //if config.max_size > 0 && buffer.as_ref().len() > config.max_size { - // net_trace!("rx: dropping a packet that is too large"); - // return Error::Exhausted - //} + if config.max_size > 0 && buffer.as_ref().len() > config.max_size { + net_trace!("rx: dropping a packet that is too large"); + return Err(Error::Exhausted) + } if state.borrow_mut().maybe(config.corrupt_pct) { net_trace!("rx: randomly corrupting a packet"); let mut corrupt = &mut corrupt[..buffer.len()]; @@ -282,7 +281,7 @@ pub struct TxToken<'a, Tx: phy::TxToken> { impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { fn consume(mut self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("tx: randomly dropping a packet"); @@ -298,7 +297,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { }; if drop { - return Ok(f(&mut self.junk[..len])); + return f(&mut self.junk[..len]); } let Self { token, state, config, .. } = self; diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index a5e285b25..5e2023b7a 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -86,7 +86,7 @@ pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let Self { fuzzer, token } = self; token.consume(timestamp, |buffer| { @@ -104,7 +104,7 @@ pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> { impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let Self { fuzzer, token } = self; token.consume(timestamp, len, |mut buf| { diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index f18113faf..beeba9f6b 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -68,9 +68,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { - Ok(f(&mut self.buffer)) + f(&mut self.buffer) } } @@ -81,12 +81,12 @@ pub struct TxToken<'a> { impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let mut buffer = Vec::new(); buffer.resize(len, 0); let result = f(&mut buffer); self.queue.push_back(buffer); - Ok(result) + result } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 8b3693c6a..c04867772 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -295,7 +295,7 @@ pub trait RxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R; + where F: FnOnce(&mut [u8]) -> Result; } /// A token to transmit a single network packet. @@ -310,5 +310,5 @@ pub trait TxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R; + where F: FnOnce(&mut [u8]) -> Result; } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 6518a80fa..a2226bbd4 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -178,7 +178,7 @@ pub struct RxToken { } impl phy::RxToken for RxToken { - fn consume R>(self, timestamp: Instant, f: F) -> Result { + fn consume Result>(self, timestamp: Instant, f: F) -> Result { let Self { token, sink, mode } = self; token.consume(timestamp, |buffer| { match mode { @@ -200,7 +200,7 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let Self { token, sink, mode } = self; token.consume(timestamp, len, |buffer| { diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index bf640b4f8..b4d4e68fa 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -80,9 +80,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { - Ok(f(&mut self.buffer[..])) + f(&mut self.buffer[..]) } } @@ -93,12 +93,12 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); lower.send(&buffer[..]).unwrap(); - Ok(result) + result } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index c50f8cbe2..8a5e377bd 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -77,7 +77,7 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let Self { token, writer, medium } = self; token.consume(timestamp, |buffer| { @@ -100,7 +100,7 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let Self { token, writer, medium } = self; token.consume(timestamp, len, |buffer| { @@ -137,4 +137,4 @@ impl<'a> fmt::Display for Packet<'a> { } } } -} +} \ No newline at end of file diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index 62ecb8f67..cfd4f2d92 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -83,9 +83,9 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { - Ok(f(&mut self.buffer[..])) + f(&mut self.buffer[..]) } } @@ -96,12 +96,12 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> R + where F: FnOnce(&mut [u8]) -> Result { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); lower.send(&buffer[..]).unwrap(); - Ok(result) + result } } From fcfe355e87524954a59827ba7a9b4a1ccfb570c4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:39:30 +0200 Subject: [PATCH 136/566] Removing unneeded changes --- src/iface/interface.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 791cf6b52..05dae4eca 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -608,7 +608,10 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Ok(response) => { processed_any = true; if let Some(packet) = response { - inner.dispatch(tx_token, timestamp, packet); + match inner.dispatch(tx_token, timestamp, packet) { + Err(err) => net_debug!("Failed to send response: {}", err), + _ => {}, + }; } } Err(err) => { @@ -1563,7 +1566,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ethernet")] fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, - packet: EthernetPacket) + packet: EthernetPacket) -> Result<()> where Tx: TxToken { match packet { @@ -1580,34 +1583,29 @@ impl<'a> InterfaceInner<'a> { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); arp_repr.emit(&mut packet); - }); + }) }, EthernetPacket::Ip(packet) => { - match self.dispatch_ip(tx_token, timestamp, packet) { - Err(err) => net_debug!("Failed to egress: {}", err), - _ => {}, - }; + self.dispatch_ip(tx_token, timestamp, packet) }, } } #[cfg(feature = "medium-ethernet")] fn dispatch_ethernet(&mut self, tx_token: Tx, timestamp: Instant, - buffer_len: usize, f: F) -> () + buffer_len: usize, f: F) -> Result<()> where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>) { let tx_len = EthernetFrame::<&[u8]>::buffer_len(buffer_len); - match tx_token.consume(timestamp, tx_len, |tx_buffer| { + tx_token.consume(timestamp, 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()); f(frame); + Ok(()) - }) { - Err(err) => net_debug!("Failed to consume TX token: {}", err), - Ok(_) => {}, - }; + }) } fn in_same_network(&self, addr: &IpAddress) -> bool { @@ -1706,7 +1704,7 @@ impl<'a> InterfaceInner<'a> { frame.set_ethertype(EthernetProtocol::Arp); arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) - }); + })?; } #[cfg(feature = "proto-ipv6")] @@ -1766,8 +1764,7 @@ impl<'a> InterfaceInner<'a> { let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; packet.emit_payload(ip_repr, payload, &caps); - }); - Ok(()) + }) } #[cfg(feature = "medium-ip")] Medium::Ip => { @@ -1779,6 +1776,7 @@ impl<'a> InterfaceInner<'a> { let payload = &mut tx_buffer[ip_repr.buffer_len()..]; packet.emit_payload(ip_repr, payload, &caps); + Ok(()) }) } From 5a0ac07ac5e2eda6569a7082118095e74db26364 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:42:01 +0200 Subject: [PATCH 137/566] Simplifying loop logic --- src/iface/interface.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 05dae4eca..f32f796f3 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -594,12 +594,8 @@ impl<'a, DeviceT> Interface<'a, DeviceT> fn socket_ingress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> bool { let mut processed_any = false; - loop { - let &mut Self { ref mut device, ref mut inner } = self; - let (rx_token, tx_token) = match device.receive() { - None => break, - Some(tokens) => tokens, - }; + let &mut Self { ref mut device, ref mut inner } = self; + while let Some((rx_token, tx_token)) = device.receive() { match rx_token.consume(timestamp, |frame| { match inner.device_capabilities.medium { #[cfg(feature = "medium-ethernet")] @@ -645,6 +641,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Ok(_) => {}, }; } + processed_any } From b9fb265425e1a18c8198878e5c3818e12fcc3be5 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 17:58:20 +0200 Subject: [PATCH 138/566] Fixing clippy --- src/iface/interface.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index f32f796f3..eb6e186e0 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -596,7 +596,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let mut processed_any = false; let &mut Self { ref mut device, ref mut inner } = self; while let Some((rx_token, tx_token)) = device.receive() { - match rx_token.consume(timestamp, |frame| { + if let Err(err) = rx_token.consume(timestamp, |frame| { match inner.device_capabilities.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { @@ -604,10 +604,9 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Ok(response) => { processed_any = true; if let Some(packet) = response { - match inner.dispatch(tx_token, timestamp, packet) { - Err(err) => net_debug!("Failed to send response: {}", err), - _ => {}, - }; + if let Err(err) = inner.dispatch(tx_token, timestamp, packet) { + net_debug!("Failed to send response: {}", err); + } } } Err(err) => { @@ -624,10 +623,9 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Ok(response) => { processed_any = true; if let Some(packet) = response { - match inner.dispatch_ip(tx_token, timestamp, packet) { - Err(err) => net_debug!("Failed to send response: {}", err), - _ => {}, - }; + if let Err(err) = inner.dispatch_ip(tx_token, timestamp, packet) { + net_debug!("Failed to send response: {}", err); + } } } Err(err) => net_debug!("cannot process ingress packet: {}", err), @@ -637,9 +635,8 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Ok(()) }) { - Err(err) => net_debug!("Failed to consume RX token: {}", err), - Ok(_) => {}, - }; + net_debug!("Failed to consume RX token: {}", err); + } } processed_any From 4749c328b16aace563e64d69ebb10747ff5c3f6a Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 18:04:02 +0200 Subject: [PATCH 139/566] Updating UDP close to clear RX/TX buffers --- src/socket/udp.rs | 11 +++++++++++ src/storage/packet_buffer.rs | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 34071f03f..f39b0ddb4 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -148,7 +148,18 @@ impl<'a> UdpSocket<'a> { /// Close the socket. pub fn close(&mut self) { + // Clear the bound endpoint of the socket. self.endpoint = IpEndpoint::default(); + + // Reset the RX and TX buffers of the socket. + self.tx_buffer.reset(); + self.rx_buffer.reset(); + + #[cfg(feature = "async")] + { + self.rx_waker.wake(); + self.tx_waker.wake(); + } } /// Check whether the socket is open. diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 5502de2fe..8ad763762 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -179,6 +179,12 @@ impl<'a, H> PacketBuffer<'a, H> { pub fn payload_capacity(&self) -> usize { self.payload_ring.capacity() } + + /// Reset the packet buffer and clear any staged. + pub(crate) fn reset(&mut self) { + self.payload_ring.clear(); + self.metadata_ring.clear(); + } } #[cfg(test)] @@ -304,4 +310,18 @@ mod test { assert!(buffer.dequeue().is_ok()); assert!(buffer.enqueue(5, ()).is_ok()); } + + #[test] + fn clear() { + let mut buffer = buffer(); + + // Ensure enqueuing data in teh buffer fills it somewhat. + assert!(buffer.is_empty()); + assert!(buffer.enqueue(6, ()).is_ok()); + + // Ensure that resetting the buffer causes it to be empty. + assert!(!buffer.is_empty()); + buffer.reset(); + assert!(buffer.is_empty()); + } } From 058e5b78861e3b96a41eaa94203833b287e4c4f0 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 31 May 2021 18:07:19 +0200 Subject: [PATCH 140/566] Updating packet buffer clear to be UDP-only --- src/storage/packet_buffer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 8ad763762..c0c56bec6 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -181,6 +181,7 @@ impl<'a, H> PacketBuffer<'a, H> { } /// Reset the packet buffer and clear any staged. + #[cfg(feature(socket-udp))] pub(crate) fn reset(&mut self) { self.payload_ring.clear(); self.metadata_ring.clear(); From 52d880206965c6563fbf3896ff97dc6bc002eb34 Mon Sep 17 00:00:00 2001 From: qiujiangkun Date: Tue, 8 Jun 2021 14:34:38 +0800 Subject: [PATCH 141/566] add std::error::Error to smoltcp::Error --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 028edf89c..9b83b4769 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -160,6 +160,9 @@ pub enum Error { Dropped, } +#[cfg(feature = "std")] +impl std::error::Error for Error {} + /// The result type for the networking stack. pub type Result = core::result::Result; From c0235f1548d87ae73ad403adb5c36597d59e5be8 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 9 Jun 2021 11:13:56 +0200 Subject: [PATCH 142/566] Fixing feature flag --- src/storage/packet_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index c0c56bec6..5672eabe8 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -181,7 +181,7 @@ impl<'a, H> PacketBuffer<'a, H> { } /// Reset the packet buffer and clear any staged. - #[cfg(feature(socket-udp))] + #[cfg(feature = "socket-udp")] pub(crate) fn reset(&mut self) { self.payload_ring.clear(); self.metadata_ring.clear(); From 02d565a3a3fd5c9fae9c138eec59f98dfa42a848 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 9 Jun 2021 11:30:02 +0200 Subject: [PATCH 143/566] Restructuring to allow-unused --- src/storage/packet_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 5672eabe8..b35d0ab68 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -181,7 +181,7 @@ impl<'a, H> PacketBuffer<'a, H> { } /// Reset the packet buffer and clear any staged. - #[cfg(feature = "socket-udp")] + #[allow(unused)] pub(crate) fn reset(&mut self) { self.payload_ring.clear(); self.metadata_ring.clear(); From fb0ac237759fe72ea0326c0dd198ad01843bebfc Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 9 Jun 2021 11:36:28 +0200 Subject: [PATCH 144/566] Sanitizing log macro to allow usage in match arm directly --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index f24187191..28e49ee25 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -13,7 +13,7 @@ macro_rules! net_log { #[cfg(not(any(feature = "log", feature = "defmt")))] macro_rules! net_log { - ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* } + ($level:ident, $($arg:expr),*) => {{ $( let _ = $arg; )* }} } macro_rules! net_trace { From e798dfddb97ee63f0c22e72b9573ca86341d8892 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 11 Jun 2021 11:25:30 +0200 Subject: [PATCH 145/566] Fix "subtract sequence numbers with underflow" on remote window shrink. Fixes #489 --- src/socket/tcp.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index dbdf8ff39..0b9a959ad 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1791,11 +1791,33 @@ impl<'a> TcpSocket<'a> { State::Established | State::FinWait1 | State::Closing | State::CloseWait | State::LastAck => { // Extract as much data as the remote side can receive in this packet // from the transmit buffer. + + // Right edge of window, ie the max sequence number we're allowed to send. + let win_right_edge = self.local_seq_no + self.remote_win_len; + + // Max amount of octets we're allowed to send according to the remote window. + let win_limit = if win_right_edge >= self.remote_last_seq { + win_right_edge - self.remote_last_seq + } else { + // This can happen if we've sent some data and later the remote side + // has shrunk its window so that data is no longer inside the window. + // This should be very rare and is strongly discouraged by the RFCs, + // but it does happen in practice. + // http://www.tcpipguide.com/free/t_TCPWindowManagementIssues.htm + 0 + }; + + // Maximum size we're allowed to send. This can be limited by 3 factors: + // 1. remote window + // 2. MSS the remote is willing to accept, probably determined by their MTU + // 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()); + let offset = self.remote_last_seq - self.local_seq_no; - let win_limit = self.local_seq_no + self.remote_win_len - self.remote_last_seq; - let size = cmp::min(cmp::min(win_limit, self.remote_mss), - ip_mtu - ip_repr.buffer_len() - repr.mss_header_len()); repr.payload = self.tx_buffer.get_allocated(offset, size); + // If we've sent everything we had in the buffer, follow it with the PSH or FIN // flags, depending on whether the transmit half of the connection is open. if offset + repr.payload.len() == self.tx_buffer.len() { @@ -3007,6 +3029,48 @@ mod test { }]); } + #[test] + fn test_established_send_window_shrink() { + let mut s = socket_established(); + + // 6 octets fit on the remote side's window, so we send them. + s.send_slice(b"abcdef").unwrap(); + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }]); + assert_eq!(s.tx_buffer.len(), 6); + + println!("local_seq_no={} remote_win_len={} remote_last_seq={}", s.local_seq_no, s.remote_win_len, s.remote_last_seq); + + // - Peer doesn't ack them yet + // - Sends data so we need to reply with an ACK + // - ...AND and sends a window announcement that SHRINKS the window, so data we've + // previously sent is now outside the window. Yes, this is allowed by TCP. + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_len: 3, + payload: &b"xyzxyz"[..], + ..SEND_TEMPL + }); + assert_eq!(s.tx_buffer.len(), 6); + + println!("local_seq_no={} remote_win_len={} remote_last_seq={}", s.local_seq_no, s.remote_win_len, s.remote_last_seq); + + // More data should not get sent since it doesn't fit in the window + s.send_slice(b"foobar").unwrap(); + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 64 - 6, + ..RECV_TEMPL + }]); + } + + #[test] fn test_established_send_wrap() { let mut s = socket_established(); From 6d87df0665010f6c7f85dc3776448811638d015a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 11 Jun 2021 22:17:24 +0200 Subject: [PATCH 146/566] tcp: extract ack_min, ack_max vars --- src/socket/tcp.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 0b9a959ad..65488d49b 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1188,17 +1188,22 @@ impl<'a> TcpSocket<'a> { // Every acknowledgement must be for transmitted but unacknowledged data. (_, &TcpRepr { ack_number: Some(ack_number), .. }) => { let unacknowledged = self.tx_buffer.len() + control_len; - if ack_number < self.local_seq_no { + + // Acceptable ACK range (both inclusive) + let ack_min = self.local_seq_no; + let ack_max = self.local_seq_no + unacknowledged; + + if ack_number < ack_min { net_debug!("{}:{}:{}: duplicate ACK ({} not in {}...{})", self.meta.handle, self.local_endpoint, self.remote_endpoint, - ack_number, self.local_seq_no, self.local_seq_no + unacknowledged); + ack_number, ack_min, ack_max); return Err(Error::Dropped) } - if ack_number > self.local_seq_no + unacknowledged { + if ack_number > ack_max { net_debug!("{}:{}:{}: unacceptable ACK ({} not in {}...{})", self.meta.handle, self.local_endpoint, self.remote_endpoint, - ack_number, self.local_seq_no, self.local_seq_no + unacknowledged); + ack_number, ack_min, ack_max); return Ok(Some(self.ack_reply(ip_repr, &repr))) } } From c3a0602454fd638a0d7a3e70e5bbb0263e0d27d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 11 Jun 2021 22:17:59 +0200 Subject: [PATCH 147/566] tcp: fix substract with overflow when receiving a SYNACK with unincremented ACK number. --- src/socket/tcp.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 65488d49b..7f0c4e250 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1185,6 +1185,16 @@ impl<'a> TcpSocket<'a> { self.abort(); return Err(Error::Dropped) } + // SYN|ACK in the SYN-SENT state must have the exact ACK number. + (State::SynSent, &TcpRepr { + control: TcpControl::Syn, ack_number: Some(ack_number), .. + }) => { + if ack_number != self.local_seq_no + 1 { + net_debug!("{}:{}:{}: unacceptable SYN|ACK in response to initial SYN", + self.meta.handle, self.local_endpoint, self.remote_endpoint); + return Err(Error::Dropped) + } + } // Every acknowledgement must be for transmitted but unacknowledged data. (_, &TcpRepr { ack_number: Some(ack_number), .. }) => { let unacknowledged = self.tx_buffer.len() + control_len; @@ -2741,6 +2751,29 @@ mod test { sanity!(s, socket_established()); } + #[test] + fn test_syn_sent_syn_ack_not_incremented() { + let mut s = socket_syn_sent(); + recv!(s, [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }]); + send!(s, TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ), // WRONG + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(0), + ..SEND_TEMPL + }, Err(Error::Dropped)); + assert_eq!(s.state, State::SynSent); + } + #[test] fn test_syn_sent_rst() { let mut s = socket_syn_sent(); From e533b0f83e7fa07482a586fa6fc417de53ef0cb1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 11 Jun 2021 22:33:13 +0200 Subject: [PATCH 148/566] tcp: use nonzero initial sequence number. --- src/socket/tcp.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 7f0c4e250..b1b00ee36 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -54,6 +54,11 @@ impl fmt::Display for State { } } +/// Initial sequence number. This used to be 0, but some servers don't behave correctly +/// with that, so we use a non-zero starting sequence number. TODO: randomize instead. +/// https://github.com/smoltcp-rs/smoltcp/issues/489 +const INITIAL_SEQ_NO: TcpSeqNumber = TcpSeqNumber(42); + // Conservative initial RTT estimate. const RTTE_INITIAL_RTT: u32 = 300; const RTTE_INITIAL_DEV: u32 = 100; @@ -391,7 +396,7 @@ impl<'a> TcpSocket<'a> { listen_address: IpAddress::default(), local_endpoint: IpEndpoint::default(), remote_endpoint: IpEndpoint::default(), - local_seq_no: TcpSeqNumber::default(), + local_seq_no: INITIAL_SEQ_NO, remote_seq_no: TcpSeqNumber::default(), remote_last_seq: TcpSeqNumber::default(), remote_last_ack: None, @@ -592,7 +597,7 @@ impl<'a> TcpSocket<'a> { self.listen_address = IpAddress::default(); self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); - self.local_seq_no = TcpSeqNumber::default(); + self.local_seq_no = INITIAL_SEQ_NO; self.remote_seq_no = TcpSeqNumber::default(); self.remote_last_seq = TcpSeqNumber::default(); self.remote_last_ack = None; @@ -2845,10 +2850,12 @@ mod test { (1048576, 5), ] { let mut s = socket_with_buffer_sizes(64, *buffer_size); + s.local_seq_no = LOCAL_SEQ; assert_eq!(s.remote_win_shift, *shift_amt); s.connect(REMOTE_END, LOCAL_END).unwrap(); recv!(s, [TcpRepr { control: TcpControl::Syn, + seq_number: LOCAL_SEQ, ack_number: None, max_seg_size: Some(BASE_MSS), window_scale: Some(*shift_amt), From 8dfabdc4764e858036ef07cd9e01c7a23e49b66b Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 14 Jun 2021 10:53:27 +0200 Subject: [PATCH 149/566] Adding support for retrieving socket handle from DHCP sockets --- src/socket/dhcpv4.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index cc61373eb..c60f9889c 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -6,6 +6,7 @@ use crate::wire::{EthernetAddress, IpProtocol, IpAddress, use crate::wire::dhcpv4::{field as dhcpv4_field}; use crate::socket::SocketMeta; use crate::time::{Instant, Duration}; +use crate::socket::SocketHandle; use super::{PollAt, Socket}; @@ -420,6 +421,12 @@ impl Dhcpv4Socket { } } + /// Return the socket handle. + #[inline] + pub fn handle(&self) -> SocketHandle { + self.meta.handle + } + /// Reset state and restart discovery phase. /// /// Use this to speed up acquisition of an address in a new From 9e2701ea5fec263a65dac2f569119ead8bbed617 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 14 Jun 2021 17:49:54 +0200 Subject: [PATCH 150/566] Bumping smoltcp version to 0.8 --- CHANGELOG.md | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dad0d6a57..bbf01bfb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Version bumped to 0.8 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) - udp: Add `close()` method to unbind socket. diff --git a/Cargo.toml b/Cargo.toml index c0d2166b6..54eb6c3b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smoltcp" -version = "0.7.0" +version = "0.8.0" edition = "2018" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." From 4bab7f45c3a98103e21b610b3d0d73348d9545e5 Mon Sep 17 00:00:00 2001 From: Gerd Zellweger Date: Mon, 14 Jun 2021 09:44:05 -0700 Subject: [PATCH 151/566] Update loopback.rs --- src/phy/loopback.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index beeba9f6b..c44c97ce7 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -1,12 +1,7 @@ -#[cfg(feature = "std")] -use std::vec::Vec; -#[cfg(feature = "std")] -use std::collections::VecDeque; -#[cfg(feature = "alloc")] use alloc::vec::Vec; -#[cfg(all(feature = "alloc", not(feature = "rust-1_28")))] +#[cfg(not(feature = "rust-1_28"))] use alloc::collections::VecDeque; -#[cfg(all(feature = "alloc", feature = "rust-1_28"))] +#[cfg(feature = "rust-1_28")] use alloc::VecDeque; use crate::Result; From e36fabce2d19959eacf918fb6837ad114edf8552 Mon Sep 17 00:00:00 2001 From: Gerd Zellweger Date: Mon, 14 Jun 2021 13:39:33 -0700 Subject: [PATCH 152/566] Also import alloc crate for std. --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9b83b4769..0c32c7f58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,6 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::option_map_unit_fn)] #![allow(clippy::unit_arg)] -#[cfg(feature = "alloc")] extern crate alloc; #[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))] From 95f7abff90edc14c8c3835b66e00476ec1805944 Mon Sep 17 00:00:00 2001 From: Gerd Zellweger Date: Mon, 14 Jun 2021 15:16:15 -0700 Subject: [PATCH 153/566] Fix unused extern crate warning with certain build flags. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 0c32c7f58..dac1a0675 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::option_map_unit_fn)] #![allow(clippy::unit_arg)] +#[cfg(any(feature = "std", feature = "alloc"))] extern crate alloc; #[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))] From 8a225837e2cbdf2e69897e4e7b7c969a446c94a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 03:03:33 +0200 Subject: [PATCH 154/566] Add Context struct. --- .github/workflows/test.yml | 2 - src/iface/interface.rs | 301 ++++++++++++++++++++----------------- src/phy/mod.rs | 11 ++ src/socket/dhcpv4.rs | 38 ++--- src/socket/icmp.rs | 60 ++++---- src/socket/mod.rs | 44 +++++- src/socket/raw.rs | 48 +++--- src/socket/tcp.rs | 122 +++++++-------- src/socket/udp.rs | 28 ++-- 9 files changed, 357 insertions(+), 297 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91eccf24d..dafbe2452 100644 --- a/.github/workflows/test.yml +++ b/.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 diff --git a/src/iface/interface.rs b/src/iface/interface.rs index eb6e186e0..f4be49502 100644 --- a/src/iface/interface.rs +++ b/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>(&mut self, addr: T, _timestamp: Instant) -> Result { + 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 { + 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 { + 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 { + fn socket_egress(&mut self, cx: &Context, sockets: &mut SocketSet) -> Result { 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 { + fn igmp_egress(&mut self, cx: &Context, timestamp: Instant) -> Result { 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>> { 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, ð_frame), + self.process_arp(cx.now, ð_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>> { 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>> { 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>> { 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>> { - 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>> { 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>> { 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>> { @@ -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>> { 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>> { 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>> { 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(&mut self, tx_token: Tx, timestamp: Instant, + fn dispatch(&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(&mut self, tx_token: Tx, timestamp: Instant, + fn dispatch_ethernet(&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(&mut self, tx_token: Tx, timestamp: Instant, + fn lookup_hardware_addr(&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(&mut self, tx_token: Tx, timestamp: Instant, + fn dispatch_ip(&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)); { diff --git a/src/phy/mod.rs b/src/phy/mod.rs index c04867772..50cd774ff 100644 --- a/src/phy/mod.rs +++ b/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))] diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index c60f9889c..7caa16e85 100644 --- a/src/socket/dhcpv4.rs +++ b/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(&mut self, now: Instant, ethernet_addr: EthernetAddress, ip_mtu: usize, emit: F) -> Result<()> + pub(crate) fn dispatch(&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(()) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 9208ca7c5..39c8b5888 100644 --- a/src/socket/icmp.rs +++ b/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(&mut self, emit: F) -> Result<()> + pub(crate) fn dispatch(&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()); diff --git a/src/socket/mod.rs b/src/socket/mod.rs index ebd0c4273..1c01efb05 100644 --- a/src/socket/mod.rs +++ b/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, + 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}, + }; + +} \ No newline at end of file diff --git a/src/socket/raw.rs b/src/socket/raw.rs index e4e60edb6..0baa70238 100644 --- a/src/socket/raw.rs +++ b/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(&mut self, checksum_caps: &ChecksumCapabilities, emit: F) -> - Result<()> + pub(crate) fn dispatch(&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()); diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index b1b00ee36..f6b499299 100644 --- a/src/socket/tcp.rs +++ b/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)>> { 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(&mut self, timestamp: Instant, ip_mtu: usize, - emit: F) -> Result<()> + pub(crate) fn dispatch(&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(socket: &mut TcpSocket, timestamp: Instant, mut f: F) where F: FnMut(Result) { - 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(())); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index f39b0ddb4..750371107 100644 --- a/src/socket/udp.rs +++ b/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(&mut self, emit: F) -> Result<()> + pub(crate) fn dispatch(&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))); } From 6705d9f5466cad3ab1568e1cc1247d7367b29e04 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 03:22:02 +0200 Subject: [PATCH 155/566] Check that at least one medium is enabled if socket is enabled. --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index dac1a0675..4c8e93fba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,15 @@ compile_error!("You must enable at least one of the following features: proto-ip ))] compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp"); +#[cfg(all( + feature = "socket", + not(any( + feature = "medium-ethernet", + feature = "medium-ip", + )) +))] +compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet"); + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You must enable at most one of the following features: defmt, log"); From b82b7300aa4075e57de3171d5807324c22fdd09d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 04:44:13 +0200 Subject: [PATCH 156/566] tcp: add Nagle's Algorithm. --- src/socket/tcp.rs | 144 ++++++++++++++++++++++++++++++++++++++++------ src/wire/ipv4.rs | 3 + src/wire/ipv6.rs | 3 + src/wire/mod.rs | 7 ++- src/wire/tcp.rs | 10 +--- 5 files changed, 140 insertions(+), 27 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f6b499299..4eec437fd 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -12,7 +12,7 @@ use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context}; use crate::storage::{Assembler, RingBuffer}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl}; +use crate::wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl, TCP_HEADER_LEN}; /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -354,6 +354,9 @@ pub struct TcpSocket<'a> { /// ACK or window updates (ie, no data) won't be sent until expiry. ack_delay_until: Option, + /// Nagle's Algorithm enabled. + nagle: bool, + #[cfg(feature = "async")] rx_waker: WakerRegistration, #[cfg(feature = "async")] @@ -412,6 +415,7 @@ impl<'a> TcpSocket<'a> { local_rx_dup_acks: 0, ack_delay: Some(ACK_DELAY_DEFAULT), ack_delay_until: None, + nagle: true, #[cfg(feature = "async")] rx_waker: WakerRegistration::new(), @@ -475,6 +479,13 @@ impl<'a> TcpSocket<'a> { self.ack_delay } + /// Return whether Nagle's Algorithm is enabled. + /// + /// See also the [set_nagle_enabled](#method.set_nagle_enabled) method. + pub fn nagle_enabled(&self) -> Option { + self.ack_delay + } + /// Return the current window field value, including scaling according to RFC 1323. /// /// Used in internal calculations as well as packet generation. @@ -507,6 +518,22 @@ impl<'a> TcpSocket<'a> { self.ack_delay = duration } + /// Enable or disable Nagle's Algorithm. + /// + /// Also known as "tinygram prevention". By default, it is enabled. + /// Disabling it is equivalent to Linux's TCP_NODELAY flag. + /// + /// When enabled, Nagle's Algorithm prevents sending segments smaller than MSS if + /// there is data in flight (sent but not acknowledged). In other words, it ensures + /// at most only one segment smaller than MSS is in flight at a time. + /// + /// It ensures better network utilization by preventing sending many very small packets, + /// at the cost of increased latency in some situations, particularly when the remote peer + /// has ACK delay enabled. + pub fn set_nagle_enabled(&mut self, enabled: bool) { + self.nagle = enabled + } + /// Return the keep-alive interval. /// /// See also the [set_keep_alive](#method.set_keep_alive) method. @@ -609,6 +636,7 @@ impl<'a> TcpSocket<'a> { self.remote_last_ts = None; self.ack_delay = Some(ACK_DELAY_DEFAULT); self.ack_delay_until = None; + self.nagle = true; #[cfg(feature = "async")] { @@ -1639,12 +1667,38 @@ impl<'a> TcpSocket<'a> { } } - fn seq_to_transmit(&self) -> bool { - // We can send data if we have data that: - // - hasn't been sent before - // - fits in the remote window - let can_data = self.remote_last_seq - < self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); + fn seq_to_transmit(&self, cx: &Context) -> bool { + let ip_header_len = match self.local_endpoint.addr { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(_) => crate::wire::IPV4_HEADER_LEN, + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(_) => crate::wire::IPV6_HEADER_LEN, + IpAddress::Unspecified => unreachable!(), + }; + + // Max segment size we're able to send due to MTU limitations. + let local_mss = cx.caps.ip_mtu() - ip_header_len - TCP_HEADER_LEN; + + // The effective max segment size, taking into account our and remote's limits. + let effective_mss = local_mss.min(self.remote_mss); + + // Have we sent data that hasn't been ACKed yet? + let data_in_flight = self.remote_last_seq != self.local_seq_no; + + // max sequence number we can send. + let max_send_seq = self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); + + // Max amount of octets we can send. + let max_send = if max_send_seq >= self.remote_last_seq { + max_send_seq - self.remote_last_seq + } else { + 0 + }; + + // Can we send at least 1 octet? + let mut can_send = max_send != 0; + // Can we send at least 1 full segment? + let can_send_full = max_send >= effective_mss; // Do we have to send a FIN? let want_fin = match self.state { @@ -1654,6 +1708,10 @@ impl<'a> TcpSocket<'a> { _ => false, }; + if self.nagle && data_in_flight && !can_send_full { + can_send = false; + } + // Can we actually send the FIN? We can send it if: // 1. We have unsent data that fits in the remote window. // 2. We have no unsent data. @@ -1661,7 +1719,7 @@ impl<'a> TcpSocket<'a> { let can_fin = want_fin && self.remote_last_seq == self.local_seq_no + self.tx_buffer.len(); - can_data || can_fin + can_send || can_fin } fn delayed_ack_expired(&self, timestamp: Instant) -> bool { @@ -1708,7 +1766,7 @@ impl<'a> TcpSocket<'a> { net_debug!("{}:{}:{}: timeout exceeded", self.meta.handle, self.local_endpoint, self.remote_endpoint); self.set_state(State::Closed); - } else if !self.seq_to_transmit() { + } else if !self.seq_to_transmit(cx) { 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+{}", @@ -1720,7 +1778,7 @@ impl<'a> TcpSocket<'a> { } // Decide whether we're sending a packet. - if self.seq_to_transmit() { + if self.seq_to_transmit(cx) { // 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); @@ -1832,7 +1890,7 @@ impl<'a> TcpSocket<'a> { // 3. MSS we can send, determined by our MTU. let size = win_limit .min(self.remote_mss) - .min(cx.caps.ip_mtu() - ip_repr.buffer_len() - repr.mss_header_len()); + .min(cx.caps.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN); let offset = self.remote_last_seq - self.local_seq_no; repr.payload = self.tx_buffer.get_allocated(offset, size); @@ -1894,9 +1952,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 = cx.caps.ip_mtu(); - max_segment_size -= ip_repr.buffer_len(); - max_segment_size -= repr.mss_header_len(); + let max_segment_size = cx.caps.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN; repr.max_seg_size = Some(max_segment_size as u16); } @@ -1936,7 +1992,7 @@ impl<'a> TcpSocket<'a> { self.rtte.on_send(cx.now, repr.seq_number + repr.segment_len()); } - if !self.seq_to_transmit() && repr.segment_len() > 0 { + if !self.seq_to_transmit(cx) && 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(cx.now, self.rtte.retransmission_timeout()); @@ -1952,7 +2008,7 @@ impl<'a> TcpSocket<'a> { } #[allow(clippy::if_same_then_else)] - pub(crate) fn poll_at(&self, _cx: &Context) -> 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. @@ -1963,7 +2019,7 @@ impl<'a> TcpSocket<'a> { } else if self.state == State::Closed { // Socket was aborted, we have an RST packet to transmit. PollAt::Now - } else if self.seq_to_transmit() { + } else if self.seq_to_transmit(cx) { // We have a data or flag packet to transmit. PollAt::Now } else { @@ -3043,6 +3099,7 @@ mod test { #[test] fn test_established_send_no_ack_send() { let mut s = socket_established(); + s.set_nagle_enabled(false); s.send_slice(b"abcdef").unwrap(); recv!(s, [TcpRepr { seq_number: LOCAL_SEQ + 1, @@ -5121,6 +5178,8 @@ mod test { #[test] fn test_buffer_wraparound_tx() { let mut s = socket_established(); + s.set_nagle_enabled(false); + s.tx_buffer = SocketBuffer::new(vec![b'.'; 9]); assert_eq!(s.send_slice(b"xxxyyy"), Ok(6)); assert_eq!(s.tx_buffer.dequeue_many(3), &b"xxx"[..]); @@ -5409,6 +5468,57 @@ mod test { })); } + // =========================================================================================// + // Tests for Nagle's Algorithm + // =========================================================================================// + + #[test] + fn test_nagle() { + let mut s = socket_established(); + s.remote_mss = 6; + + s.send_slice(b"abcdef").unwrap(); + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }]); + + // If there's data in flight, full segments get sent. + s.send_slice(b"foobar").unwrap(); + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"foobar"[..], + ..RECV_TEMPL + }]); + + s.send_slice(b"aaabbbccc").unwrap(); + // If there's data in flight, not-full segments don't get sent. + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"aaabbb"[..], + ..RECV_TEMPL + }]); + + // Data gets ACKd, so there's no longer data in flight + send!(s, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 6 + 6), + ..SEND_TEMPL + }); + + // Now non-full segment gets sent. + recv!(s, [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 6 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"ccc"[..], + ..RECV_TEMPL + }]); + } + // =========================================================================================// // Tests for packet filtering. // =========================================================================================// diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 687b4b9ef..221d656b1 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -263,6 +263,9 @@ mod field { pub const DST_ADDR: Field = 16..20; } +pub const HEADER_LEN: usize = field::DST_ADDR.end; + + impl> Packet { /// Imbue a raw octet buffer with IPv4 packet structure. pub fn new_unchecked(buffer: T) -> Packet { diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 6f54a0c10..575219c54 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -380,6 +380,9 @@ mod field { pub const DST_ADDR: Field = 24..40; } +/// Length of an IPv6 header. +pub const HEADER_LEN: usize = field::DST_ADDR.end; + impl> Packet { /// Create a raw octet buffer with an IPv6 packet structure. #[inline] diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 7fb37b54e..7a7f39c75 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -140,13 +140,15 @@ pub use self::ipv4::{Address as Ipv4Address, Packet as Ipv4Packet, Repr as Ipv4Repr, Cidr as Ipv4Cidr, - MIN_MTU as IPV4_MIN_MTU}; + HEADER_LEN as IPV4_HEADER_LEN, + MIN_MTU as IPV4_MIN_MTU}; #[cfg(feature = "proto-ipv6")] pub use self::ipv6::{Address as Ipv6Address, Packet as Ipv6Packet, Repr as Ipv6Repr, Cidr as Ipv6Cidr, + HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU}; #[cfg(feature = "proto-ipv6")] @@ -218,7 +220,8 @@ pub use self::tcp::{SeqNumber as TcpSeqNumber, Packet as TcpPacket, TcpOption, Repr as TcpRepr, - Control as TcpControl}; + Control as TcpControl, + HEADER_LEN as TCP_HEADER_LEN}; #[cfg(feature = "proto-dhcpv4")] pub use self::dhcpv4::{Packet as DhcpPacket, diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index c0fff417b..d5622d97f 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -109,6 +109,8 @@ mod field { pub const OPT_SACKRNG: u8 = 0x05; } +pub const HEADER_LEN: usize = field::URGENT.end; + impl> Packet { /// Imbue a raw octet buffer with TCP packet structure. pub fn new_unchecked(buffer: T) -> Packet { @@ -857,14 +859,6 @@ impl<'a> Repr<'a> { length } - /// Return the length of the header for the TCP protocol. - /// - /// Per RFC 6691, this should be used for MSS calculations. It may be smaller than the buffer - /// space required to accomodate this packet's data. - pub fn mss_header_len(&self) -> usize { - field::URGENT.end - } - /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { self.header_len() + self.payload.len() From 2ac0321db9b8d9a9e4f4246d7363bd09b79e4484 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 05:56:01 +0200 Subject: [PATCH 157/566] tcp: fix window scaling of synack being ignored. --- src/socket/tcp.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 4eec437fd..0e6b676d2 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1411,6 +1411,7 @@ impl<'a> TcpSocket<'a> { self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no + 1; self.remote_last_ack = Some(repr.seq_number); + self.remote_win_scale = repr.window_scale; if let Some(max_seg_size) = repr.max_seg_size { self.remote_mss = max_seg_size as usize; } @@ -2925,6 +2926,32 @@ mod test { } } + + #[test] + fn test_syn_sent_syn_ack_window_scaling() { + let mut s = socket_syn_sent(); + recv!(s, [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }]); + send!(s, TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(7), + window_len: 42, + ..SEND_TEMPL + }); + assert_eq!(s.state, State::Established); + assert_eq!(s.remote_win_scale, Some(7)); + } + // =========================================================================================// // Tests for the ESTABLISHED state. // =========================================================================================// From 9ca8408d6ba4e2713eab170a0b34c048f52f9596 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 06:02:36 +0200 Subject: [PATCH 158/566] tcp: don't do window scaling when connecting if remote doesn't support it. --- src/socket/tcp.rs | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 0e6b676d2..116a7cc52 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1379,7 +1379,7 @@ impl<'a> TcpSocket<'a> { self.remote_mss = max_seg_size as usize } self.remote_win_scale = repr.window_scale; - // No window scaling means don't do any window shifting + // Remote doesn't support window scaling, don't do it. if self.remote_win_scale.is_none() { self.remote_win_shift = 0; } @@ -1412,6 +1412,11 @@ impl<'a> TcpSocket<'a> { self.remote_last_seq = self.local_seq_no + 1; self.remote_last_ack = Some(repr.seq_number); self.remote_win_scale = repr.window_scale; + // Remote doesn't support window scaling, don't do it. + if self.remote_win_scale.is_none() { + self.remote_win_shift = 0; + } + if let Some(max_seg_size) = repr.max_seg_size { self.remote_mss = max_seg_size as usize; } @@ -2276,8 +2281,11 @@ mod test { socket_syn_received_with_buffer_sizes(64, 64) } - fn socket_syn_sent() -> TcpSocket<'static> { - let mut s = socket(); + fn socket_syn_sent_with_buffer_sizes( + tx_len: usize, + rx_len: usize + ) -> TcpSocket<'static> { + let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynSent; s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT); s.remote_endpoint = REMOTE_END; @@ -2286,6 +2294,10 @@ mod test { s } + fn socket_syn_sent() -> TcpSocket<'static> { + socket_syn_sent_with_buffer_sizes(64, 64) + } + fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TcpSocket<'static> { let mut s = socket(); s.state = State::SynSent; @@ -2663,6 +2675,7 @@ mod test { window_scale: None, ..SEND_TEMPL }); + assert_eq!(s.remote_win_shift, 0); assert_eq!(s.remote_win_scale, None); } @@ -2926,6 +2939,33 @@ mod test { } } + #[test] + fn test_syn_sent_syn_ack_no_window_scaling() { + let mut s = socket_syn_sent_with_buffer_sizes(1048576, 1048576); + recv!(s, [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_len: 32768, + window_scale: Some(5), + sack_permitted: true, + ..RECV_TEMPL + }]); + assert_eq!(s.remote_win_shift, 5); + send!(s, TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: None, + window_len: 42, + ..SEND_TEMPL + }); + assert_eq!(s.state, State::Established); + assert_eq!(s.remote_win_shift, 0); + assert_eq!(s.remote_win_scale, None); + } #[test] fn test_syn_sent_syn_ack_window_scaling() { From 6a08274558bdcc0decd6f86f93d640a8e6fd6b0b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Jun 2021 06:22:30 +0200 Subject: [PATCH 159/566] tcp: do not scale window in SYN packets. --- src/socket/tcp.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 116a7cc52..35e60ebfa 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1515,7 +1515,11 @@ impl<'a> TcpSocket<'a> { // 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. - self.remote_win_len = (repr.window_len as usize) << (self.remote_win_scale.unwrap_or(0) as usize); + let scale = match repr.control { + TcpControl::Syn => 0, + _ => self.remote_win_scale.unwrap_or(0), + }; + self.remote_win_len = (repr.window_len as usize) << (scale as usize); if ack_len > 0 { // Dequeue acknowledged octets. @@ -1746,7 +1750,7 @@ impl<'a> TcpSocket<'a> { fn window_to_update(&self) -> bool { match self.state { State::SynSent | State::SynReceived | State::Established | State::FinWait1 | State::FinWait2 => - (self.rx_buffer.window() >> self.remote_win_shift) as u16 > self.remote_last_win, + self.scaled_window() > self.remote_last_win, _ => false, } } @@ -1858,6 +1862,8 @@ impl<'a> TcpSocket<'a> { // We transmit a SYN|ACK in the SYN-RECEIVED state. State::SynSent | State::SynReceived => { repr.control = TcpControl::Syn; + // window len must NOT be scaled in SYNs. + repr.window_len = self.rx_buffer.window().min((1<<16)-1) as u16; if self.state == State::SynSent { repr.ack_number = None; repr.window_scale = Some(self.remote_win_shift); @@ -2503,7 +2509,7 @@ mod test { ack_number: Some(REMOTE_SEQ + 1), max_seg_size: Some(BASE_MSS), window_scale: Some(*shift_amt), - window_len: cmp::min(*buffer_size >> *shift_amt, 65535) as u16, + window_len: cmp::min(*buffer_size, 65535) as u16, ..RECV_TEMPL }]); } @@ -2932,7 +2938,7 @@ mod test { ack_number: None, max_seg_size: Some(BASE_MSS), window_scale: Some(*shift_amt), - window_len: cmp::min(*buffer_size >> *shift_amt, 65535) as u16, + window_len: cmp::min(*buffer_size, 65535) as u16, sack_permitted: true, ..RECV_TEMPL }]); @@ -2947,7 +2953,8 @@ mod test { seq_number: LOCAL_SEQ, ack_number: None, max_seg_size: Some(BASE_MSS), - window_len: 32768, + // scaling does NOT apply to the window value in SYN packets + window_len: 65535, window_scale: Some(5), sack_permitted: true, ..RECV_TEMPL @@ -2965,6 +2972,7 @@ mod test { assert_eq!(s.state, State::Established); assert_eq!(s.remote_win_shift, 0); assert_eq!(s.remote_win_scale, None); + assert_eq!(s.remote_win_len, 42); } #[test] @@ -2990,6 +2998,8 @@ mod test { }); assert_eq!(s.state, State::Established); assert_eq!(s.remote_win_scale, Some(7)); + // scaling does NOT apply to the window value in SYN packets + assert_eq!(s.remote_win_len, 42); } // =========================================================================================// From 2cb407d62af97b960aa1be6549cbbb13d9571b91 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Jun 2021 09:31:59 +0200 Subject: [PATCH 160/566] rustfmt --- benches/bench.rs | 78 +- examples/benchmark.rs | 58 +- examples/client.rs | 54 +- examples/dhcp_client.rs | 23 +- examples/httpclient.rs | 57 +- examples/loopback.rs | 48 +- examples/multicast.rs | 56 +- examples/ping.rs | 187 +- examples/server.rs | 104 +- examples/tcpdump.rs | 21 +- examples/utils.rs | 178 +- src/iface/interface.rs | 1933 ++++++++----- src/iface/mod.rs | 8 +- src/iface/neighbor.rs | 203 +- src/iface/route.rs | 162 +- src/lib.rs | 37 +- src/macros.rs | 1 - src/parsers.rs | 344 ++- src/phy/fault_injector.rs | 129 +- src/phy/fuzz_injector.rs | 49 +- src/phy/loopback.rs | 14 +- src/phy/mod.rs | 54 +- src/phy/pcap_writer.rs | 100 +- src/phy/raw_socket.rs | 36 +- src/phy/sys/bpf.rs | 2 +- src/phy/sys/linux.rs | 12 +- src/phy/sys/mod.rs | 103 +- src/phy/sys/raw_socket.rs | 74 +- src/phy/sys/tuntap_interface.rs | 48 +- src/phy/tracer.rs | 129 +- src/phy/tuntap_interface.rs | 32 +- src/socket/dhcpv4.rs | 133 +- src/socket/icmp.rs | 519 ++-- src/socket/meta.rs | 58 +- src/socket/mod.rs | 71 +- src/socket/raw.rs | 324 ++- src/socket/ref_.rs | 10 +- src/socket/set.rs | 80 +- src/socket/tcp.rs | 4646 +++++++++++++++++++------------ src/socket/udp.rs | 360 ++- src/socket/waker.rs | 2 +- src/storage/assembler.rs | 74 +- src/storage/mod.rs | 4 +- src/storage/packet_buffer.rs | 109 +- src/storage/ring_buffer.rs | 134 +- src/time.rs | 71 +- src/wire/arp.rs | 146 +- src/wire/dhcpv4.rs | 306 +- src/wire/ethernet.rs | 138 +- src/wire/icmpv4.rs | 257 +- src/wire/icmpv6.rs | 521 ++-- src/wire/igmp.rs | 81 +- src/wire/ip.rs | 596 ++-- src/wire/ipv4.rs | 494 ++-- src/wire/ipv6.rs | 485 ++-- src/wire/ipv6fragment.rs | 81 +- src/wire/ipv6hopbyhop.rs | 132 +- src/wire/ipv6option.rs | 183 +- src/wire/ipv6routing.rs | 225 +- src/wire/mld.rs | 246 +- src/wire/mod.rs | 171 +- src/wire/ndisc.rs | 210 +- src/wire/ndiscoption.rs | 237 +- src/wire/pretty_print.rs | 21 +- src/wire/tcp.rs | 548 ++-- src/wire/udp.rs | 141 +- utils/packet2pcap.rs | 47 +- 67 files changed, 9851 insertions(+), 6344 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index b1ddc8528..943a5421b 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,22 +1,24 @@ #![feature(test)] mod wire { - use test; - #[cfg(feature = "proto-ipv6")] - use smoltcp::wire::{Ipv6Address, Ipv6Repr, Ipv6Packet}; - #[cfg(feature = "proto-ipv4")] - use smoltcp::wire::{Ipv4Address, Ipv4Repr, Ipv4Packet}; - use smoltcp::phy::{ChecksumCapabilities}; + use smoltcp::phy::ChecksumCapabilities; use smoltcp::wire::{IpAddress, IpProtocol}; - use smoltcp::wire::{TcpRepr, TcpPacket, TcpSeqNumber, TcpControl}; - use smoltcp::wire::{UdpRepr, UdpPacket}; + #[cfg(feature = "proto-ipv4")] + use smoltcp::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr}; + #[cfg(feature = "proto-ipv6")] + use smoltcp::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr}; + use smoltcp::wire::{TcpControl, TcpPacket, TcpRepr, TcpSeqNumber}; + use smoltcp::wire::{UdpPacket, UdpRepr}; + use test; #[cfg(feature = "proto-ipv6")] - const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1])); + const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ])); #[cfg(feature = "proto-ipv6")] - const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2])); + const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + ])); #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] const SRC_ADDR: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1])); @@ -26,42 +28,50 @@ mod wire { #[bench] #[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))] fn bench_emit_tcp(b: &mut test::Bencher) { - static PAYLOAD_BYTES: [u8; 400] = - [0x2a; 400]; + static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400]; let repr = TcpRepr { - src_port: 48896, - dst_port: 80, - seq_number: TcpSeqNumber(0x01234567), - ack_number: None, - window_len: 0x0123, - control: TcpControl::Syn, + src_port: 48896, + dst_port: 80, + seq_number: TcpSeqNumber(0x01234567), + ack_number: None, + window_len: 0x0123, + control: TcpControl::Syn, max_seg_size: None, window_scale: None, - payload: &PAYLOAD_BYTES + payload: &PAYLOAD_BYTES, }; let mut bytes = vec![0xa5; repr.buffer_len()]; b.iter(|| { let mut packet = TcpPacket::new(&mut bytes); - repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default()); + repr.emit( + &mut packet, + &SRC_ADDR, + &DST_ADDR, + &ChecksumCapabilities::default(), + ); }); } #[bench] #[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))] fn bench_emit_udp(b: &mut test::Bencher) { - static PAYLOAD_BYTES: [u8; 400] = - [0x2a; 400]; + static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400]; let repr = UdpRepr { src_port: 48896, dst_port: 80, - payload: &PAYLOAD_BYTES + payload: &PAYLOAD_BYTES, }; let mut bytes = vec![0xa5; repr.buffer_len()]; b.iter(|| { let mut packet = UdpPacket::new(&mut bytes); - repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default()); + repr.emit( + &mut packet, + &SRC_ADDR, + &DST_ADDR, + &ChecksumCapabilities::default(), + ); }); } @@ -69,11 +79,11 @@ mod wire { #[cfg(feature = "proto-ipv4")] fn bench_emit_ipv4(b: &mut test::Bencher) { let repr = Ipv4Repr { - src_addr: Ipv4Address([192, 168, 1, 1]), - dst_addr: Ipv4Address([192, 168, 1, 2]), - protocol: IpProtocol::Tcp, + src_addr: Ipv4Address([192, 168, 1, 1]), + dst_addr: Ipv4Address([192, 168, 1, 2]), + protocol: IpProtocol::Tcp, payload_len: 100, - hop_limit: 64 + hop_limit: 64, }; let mut bytes = vec![0xa5; repr.buffer_len()]; @@ -87,13 +97,11 @@ mod wire { #[cfg(feature = "proto-ipv6")] fn bench_emit_ipv6(b: &mut test::Bencher) { let repr = Ipv6Repr { - src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1]), - dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2]), + src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), + dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]), next_header: IpProtocol::Tcp, payload_len: 100, - hop_limit: 64 + hop_limit: 64, }; let mut bytes = vec![0xa5; repr.buffer_len()]; diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 6527ba4ee..3bb23e925 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -2,28 +2,34 @@ mod utils; +use log::debug; use std::cmp; use std::collections::BTreeMap; -use std::sync::atomic::{Ordering, AtomicBool}; -use std::thread; use std::io::{Read, Write}; use std::net::TcpStream; use std::os::unix::io::AsRawFd; -use log::debug; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; -use smoltcp::phy::{Device, Medium, wait as phy_wait}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; const AMOUNT: usize = 1_000_000_000; -enum Client { Reader, Writer } +enum Client { + Reader, + Writer, +} fn client(kind: Client) { - let port = match kind { Client::Reader => 1234, Client::Writer => 1235 }; + let port = match kind { + Client::Reader => 1234, + Client::Writer => 1235, + }; let mut stream = TcpStream::connect(("192.168.69.1", port)).unwrap(); let mut buffer = vec![0; 1_000_000]; @@ -42,7 +48,7 @@ fn client(kind: Client) { // print!("(P:{})", result); processed += result } - Err(err) => panic!("cannot process: {}", err) + Err(err) => panic!("cannot process: {}", err), } } @@ -69,11 +75,11 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let mode = match matches.free[0].as_ref() { "reader" => Client::Reader, "writer" => Client::Writer, - _ => panic!("invalid mode") + _ => panic!("invalid mode"), }; thread::spawn(move || client(mode)); @@ -91,8 +97,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -109,13 +114,12 @@ fn main() { while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { - debug!("poll error: {}",e); + debug!("poll error: {}", e); } } - // tcp:1234: emit data { let mut socket = sockets.get::(tcp1_handle); @@ -125,10 +129,12 @@ fn main() { if socket.can_send() { if processed < AMOUNT { - let length = socket.send(|buffer| { - let length = cmp::min(buffer.len(), AMOUNT - processed); - (length, length) - }).unwrap(); + let length = socket + .send(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); processed += length; } } @@ -143,10 +149,12 @@ fn main() { if socket.can_recv() { if processed < AMOUNT { - let length = socket.recv(|buffer| { - let length = cmp::min(buffer.len(), AMOUNT - processed); - (length, length) - }).unwrap(); + let length = socket + .recv(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); processed += length; } } @@ -155,7 +163,7 @@ fn main() { match iface.poll_at(&sockets, timestamp) { Some(poll_at) if timestamp < poll_at => { phy_wait(fd, Some(poll_at - timestamp)).expect("wait error"); - }, + } Some(_) => (), None => { phy_wait(fd, default_timeout).expect("wait error"); diff --git a/examples/client.rs b/examples/client.rs index 308ec991b..19e1417e4 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,15 +1,15 @@ mod utils; -use std::str::{self, FromStr}; +use log::debug; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use log::debug; +use std::str::{self, FromStr}; -use smoltcp::phy::{Device, Medium, wait as phy_wait}; -use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; fn main() { utils::setup_logging(""); @@ -24,7 +24,7 @@ fn main() { let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let port = u16::from_str(&matches.free[1]).expect("invalid port format"); @@ -40,11 +40,11 @@ fn main() { let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); - + let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + .ip_addrs(ip_addrs) + .routes(routes); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -64,7 +64,7 @@ fn main() { loop { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } @@ -76,25 +76,31 @@ fn main() { debug!("connected"); } else if !socket.is_active() && tcp_active { debug!("disconnected"); - break + break; } tcp_active = socket.is_active(); if socket.may_recv() { - let data = socket.recv(|data| { - let mut data = data.to_owned(); - if !data.is_empty() { - debug!("recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); - data = data.split(|&b| b == b'\n').collect::>().concat(); - data.reverse(); - data.extend(b"\n"); - } - (data.len(), data) - }).unwrap(); + let data = socket + .recv(|data| { + let mut data = data.to_owned(); + if !data.is_empty() { + debug!( + "recv data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + data = data.split(|&b| b == b'\n').collect::>().concat(); + data.reverse(); + data.extend(b"\n"); + } + (data.len(), data) + }) + .unwrap(); if socket.can_send() && !data.is_empty() { - debug!("send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); + debug!( + "send data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); socket.send_slice(&data[..]).unwrap(); } } else if socket.may_send() { diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index d274caebd..57d9211a9 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -1,15 +1,18 @@ #![allow(clippy::option_map_unit_fn)] mod utils; +use log::*; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use log::*; -use smoltcp::{phy::{Device, Medium, wait as phy_wait}, time::Duration}; -use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes}; -use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event}; +use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketSet}; use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +use smoltcp::{ + phy::{wait as phy_wait, Device, Medium}, + time::Duration, +}; fn main() { #[cfg(feature = "log")] @@ -22,7 +25,7 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); @@ -32,8 +35,8 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + .ip_addrs(ip_addrs) + .routes(routes); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -92,11 +95,11 @@ fn main() { } fn set_ipv4_addr(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr) - where DeviceT: for<'d> Device<'d> +where + DeviceT: for<'d> Device<'d>, { iface.update_ip_addrs(|addrs| { let dest = addrs.iter_mut().next().unwrap(); *dest = IpCidr::Ipv4(cidr); }); } - diff --git a/examples/httpclient.rs b/examples/httpclient.rs index f41bb3204..dafde6b5a 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -1,16 +1,16 @@ mod utils; -use std::str::{self, FromStr}; +use log::debug; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; +use std::str::{self, FromStr}; use url::Url; -use log::debug; -use smoltcp::phy::{Device, Medium, wait as phy_wait}; -use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; fn main() { utils::setup_logging(""); @@ -24,11 +24,10 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let url = Url::parse(&matches.free[1]).expect("invalid url format"); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]); @@ -36,9 +35,11 @@ fn main() { let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)]; + let ip_addrs = [ + IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), + IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), + ]; let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; @@ -48,8 +49,8 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + .ip_addrs(ip_addrs) + .routes(routes); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -60,15 +61,19 @@ fn main() { let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); - enum State { Connect, Request, Response } + enum State { + Connect, + Request, + Response, + } let mut state = State::Connect; loop { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { - debug!("poll error: {}",e); + debug!("poll error: {}", e); } } @@ -79,7 +84,9 @@ fn main() { State::Connect if !socket.is_active() => { debug!("connecting"); let local_port = 49152 + rand::random::() % 16384; - socket.connect((address, url.port().unwrap_or(80)), local_port).unwrap(); + socket + .connect((address, url.port().unwrap_or(80)), local_port) + .unwrap(); State::Request } State::Request if socket.may_send() => { @@ -88,22 +95,26 @@ fn main() { socket.send_slice(http_get.as_ref()).expect("cannot send"); let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n"; socket.send_slice(http_host.as_ref()).expect("cannot send"); - socket.send_slice(b"Connection: close\r\n").expect("cannot send"); + socket + .send_slice(b"Connection: close\r\n") + .expect("cannot send"); socket.send_slice(b"\r\n").expect("cannot send"); State::Response } State::Response if socket.can_recv() => { - socket.recv(|data| { - println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)")); - (data.len(), ()) - }).unwrap(); + socket + .recv(|data| { + println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)")); + (data.len(), ()) + }) + .unwrap(); State::Response } State::Response if !socket.may_recv() => { debug!("received complete response"); - break + break; } - _ => state + _ => state, } } diff --git a/examples/loopback.rs b/examples/loopback.rs index 7eb6bc3d7..00e465146 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -7,18 +7,18 @@ mod utils; use core::str; -use log::{info, debug, error}; +use log::{debug, error, info}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{Loopback, Medium}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; #[cfg(not(feature = "std"))] mod mock { - use smoltcp::time::{Duration, Instant}; use core::cell::Cell; + use smoltcp::time::{Duration, Instant}; #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -41,9 +41,9 @@ mod mock { #[cfg(feature = "std")] mod mock { + use smoltcp::time::{Duration, Instant}; + use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; - use std::sync::atomic::{Ordering, AtomicUsize}; - use smoltcp::time::{Duration, Instant}; // should be AtomicU64 but that's unstable #[derive(Debug, Clone)] @@ -56,7 +56,8 @@ mod mock { } pub fn advance(&self, duration: Duration) { - self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst); + self.0 + .fetch_add(duration.total_millis() as usize, Ordering::SeqCst); } pub fn elapsed(&self) -> Instant { @@ -78,7 +79,7 @@ fn main() { utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - utils::parse_middleware_options(&mut matches, device, /*loopback=*/true) + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true) }; let mut neighbor_cache_entries = [None; 8]; @@ -86,10 +87,10 @@ fn main() { let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(EthernetAddress::default()) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .ethernet_addr(EthernetAddress::default()) + .neighbor_cache(neighbor_cache) + .ip_addrs(ip_addrs) + .finalize(); let server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but @@ -116,12 +117,12 @@ fn main() { let server_handle = socket_set.add(server_socket); let client_handle = socket_set.add(client_socket); - let mut did_listen = false; + let mut did_listen = false; let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(10_000) { match iface.poll(&mut socket_set, clock.elapsed()) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } @@ -138,9 +139,10 @@ fn main() { } if socket.can_recv() { - debug!("got {:?}", socket.recv(|buffer| { - (buffer.len(), str::from_utf8(buffer).unwrap()) - })); + debug!( + "got {:?}", + socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) }) + ); socket.close(); done = true; } @@ -151,8 +153,12 @@ fn main() { if !socket.is_open() { if !did_connect { debug!("connecting"); - socket.connect((IpAddress::v4(127, 0, 0, 1), 1234), - (IpAddress::Unspecified, 65000)).unwrap(); + socket + .connect( + (IpAddress::v4(127, 0, 0, 1), 1234), + (IpAddress::Unspecified, 65000), + ) + .unwrap(); did_connect = true; } } @@ -169,8 +175,8 @@ fn main() { Some(delay) => { debug!("sleeping for {} ms", delay); clock.advance(delay) - }, - None => clock.advance(Duration::from_millis(1)) + } + None => clock.advance(Duration::from_millis(1)), } } diff --git a/examples/multicast.rs b/examples/multicast.rs index 33d24262c..30a368eee 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -1,17 +1,20 @@ mod utils; +use log::debug; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use log::debug; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::wait as phy_wait; -use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address, - Ipv4Packet, IgmpPacket, IgmpRepr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder}; -use smoltcp::socket::{SocketSet, - RawSocket, RawSocketBuffer, RawPacketMetadata, - UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; +use smoltcp::socket::{ + RawPacketMetadata, RawSocket, RawSocketBuffer, SocketSet, UdpPacketMetadata, UdpSocket, + UdpSocketBuffer, +}; use smoltcp::time::Instant; +use smoltcp::wire::{ + EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address, + Ipv4Packet, +}; const MDNS_PORT: u16 = 5353; const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251]; @@ -26,10 +29,7 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, - device, - /*loopback=*/ - false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); let local_addr = Ipv4Address::new(192, 168, 69, 2); @@ -38,15 +38,17 @@ fn main() { let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) - .ip_addrs([ip_addr]) - .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) - .finalize(); + .ethernet_addr(ethernet_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs([ip_addr]) + .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) + .finalize(); let now = Instant::now(); // Join a multicast group to receive mDNS traffic - iface.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now).unwrap(); + iface + .join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now) + .unwrap(); let mut sockets = SocketSet::new(vec![]); @@ -55,8 +57,10 @@ fn main() { // Will not send IGMP let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]); let raw_socket = RawSocket::new( - IpVersion::Ipv4, IpProtocol::Igmp, - raw_rx_buffer, raw_tx_buffer + IpVersion::Ipv4, + IpProtocol::Igmp, + raw_rx_buffer, + raw_tx_buffer, ); let raw_handle = sockets.add(raw_socket); @@ -70,9 +74,9 @@ fn main() { loop { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { - debug!("poll error: {}",e); + debug!("poll error: {}", e); } } @@ -82,7 +86,8 @@ fn main() { if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets // in the application layer - socket.recv() + socket + .recv() .and_then(Ipv4Packet::new_checked) .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) @@ -97,8 +102,11 @@ fn main() { } if socket.can_recv() { - socket.recv() - .map(|(data, sender)| println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)) + socket + .recv() + .map(|(data, sender)| { + println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender) + }) .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); } } diff --git a/examples/ping.rs b/examples/ping.rs index 816470c1e..45e327bc0 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -1,21 +1,25 @@ mod utils; -use std::str::FromStr; -use std::collections::BTreeMap; +use byteorder::{ByteOrder, NetworkEndian}; +use log::debug; use std::cmp; -use std::os::unix::io::AsRawFd; +use std::collections::BTreeMap; use std::collections::HashMap; -use log::debug; -use byteorder::{ByteOrder, NetworkEndian}; +use std::os::unix::io::AsRawFd; +use std::str::FromStr; -use smoltcp::{phy::Medium, time::{Duration, Instant}}; -use smoltcp::phy::Device; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::wait as phy_wait; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, - Ipv6Address, Icmpv6Repr, Icmpv6Packet, - Ipv4Address, Icmpv4Repr, Icmpv4Packet}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; -use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; +use smoltcp::phy::Device; +use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketSet}; +use smoltcp::wire::{ + EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr, + Ipv4Address, Ipv6Address, +}; +use smoltcp::{ + phy::Medium, + time::{Duration, Instant}, +}; macro_rules! send_icmp_ping { ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, @@ -26,13 +30,11 @@ macro_rules! send_icmp_ping { data: &$echo_payload, }; - let icmp_payload = $socket - .send(icmp_repr.buffer_len(), $remote_addr) - .unwrap(); + let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap(); let icmp_packet = $packet_type::new_unchecked(icmp_payload); (icmp_repr, icmp_packet) - }} + }}; } macro_rules! get_icmp_pong { @@ -41,14 +43,18 @@ macro_rules! get_icmp_pong { if let $repr_type::EchoReply { seq_no, data, .. } = $repr { if let Some(_) = $waiting_queue.get(&seq_no) { let packet_timestamp_ms = NetworkEndian::read_i64(data); - println!("{} bytes from {}: icmp_seq={}, time={}ms", - data.len(), $remote_addr, seq_no, - $timestamp.total_millis() - packet_timestamp_ms); + println!( + "{} bytes from {}: icmp_seq={}, time={}ms", + data.len(), + $remote_addr, + seq_no, + $timestamp.total_millis() - packet_timestamp_ms + ); $waiting_queue.remove(&seq_no); $received += 1; } } - }} + }}; } fn main() { @@ -57,26 +63,45 @@ fn main() { let (mut opts, mut free) = utils::create_options(); utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); - opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT"); - opts.optopt("i", "interval", - "Interval between successive packets sent (seconds) (default: 1)", "INTERVAL"); - opts.optopt("", "timeout", - "Maximum wait duration for an echo response packet (seconds) (default: 5)", - "TIMEOUT"); + opts.optopt( + "c", + "count", + "Amount of echo request packets to send (default: 4)", + "COUNT", + ); + opts.optopt( + "i", + "interval", + "Interval between successive packets sent (seconds) (default: 1)", + "INTERVAL", + ); + opts.optopt( + "", + "timeout", + "Maximum wait duration for an echo response packet (seconds) (default: 5)", + "TIMEOUT", + ); free.push("ADDRESS"); let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let device_caps = device.capabilities(); - let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); - let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4); - let interval = matches.opt_str("interval") + let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); + let count = matches + .opt_str("count") + .map(|s| usize::from_str(&s).unwrap()) + .unwrap_or(4); + let interval = matches + .opt_str("interval") .map(|s| Duration::from_secs(u64::from_str(&s).unwrap())) .unwrap_or_else(|| Duration::from_secs(1)); - let timeout = Duration::from_secs( - matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5) + let timeout = Duration::from_secs( + matches + .opt_str("timeout") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(5), ); let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -89,9 +114,11 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(src_ipv6, 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)]; + let ip_addrs = [ + IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), + IpCidr::new(src_ipv6, 64), + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), + ]; let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; @@ -101,8 +128,8 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + .ip_addrs(ip_addrs) + .routes(routes); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -123,7 +150,7 @@ fn main() { loop { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } @@ -137,25 +164,40 @@ fn main() { send_at = timestamp; } - if socket.can_send() && seq_no < count as u16 && - send_at <= timestamp { + if socket.can_send() && seq_no < count as u16 && send_at <= timestamp { NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis()); match remote_addr { IpAddress::Ipv4(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - Icmpv4Repr, Icmpv4Packet, ident, seq_no, - echo_payload, socket, remote_addr); + Icmpv4Repr, + Icmpv4Packet, + ident, + seq_no, + echo_payload, + socket, + remote_addr + ); icmp_repr.emit(&mut icmp_packet, &device_caps.checksum); - }, + } IpAddress::Ipv6(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - Icmpv6Repr, Icmpv6Packet, ident, seq_no, - echo_payload, socket, remote_addr); - icmp_repr.emit(&src_ipv6, &remote_addr, - &mut icmp_packet, &device_caps.checksum); - }, - _ => unimplemented!() + Icmpv6Repr, + Icmpv6Packet, + ident, + seq_no, + echo_payload, + socket, + remote_addr + ); + icmp_repr.emit( + &src_ipv6, + &remote_addr, + &mut icmp_packet, + &device_caps.checksum, + ); + } + _ => unimplemented!(), } waiting_queue.insert(seq_no, timestamp); @@ -171,17 +213,36 @@ fn main() { let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap(); let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap(); - get_icmp_pong!(Icmpv4Repr, icmp_repr, payload, - waiting_queue, remote_addr, timestamp, received); + get_icmp_pong!( + Icmpv4Repr, + icmp_repr, + payload, + waiting_queue, + remote_addr, + timestamp, + received + ); } IpAddress::Ipv6(_) => { let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); - let icmp_repr = Icmpv6Repr::parse(&remote_addr, &src_ipv6, - &icmp_packet, &device_caps.checksum).unwrap(); - get_icmp_pong!(Icmpv6Repr, icmp_repr, payload, - waiting_queue, remote_addr, timestamp, received); - }, - _ => unimplemented!() + let icmp_repr = Icmpv6Repr::parse( + &remote_addr, + &src_ipv6, + &icmp_packet, + &device_caps.checksum, + ) + .unwrap(); + get_icmp_pong!( + Icmpv6Repr, + icmp_repr, + payload, + waiting_queue, + remote_addr, + timestamp, + received + ); + } + _ => unimplemented!(), } } @@ -195,7 +256,7 @@ fn main() { }); if seq_no == count as u16 && waiting_queue.is_empty() { - break + break; } } @@ -204,7 +265,7 @@ fn main() { Some(poll_at) if timestamp < poll_at => { let resume_at = cmp::min(poll_at, send_at); phy_wait(fd, Some(resume_at - timestamp)).expect("wait error"); - }, + } Some(_) => (), None => { phy_wait(fd, Some(send_at - timestamp)).expect("wait error"); @@ -213,6 +274,10 @@ fn main() { } println!("--- {} ping statistics ---", remote_addr); - println!("{} packets transmitted, {} received, {:.0}% packet loss", - seq_no, received, 100.0 * (seq_no - received) as f64 / seq_no as f64); + println!( + "{} packets transmitted, {} received, {:.0}% packet loss", + seq_no, + received, + 100.0 * (seq_no - received) as f64 / seq_no as f64 + ); } diff --git a/examples/server.rs b/examples/server.rs index 944cde403..1db276a36 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,18 +1,18 @@ mod utils; -use std::str; +use log::debug; use std::collections::BTreeMap; use std::fmt::Write; use std::os::unix::io::AsRawFd; -use log::debug; +use std::str; -use smoltcp::phy::{Device, Medium, wait as phy_wait}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::SocketSet; -use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use smoltcp::time::{Duration, Instant}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; fn main() { utils::setup_logging(""); @@ -24,7 +24,7 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -52,12 +52,11 @@ fn main() { let ip_addrs = [ IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64) + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), ]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .ethernet_addr(ethernet_addr) @@ -66,7 +65,7 @@ fn main() { let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); - let udp_handle = sockets.add(udp_socket); + let udp_handle = sockets.add(udp_socket); let tcp1_handle = sockets.add(tcp1_socket); let tcp2_handle = sockets.add(tcp2_socket); let tcp3_handle = sockets.add(tcp3_socket); @@ -76,7 +75,7 @@ fn main() { loop { let timestamp = Instant::now(); match iface.poll(&mut sockets, timestamp) { - Ok(_) => {}, + Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } @@ -91,16 +90,21 @@ fn main() { let client = match socket.recv() { Ok((data, endpoint)) => { - debug!("udp:6969 recv data: {:?} from {}", - str::from_utf8(data).unwrap(), endpoint); + debug!( + "udp:6969 recv data: {:?} from {}", + str::from_utf8(data).unwrap(), + endpoint + ); Some(endpoint) } - Err(_) => None + Err(_) => None, }; if let Some(endpoint) = client { let data = b"hello\n"; - debug!("udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap()); + debug!( + "udp:6969 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap() + ); socket.send_slice(data, endpoint).unwrap(); } } @@ -135,21 +139,27 @@ fn main() { tcp_6970_active = socket.is_active(); if socket.may_recv() { - let data = socket.recv(|buffer| { - let recvd_len = buffer.len(); - let mut data = buffer.to_owned(); - if !data.is_empty() { - debug!("tcp:6970 recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); - data = data.split(|&b| b == b'\n').collect::>().concat(); - data.reverse(); - data.extend(b"\n"); - } - (recvd_len, data) - }).unwrap(); + let data = socket + .recv(|buffer| { + let recvd_len = buffer.len(); + let mut data = buffer.to_owned(); + if !data.is_empty() { + debug!( + "tcp:6970 recv data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + data = data.split(|&b| b == b'\n').collect::>().concat(); + data.reverse(); + data.extend(b"\n"); + } + (recvd_len, data) + }) + .unwrap(); if socket.can_send() && !data.is_empty() { - debug!("tcp:6970 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); + debug!( + "tcp:6970 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); socket.send_slice(&data[..]).unwrap(); } } else if socket.may_send() { @@ -168,12 +178,14 @@ fn main() { } if socket.may_recv() { - socket.recv(|buffer| { - if !buffer.is_empty() { - debug!("tcp:6971 recv {:?} octets", buffer.len()); - } - (buffer.len(), ()) - }).unwrap(); + socket + .recv(|buffer| { + if !buffer.is_empty() { + debug!("tcp:6971 recv {:?} octets", buffer.len()); + } + (buffer.len(), ()) + }) + .unwrap(); } else if socket.may_send() { socket.close(); } @@ -187,15 +199,17 @@ fn main() { } if socket.may_send() { - socket.send(|data| { - if !data.is_empty() { - debug!("tcp:6972 send {:?} octets", data.len()); - for (i, b) in data.iter_mut().enumerate() { - *b = (i % 256) as u8; + socket + .send(|data| { + if !data.is_empty() { + debug!("tcp:6972 send {:?} octets", data.len()); + for (i, b) in data.iter_mut().enumerate() { + *b = (i % 256) as u8; + } } - } - (data.len(), ()) - }).unwrap(); + (data.len(), ()) + }) + .unwrap(); } } diff --git a/examples/tcpdump.rs b/examples/tcpdump.rs index 5ca8ec6de..6bd0e8a0e 100644 --- a/examples/tcpdump.rs +++ b/examples/tcpdump.rs @@ -1,9 +1,9 @@ -use std::env; -use std::os::unix::io::AsRawFd; use smoltcp::phy::wait as phy_wait; -use smoltcp::phy::{Device, RxToken, RawSocket}; -use smoltcp::wire::{PrettyPrinter, EthernetFrame}; +use smoltcp::phy::{Device, RawSocket, RxToken}; use smoltcp::time::Instant; +use smoltcp::wire::{EthernetFrame, PrettyPrinter}; +use std::env; +use std::os::unix::io::AsRawFd; fn main() { let ifname = env::args().nth(1).unwrap(); @@ -11,9 +11,14 @@ fn main() { loop { phy_wait(socket.as_raw_fd(), None).unwrap(); let (rx_token, _) = socket.receive().unwrap(); - rx_token.consume(Instant::now(), |buffer| { - println!("{}", PrettyPrinter::>::new("", &buffer)); - Ok(()) - }).unwrap(); + rx_token + .consume(Instant::now(), |buffer| { + println!( + "{}", + PrettyPrinter::>::new("", &buffer) + ); + Ok(()) + }) + .unwrap(); } } diff --git a/examples/utils.rs b/examples/utils.rs index b4687a4ee..9e1cfca64 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -1,43 +1,59 @@ #![allow(dead_code)] +#[cfg(feature = "log")] +use env_logger::Builder; +use getopts::{Matches, Options}; +#[cfg(feature = "log")] +use log::{trace, Level, LevelFilter}; use std::cell::RefCell; -use std::str::{self, FromStr}; -use std::rc::Rc; -use std::io::{self, Write}; -use std::fs::File; -use std::time::{SystemTime, UNIX_EPOCH}; use std::env; +use std::fs::File; +use std::io::{self, Write}; use std::process; -#[cfg(feature = "log")] -use log::{Level, LevelFilter, trace}; -#[cfg(feature = "log")] -use env_logger::Builder; -use getopts::{Options, Matches}; +use std::rc::Rc; +use std::str::{self, FromStr}; +use std::time::{SystemTime, UNIX_EPOCH}; -use smoltcp::phy::{Device, Tracer, FaultInjector, Medium}; +use smoltcp::phy::RawSocket; #[cfg(feature = "phy-tuntap_interface")] use smoltcp::phy::TunTapInterface; -use smoltcp::phy::{PcapWriter, PcapSink, PcapMode}; -use smoltcp::phy::RawSocket; +use smoltcp::phy::{Device, FaultInjector, Medium, Tracer}; +use smoltcp::phy::{PcapMode, PcapSink, PcapWriter}; use smoltcp::time::{Duration, Instant}; #[cfg(feature = "log")] pub fn setup_logging_with_clock(filter: &str, since_startup: F) - where F: Fn() -> Instant + Send + Sync + 'static { +where + F: Fn() -> Instant + Send + Sync + 'static, +{ Builder::new() .format(move |buf, record| { let elapsed = since_startup(); let timestamp = format!("[{}]", elapsed); if record.target().starts_with("smoltcp::") { - writeln!(buf, "\x1b[0m{} ({}): {}\x1b[0m", timestamp, - record.target().replace("smoltcp::", ""), record.args()) + writeln!( + buf, + "\x1b[0m{} ({}): {}\x1b[0m", + timestamp, + record.target().replace("smoltcp::", ""), + record.args() + ) } else if record.level() == Level::Trace { let message = format!("{}", record.args()); - writeln!(buf, "\x1b[37m{} {}\x1b[0m", timestamp, - message.replace("\n", "\n ")) + writeln!( + buf, + "\x1b[37m{} {}\x1b[0m", + timestamp, + message.replace("\n", "\n ") + ) } else { - writeln!(buf, "\x1b[32m{} ({}): {}\x1b[0m", timestamp, - record.target(), record.args()) + writeln!( + buf, + "\x1b[32m{} ({}): {}\x1b[0m", + timestamp, + record.target(), + record.args() + ) } }) .filter(None, LevelFilter::Trace) @@ -48,9 +64,7 @@ pub fn setup_logging_with_clock(filter: &str, since_startup: F) #[cfg(feature = "log")] pub fn setup_logging(filter: &str) { - setup_logging_with_clock(filter, move || { - Instant::now() - }) + setup_logging_with_clock(filter, move || Instant::now()) } pub fn create_options() -> (Options, Vec<&'static str>) { @@ -67,10 +81,17 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { } Ok(matches) => { if matches.opt_present("h") || matches.free.len() != free.len() { - let brief = format!("Usage: {} [OPTION]... {}", - env::args().next().unwrap(), free.join(" ")); + let brief = format!( + "Usage: {} [OPTION]... {}", + env::args().next().unwrap(), + free.join(" ") + ); print!("{}", options.usage(&brief)); - process::exit(if matches.free.len() != free.len() { 1 } else { 0 }) + process::exit(if matches.free.len() != free.len() { + 1 + } else { + 0 + }) } matches } @@ -86,7 +107,7 @@ pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) { pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { let tun = matches.opt_str("tun"); let tap = matches.opt_str("tap"); - match(tun,tap) { + match (tun, tap) { (Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(), (None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(), _ => panic!("You must specify exactly one of --tun or --tap"), @@ -100,32 +121,78 @@ pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket { pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { opts.optopt("", "pcap", "Write a packet capture file", "FILE"); - opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE"); - opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE"); - opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE"); - opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \ - (packets per interval)", "RATE"); - opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \ - (packets per interval)", "RATE"); - opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE"); + opts.optopt( + "", + "drop-chance", + "Chance of dropping a packet (%)", + "CHANCE", + ); + opts.optopt( + "", + "corrupt-chance", + "Chance of corrupting a packet (%)", + "CHANCE", + ); + opts.optopt( + "", + "size-limit", + "Drop packets larger than given size (octets)", + "SIZE", + ); + opts.optopt( + "", + "tx-rate-limit", + "Drop packets after transmit rate exceeds given limit \ + (packets per interval)", + "RATE", + ); + opts.optopt( + "", + "rx-rate-limit", + "Drop packets after transmit rate exceeds given limit \ + (packets per interval)", + "RATE", + ); + opts.optopt( + "", + "shaping-interval", + "Sets the interval for rate limiting (ms)", + "RATE", + ); } -pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: bool) - -> FaultInjector>>> - where D: for<'a> Device<'a> +pub fn parse_middleware_options( + matches: &mut Matches, + device: D, + loopback: bool, +) -> FaultInjector>>> +where + D: for<'a> Device<'a>, { - let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap()) - .unwrap_or(0); - let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap()) - .unwrap_or(0); - let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap()) - .unwrap_or(0); - let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); - let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); - let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); + let drop_chance = matches + .opt_str("drop-chance") + .map(|s| u8::from_str(&s).unwrap()) + .unwrap_or(0); + let corrupt_chance = matches + .opt_str("corrupt-chance") + .map(|s| u8::from_str(&s).unwrap()) + .unwrap_or(0); + let size_limit = matches + .opt_str("size-limit") + .map(|s| usize::from_str(&s).unwrap()) + .unwrap_or(0); + let tx_rate_limit = matches + .opt_str("tx-rate-limit") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); + let rx_rate_limit = matches + .opt_str("rx-rate-limit") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); + let shaping_interval = matches + .opt_str("shaping-interval") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); let pcap_writer: Box; if let Some(pcap_filename) = matches.opt_str("pcap") { @@ -134,12 +201,19 @@ pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: b pcap_writer = Box::new(io::sink()) } - let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos(); + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .subsec_nanos(); let device = PcapWriter::new( device, Rc::new(RefCell::new(pcap_writer)) as Rc, - if loopback { PcapMode::TxOnly } else { PcapMode::Both }, + if loopback { + PcapMode::TxOnly + } else { + PcapMode::Both + }, ); let device = Tracer::new(device, |_timestamp, _printer| { diff --git a/src/iface/interface.rs b/src/iface/interface.rs index f4be49502..0fd63ac69 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -3,16 +3,16 @@ // and RFCs 8200 and 4861 for any IPv6 and NDISC work. use core::cmp; -use managed::{ManagedSlice, ManagedMap}; +use managed::{ManagedMap, ManagedSlice}; -use crate::{Error, Result}; -use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken, Medium}; +use crate::iface::Routes; +#[cfg(feature = "medium-ethernet")] +use crate::iface::{NeighborAnswer, NeighborCache}; +use crate::phy::{Device, DeviceCapabilities, Medium, RxToken, TxToken}; +use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; -use crate::socket::*; -#[cfg(feature = "medium-ethernet")] -use crate::iface::{NeighborCache, NeighborAnswer}; -use crate::iface::Routes; +use crate::{Error, Result}; /// A network interface. /// @@ -21,7 +21,7 @@ use crate::iface::Routes; /// a `&mut [T]`, or `Vec` if a heap is available. pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, - inner: InterfaceInner<'a>, + inner: InterfaceInner<'a>, } /// The device independent part of an Ethernet network interface. @@ -33,41 +33,45 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// exclusively). However, it is still possible to call methods on its `inner` field. struct InterfaceInner<'a> { #[cfg(feature = "medium-ethernet")] - neighbor_cache: Option>, + neighbor_cache: Option>, #[cfg(feature = "medium-ethernet")] - ethernet_addr: Option, - ip_addrs: ManagedSlice<'a, IpCidr>, + ethernet_addr: Option, + ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] - any_ip: bool, - routes: Routes<'a>, + any_ip: bool, + routes: Routes<'a>, #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, /// When to report for (all or) the next multicast group membership via IGMP #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState, + igmp_report_state: IgmpReportState, } /// A builder structure used for creating a network interface. -pub struct InterfaceBuilder <'a, DeviceT: for<'d> Device<'d>> { - device: DeviceT, +pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { + device: DeviceT, #[cfg(feature = "medium-ethernet")] - ethernet_addr: Option, + ethernet_addr: Option, #[cfg(feature = "medium-ethernet")] - neighbor_cache: Option>, - ip_addrs: ManagedSlice<'a, IpCidr>, + neighbor_cache: Option>, + ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] - any_ip: bool, - routes: Routes<'a>, + any_ip: bool, + routes: Routes<'a>, /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, } impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> - where DeviceT: for<'d> Device<'d> { +where + DeviceT: for<'d> Device<'d>, +{ /// Create a builder used for creating a network interface using the /// given device and address. - #[cfg_attr(feature = "medium-ethernet", doc = r##" + #[cfg_attr( + feature = "medium-ethernet", + doc = r##" # Examples ``` @@ -90,20 +94,21 @@ let iface = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .finalize(); ``` - "##)] + "## + )] pub fn new(device: DeviceT) -> Self { InterfaceBuilder { - device: device, + device: device, #[cfg(feature = "medium-ethernet")] - ethernet_addr: None, + ethernet_addr: None, #[cfg(feature = "medium-ethernet")] - neighbor_cache: None, - ip_addrs: ManagedSlice::Borrowed(&mut []), + neighbor_cache: None, + ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] - any_ip: false, - routes: Routes::new(ManagedMap::Borrowed(&mut [])), + any_ip: false, + routes: Routes::new(ManagedMap::Borrowed(&mut [])), #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), } } @@ -129,7 +134,8 @@ let iface = InterfaceBuilder::new(device) /// /// [ip_addrs]: struct.Interface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self - where T: Into> + where + T: Into>, { let ip_addrs = ip_addrs.into(); InterfaceInner::check_ip_addrs(&ip_addrs); @@ -161,7 +167,8 @@ let iface = InterfaceBuilder::new(device) /// /// [routes]: struct.Interface.html#method.routes pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a, DeviceT> - where T: Into> + where + T: Into>, { self.routes = routes.into(); self @@ -179,7 +186,8 @@ let iface = InterfaceBuilder::new(device) /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group #[cfg(feature = "proto-igmp")] pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self - where T: Into> + where + T: Into>, { self.ipv4_multicast_groups = ipv4_multicast_groups.into(); self @@ -209,13 +217,25 @@ let iface = InterfaceBuilder::new(device) #[cfg(feature = "medium-ethernet")] let (ethernet_addr, neighbor_cache) = match device_capabilities.medium { Medium::Ethernet => ( - Some(self.ethernet_addr.expect("ethernet_addr required option was not set")), - Some(self.neighbor_cache.expect("neighbor_cache required option was not set")) + Some( + self.ethernet_addr + .expect("ethernet_addr required option was not set"), + ), + Some( + self.neighbor_cache + .expect("neighbor_cache required option was not set"), + ), ), #[cfg(feature = "medium-ip")] Medium::Ip => { - assert!(self.ethernet_addr.is_none(), "ethernet_addr is set, but device medium is IP"); - assert!(self.neighbor_cache.is_none(), "neighbor_cache is set, but device medium is IP"); + assert!( + self.ethernet_addr.is_none(), + "ethernet_addr is set, but device medium is IP" + ); + assert!( + self.neighbor_cache.is_none(), + "neighbor_cache is set, but device medium is IP" + ); (None, None) } }; @@ -235,7 +255,7 @@ let iface = InterfaceBuilder::new(device) ipv4_multicast_groups: self.ipv4_multicast_groups, #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, - } + }, } } } @@ -288,27 +308,39 @@ impl<'a> IpPacket<'a> { } } - pub(crate) fn emit_payload(&self, _ip_repr: IpRepr, payload: &mut [u8], caps: &DeviceCapabilities) { + pub(crate) fn emit_payload( + &self, + _ip_repr: IpRepr, + payload: &mut [u8], + caps: &DeviceCapabilities, + ) { match self { #[cfg(feature = "proto-ipv4")] - IpPacket::Icmpv4((_, icmpv4_repr)) => - icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum), + IpPacket::Icmpv4((_, icmpv4_repr)) => { + icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum) + } #[cfg(feature = "proto-igmp")] - IpPacket::Igmp((_, igmp_repr)) => - igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)), + IpPacket::Igmp((_, igmp_repr)) => { + igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)) + } #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmpv6_repr)) => - icmpv6_repr.emit(&_ip_repr.src_addr(), &_ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(payload), &caps.checksum), + IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit( + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + &mut Icmpv6Packet::new_unchecked(payload), + &caps.checksum, + ), #[cfg(feature = "socket-raw")] - IpPacket::Raw((_, raw_packet)) => - payload.copy_from_slice(raw_packet), + IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udp_repr, inner_payload)) => - udp_repr.emit(&mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), &_ip_repr.dst_addr(), - inner_payload.len(), |buf| buf.copy_from_slice(inner_payload), - &caps.checksum), + IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit( + &mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + inner_payload.len(), + |buf| buf.copy_from_slice(inner_payload), + &caps.checksum, + ), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((_, mut tcp_repr)) => { // This is a terrible hack to make TCP performance more acceptable on systems @@ -329,17 +361,22 @@ impl<'a> IpPacket<'a> { } } - tcp_repr.emit(&mut TcpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), &_ip_repr.dst_addr(), - &caps.checksum); + tcp_repr.emit( + &mut TcpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + &caps.checksum, + ); } #[cfg(feature = "socket-dhcpv4")] - IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => - udp_repr.emit(&mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), &_ip_repr.dst_addr(), - dhcp_repr.buffer_len(), - |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), - &caps.checksum), + IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit( + &mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + dhcp_repr.buffer_len(), + |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), + &caps.checksum, + ), } } } @@ -361,20 +398,22 @@ fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize { enum IgmpReportState { Inactive, ToGeneralQuery { - version: IgmpVersion, - timeout: Instant, - interval: Duration, - next_index: usize + version: IgmpVersion, + timeout: Instant, + interval: Duration, + next_index: usize, }, ToSpecificQuery { - version: IgmpVersion, - timeout: Instant, - group: Ipv4Address + version: IgmpVersion, + timeout: Instant, + group: Ipv4Address, }, } impl<'a, DeviceT> Interface<'a, DeviceT> - where DeviceT: for<'d> Device<'d> { +where + DeviceT: for<'d> Device<'d>, +{ /// Get the Ethernet address of the interface. /// /// # Panics @@ -417,18 +456,24 @@ 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>(&mut self, addr: T, _timestamp: Instant) -> Result { - + pub fn join_multicast_group>( + &mut self, + addr: T, + _timestamp: Instant, + ) -> Result { match addr.into() { #[cfg(feature = "proto-igmp")] IpAddress::Ipv4(addr) => { - let is_not_new = self.inner.ipv4_multicast_groups.insert(addr, ()) + let is_not_new = self + .inner + .ipv4_multicast_groups + .insert(addr, ()) .map_err(|_| Error::Exhausted)? .is_some(); if is_not_new { Ok(false) - } else if let Some(pkt) = - self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { + } 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)?; @@ -439,7 +484,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } } // Multicast is not yet implemented for other address families - _ => Err(Error::Unaddressable) + _ => Err(Error::Unaddressable), } } @@ -447,12 +492,15 @@ impl<'a, DeviceT> Interface<'a, DeviceT> /// /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` /// indicates whether an immediate leave packet has been sent. - pub fn leave_multicast_group>(&mut self, addr: T, _timestamp: Instant) -> Result { + pub fn leave_multicast_group>( + &mut self, + addr: T, + _timestamp: Instant, + ) -> Result { match addr.into() { #[cfg(feature = "proto-igmp")] IpAddress::Ipv4(addr) => { - let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr) - .is_none(); + let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none(); if was_not_present { Ok(false) } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { @@ -466,7 +514,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } } // Multicast is not yet implemented for other address families - _ => Err(Error::Unaddressable) + _ => Err(Error::Unaddressable), } } @@ -483,11 +531,13 @@ impl<'a, DeviceT> Interface<'a, DeviceT> /// Get the first IPv4 address if present. #[cfg(feature = "proto-ipv4")] pub fn ipv4_addr(&self) -> Option { - self.ip_addrs().iter() + self.ip_addrs() + .iter() .filter_map(|cidr| match cidr.address() { IpAddress::Ipv4(addr) => Some(addr), _ => None, - }).next() + }) + .next() } /// Update the IP addresses of the interface. @@ -541,7 +591,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let mut readiness_may_have_changed = false; loop { let processed_any = self.socket_ingress(&cx, sockets); - let emitted_any = self.socket_egress(&cx, sockets)?; + let emitted_any = self.socket_egress(&cx, sockets)?; #[cfg(feature = "proto-igmp")] self.igmp_egress(&cx, timestamp)?; @@ -549,7 +599,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> if processed_any || emitted_any { readiness_may_have_changed = true; } else { - break + break; } } Ok(readiness_may_have_changed) @@ -566,15 +616,19 @@ impl<'a, DeviceT> Interface<'a, DeviceT> pub fn poll_at(&self, sockets: &SocketSet, timestamp: Instant) -> Option { let cx = self.context(timestamp); - sockets.iter().filter_map(|socket| { - let socket_poll_at = socket.poll_at(&cx); - match socket.meta().poll_at(socket_poll_at, |ip_addr| - self.inner.has_neighbor(&cx, &ip_addr)) { + sockets + .iter() + .filter_map(|socket| { + let socket_poll_at = socket.poll_at(&cx); + match socket.meta().poll_at(socket_poll_at, |ip_addr| { + self.inner.has_neighbor(&cx, &ip_addr) + }) { PollAt::Ingress => None, PollAt::Time(instant) => Some(instant), PollAt::Now => Some(Instant::from_millis(0)), - } - }).min() + } + }) + .min() } /// Return an _advisory wait time_ for calling [poll] the next time. @@ -587,55 +641,52 @@ impl<'a, DeviceT> Interface<'a, DeviceT> /// [Duration]: struct.Duration.html pub fn poll_delay(&self, sockets: &SocketSet, timestamp: Instant) -> Option { match self.poll_at(sockets, timestamp) { - Some(poll_at) if timestamp < poll_at => { - Some(poll_at - timestamp) - } - Some(_) => { - Some(Duration::from_millis(0)) - } - _ => None + Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), + Some(_) => Some(Duration::from_millis(0)), + _ => None, } } 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; + 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(cx.now, |frame| { match cx.caps.medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - match inner.process_ethernet(cx, sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { - if let Err(err) = inner.dispatch(cx, tx_token, packet) { - net_debug!("Failed to send response: {}", err); - } + Medium::Ethernet => match inner.process_ethernet(cx, sockets, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + if let Err(err) = inner.dispatch(cx, tx_token, packet) { + net_debug!("Failed to send response: {}", err); } } - Err(err) => { - net_debug!("cannot process ingress packet: {}", err); - #[cfg(not(feature = "defmt"))] - net_debug!("packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame)); - } } - } + Err(err) => { + net_debug!("cannot process ingress packet: {}", err); + #[cfg(not(feature = "defmt"))] + net_debug!( + "packet dump follows:\n{}", + PrettyPrinter::>::new("", &frame) + ); + } + }, #[cfg(feature = "medium-ip")] - Medium::Ip => { - match inner.process_ip(cx, sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { - if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) { - net_debug!("Failed to send response: {}", err); - } + Medium::Ip => match inner.process_ip(cx, sockets, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) { + net_debug!("Failed to send response: {}", err); } } - Err(err) => net_debug!("cannot process ingress packet: {}", err), } - } + Err(err) => net_debug!("cannot process ingress packet: {}", err), + }, } Ok(()) @@ -652,79 +703,88 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let mut emitted_any = false; for mut socket in sockets.iter_mut() { - if !socket.meta_mut().egress_permitted(cx.now, |ip_addr| - self.inner.has_neighbor(cx, &ip_addr)) { - continue + if !socket + .meta_mut() + .egress_permitted(cx.now, |ip_addr| self.inner.has_neighbor(cx, &ip_addr)) + { + continue; } let mut neighbor_addr = None; let mut device_result = Ok(()); - let &mut Self { ref mut device, ref mut inner } = self; + let &mut Self { + ref mut device, + ref mut inner, + } = self; macro_rules! respond { - ($response:expr) => ({ + ($response:expr) => {{ 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(cx, tx_token, response); device_result - }) + }}; } - - let socket_result = - match *socket { - #[cfg(feature = "socket-raw")] - Socket::Raw(ref mut socket) => - 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(cx, |response| { - match response { - #[cfg(feature = "proto-ipv4")] - (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => - respond!(IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))), - #[cfg(feature = "proto-ipv6")] - (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => - respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))), - _ => Err(Error::Unaddressable) - } - }), - #[cfg(feature = "socket-udp")] - Socket::Udp(ref mut socket) => - socket.dispatch(cx, |response| - respond!(IpPacket::Udp(response))), - #[cfg(feature = "socket-tcp")] - Socket::Tcp(ref mut socket) => { - socket.dispatch(cx, |response| - respond!(IpPacket::Tcp(response))) + let socket_result = match *socket { + #[cfg(feature = "socket-raw")] + Socket::Raw(ref mut socket) => { + 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(cx, |response| match response { + #[cfg(feature = "proto-ipv4")] + (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { + respond!(IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) } - #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(ref mut socket) => - // todo don't unwrap - socket.dispatch(cx, |response| - respond!(IpPacket::Dhcpv4(response))), - }; + #[cfg(feature = "proto-ipv6")] + (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { + respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) + } + _ => Err(Error::Unaddressable), + }), + #[cfg(feature = "socket-udp")] + Socket::Udp(ref mut socket) => { + socket.dispatch(cx, |response| respond!(IpPacket::Udp(response))) + } + #[cfg(feature = "socket-tcp")] + Socket::Tcp(ref mut socket) => { + socket.dispatch(cx, |response| respond!(IpPacket::Tcp(response))) + } + #[cfg(feature = "socket-dhcpv4")] + Socket::Dhcpv4(ref mut socket) => + // todo don't unwrap + { + socket.dispatch(cx, |response| respond!(IpPacket::Dhcpv4(response))) + } + }; match (device_result, socket_result) { - (Err(Error::Exhausted), _) => break, // nowhere to transmit - (Ok(()), Err(Error::Exhausted)) => (), // nothing to transmit + (Err(Error::Exhausted), _) => break, // nowhere to transmit + (Ok(()), Err(Error::Exhausted)) => (), // nothing to transmit (Err(Error::Unaddressable), _) => { // `NeighborCache` already takes care of rate limiting the neighbor discovery // 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(cx.now, - neighbor_addr.expect("non-IP response packet")); - break + socket + .meta_mut() + .neighbor_missing(cx.now, neighbor_addr.expect("non-IP response packet")); + break; } (Err(err), _) | (_, Err(err)) => { - net_debug!("{}: cannot dispatch egress packet: {}", - socket.meta().handle, err); - return Err(err) + net_debug!( + "{}: cannot dispatch egress packet: {}", + socket.meta().handle, + err + ); + return Err(err); } - (Ok(()), Ok(())) => emitted_any = true + (Ok(()), Ok(())) => emitted_any = true, } } Ok(emitted_any) @@ -735,8 +795,11 @@ impl<'a, DeviceT> Interface<'a, DeviceT> #[cfg(feature = "proto-igmp")] fn igmp_egress(&mut self, cx: &Context, timestamp: Instant) -> Result { match self.inner.igmp_report_state { - IgmpReportState::ToSpecificQuery { version, timeout, group } - if timestamp >= timeout => { + 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)?; @@ -746,9 +809,15 @@ impl<'a, DeviceT> Interface<'a, DeviceT> self.inner.igmp_report_state = IgmpReportState::Inactive; Ok(true) } - IgmpReportState::ToGeneralQuery { version, timeout, interval, next_index } - if timestamp >= timeout => { - let addr = self.inner.ipv4_multicast_groups + IgmpReportState::ToGeneralQuery { + version, + timeout, + interval, + next_index, + } if timestamp >= timeout => { + let addr = self + .inner + .ipv4_multicast_groups .iter() .nth(next_index) .map(|(addr, ())| *addr); @@ -763,7 +832,10 @@ impl<'a, DeviceT> Interface<'a, DeviceT> let next_timeout = (timeout + interval).max(timestamp); self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, timeout: next_timeout, interval, next_index: next_index + 1 + version, + timeout: next_timeout, + interval, + next_index: next_index + 1, }; Ok(true) } @@ -774,7 +846,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } } } - _ => Ok(false) + _ => Ok(false), } } @@ -813,7 +885,7 @@ impl<'a> InterfaceInner<'a> { pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool { self.ip_addrs.iter().any(|cidr| { match *cidr { - IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK=> { + IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => { // Take the lower order 24 bits of the IPv6 address and // append those bits to FF02:0:0:0:0:1:FF00::/104. addr.as_bytes()[14..] == cidr.address().as_bytes()[14..] @@ -832,13 +904,13 @@ impl<'a> InterfaceInner<'a> { /// Get the first IPv4 address of the interface. #[cfg(feature = "proto-ipv4")] pub fn ipv4_address(&self) -> Option { - self.ip_addrs.iter() - .filter_map( - |addr| match *addr { - IpCidr::Ipv4(cidr) => Some(cidr.address()), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None - }) + self.ip_addrs + .iter() + .filter_map(|addr| match *addr { + IpCidr::Ipv4(cidr) => Some(cidr.address()), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None, + }) .next() } @@ -849,33 +921,34 @@ impl<'a> InterfaceInner<'a> { pub fn has_multicast_group>(&self, addr: T) -> bool { match addr.into() { #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(key) => - key == Ipv4Address::MULTICAST_ALL_SYSTEMS || - self.ipv4_multicast_groups.get(&key).is_some(), - _ => - false, + IpAddress::Ipv4(key) => { + key == Ipv4Address::MULTICAST_ALL_SYSTEMS + || self.ipv4_multicast_groups.get(&key).is_some() + } + _ => false, } } #[cfg(feature = "medium-ethernet")] - fn process_ethernet<'frame, T: AsRef<[u8]>> - (&mut self, cx: &Context, sockets: &mut SocketSet, frame: &'frame T) -> - Result>> - { + fn process_ethernet<'frame, T: AsRef<[u8]>>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + frame: &'frame T, + ) -> Result>> { let eth_frame = EthernetFrame::new_checked(frame)?; // Ignore any packets not directed to our hardware address or any of the multicast groups. - if !eth_frame.dst_addr().is_broadcast() && - !eth_frame.dst_addr().is_multicast() && - eth_frame.dst_addr() != self.ethernet_addr.unwrap() + if !eth_frame.dst_addr().is_broadcast() + && !eth_frame.dst_addr().is_multicast() + && eth_frame.dst_addr() != self.ethernet_addr.unwrap() { - return Ok(None) + return Ok(None); } match eth_frame.ethertype() { #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Arp => - self.process_arp(cx.now, ð_frame), + EthernetProtocol::Arp => self.process_arp(cx.now, ð_frame), #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => { let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; @@ -883,25 +956,41 @@ 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(), cx.now); + self.neighbor_cache.as_mut().unwrap().fill( + ip_addr, + eth_frame.src_addr(), + cx.now, + ); } } - self.process_ipv4(cx, sockets, &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 => { let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; - if eth_frame.src_addr().is_unicast() && ipv6_packet.src_addr().is_unicast() { + if eth_frame.src_addr().is_unicast() && ipv6_packet.src_addr().is_unicast() { // 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, cx.now).found() { - self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), cx.now); + if self.in_same_network(&ip_addr) + && 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(cx, sockets, &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), @@ -909,10 +998,12 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "medium-ip")] - fn process_ip<'frame, T: AsRef<[u8]>> - (&mut self, cx: &Context, sockets: &mut SocketSet, ip_payload: &'frame T) -> - Result>> - { + fn process_ip<'frame, T: AsRef<[u8]>>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ip_payload: &'frame T, + ) -> Result>> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { @@ -930,10 +1021,11 @@ impl<'a> InterfaceInner<'a> { } #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] - fn process_arp<'frame, T: AsRef<[u8]>> - (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> - Result>> - { + fn process_arp<'frame, T: AsRef<[u8]>>( + &mut self, + timestamp: Instant, + eth_frame: &EthernetFrame<&'frame T>, + ) -> Result>> { let arp_packet = ArpPacket::new_checked(eth_frame.payload())?; let arp_repr = ArpRepr::parse(&arp_packet)?; @@ -942,16 +1034,22 @@ impl<'a> InterfaceInner<'a> { // requests and replies, to minimize the chance that we have to perform // an explicit ARP request. ArpRepr::EthernetIpv4 { - operation, source_hardware_addr, source_protocol_addr, target_protocol_addr, .. + operation, + source_hardware_addr, + source_protocol_addr, + target_protocol_addr, + .. } => { if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() { - self.neighbor_cache.as_mut().unwrap().fill(source_protocol_addr.into(), - source_hardware_addr, - timestamp); + self.neighbor_cache.as_mut().unwrap().fill( + source_protocol_addr.into(), + source_hardware_addr, + timestamp, + ); } else { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); - return Err(Error::Malformed) + return Err(Error::Malformed); } if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { @@ -960,7 +1058,7 @@ impl<'a> InterfaceInner<'a> { source_hardware_addr: self.ethernet_addr.unwrap(), source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, - target_protocol_addr: source_protocol_addr + target_protocol_addr: source_protocol_addr, }))) } else { Ok(None) @@ -969,14 +1067,24 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "socket-raw"))] - fn raw_socket_filter<'frame>(&mut self, cx: &Context, sockets: &mut SocketSet, ip_repr: &IpRepr, - ip_payload: &'frame [u8]) -> bool { + #[cfg(all( + any(feature = "proto-ipv4", feature = "proto-ipv6"), + feature = "socket-raw" + ))] + fn raw_socket_filter<'frame>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ip_repr: &IpRepr, + ip_payload: &'frame [u8], + ) -> bool { 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 } + if !raw_socket.accepts(&ip_repr) { + continue; + } match raw_socket.process(cx, &ip_repr, ip_payload) { // The packet is valid and handled by socket. @@ -991,84 +1099,101 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv6")] - fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized> - (&mut self, cx: &Context, sockets: &mut SocketSet, - ipv6_packet: &Ipv6Packet<&'frame T>) -> - Result>> { + fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ipv6_packet: &Ipv6Packet<&'frame T>, + ) -> Result>> { let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; if !ipv6_repr.src_addr.is_unicast() { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); - return Err(Error::Malformed) + return Err(Error::Malformed); } let ip_payload = ipv6_packet.payload(); #[cfg(feature = "socket-raw")] - let handled_by_raw_socket = self.raw_socket_filter(cx, 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(cx, sockets, ipv6_repr, ipv6_repr.next_header, - handled_by_raw_socket, ip_payload) + self.process_nxt_hdr( + cx, + sockets, + ipv6_repr, + ipv6_repr.next_header, + handled_by_raw_socket, + ip_payload, + ) } /// Given the next header value forward the payload onto the correct process /// function. #[cfg(feature = "proto-ipv6")] - fn process_nxt_hdr<'frame> - (&mut self, cx: &Context, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr, - nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) - -> Result>> - { + fn process_nxt_hdr<'frame>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ipv6_repr: Ipv6Repr, + nxt_hdr: IpProtocol, + handled_by_raw_socket: bool, + ip_payload: &'frame [u8], + ) -> Result>> { match nxt_hdr { - IpProtocol::Icmpv6 => - self.process_icmpv6(cx, sockets, ipv6_repr.into(), ip_payload), + IpProtocol::Icmpv6 => self.process_icmpv6(cx, sockets, ipv6_repr.into(), ip_payload), #[cfg(feature = "socket-udp")] - IpProtocol::Udp => - self.process_udp(cx, sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload), + IpProtocol::Udp => self.process_udp( + cx, + sockets, + ipv6_repr.into(), + handled_by_raw_socket, + ip_payload, + ), #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => - self.process_tcp(cx, sockets, ipv6_repr.into(), ip_payload), + IpProtocol::Tcp => self.process_tcp(cx, sockets, ipv6_repr.into(), ip_payload), - IpProtocol::HopByHop => - self.process_hopbyhop(cx, sockets, ipv6_repr, handled_by_raw_socket, ip_payload), + IpProtocol::HopByHop => { + self.process_hopbyhop(cx, sockets, ipv6_repr, handled_by_raw_socket, ip_payload) + } #[cfg(feature = "socket-raw")] - _ if handled_by_raw_socket => - Ok(None), + _ if handled_by_raw_socket => Ok(None), _ => { // Send back as much of the original payload as we can. - let payload_len = icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, - ipv6_repr.buffer_len()); + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); let icmp_reply_repr = Icmpv6Repr::ParamProblem { reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, // The offending packet is after the IPv6 header. pointer: ipv6_repr.buffer_len() as u32, header: ipv6_repr, - data: &ip_payload[0..payload_len] + data: &ip_payload[0..payload_len], }; Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) - }, + } } } #[cfg(feature = "proto-ipv4")] - fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized> - (&mut self, cx: &Context, sockets: &mut SocketSet, - ipv4_packet: &Ipv4Packet<&'frame T>) -> - Result>> - { + fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ipv4_packet: &Ipv4Packet<&'frame T>, + ) -> Result>> { 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. net_debug!("non-unicast source address"); - return Err(Error::Malformed) + return Err(Error::Malformed); } let ip_repr = IpRepr::Ipv4(ipv4_repr); @@ -1079,70 +1204,74 @@ impl<'a> InterfaceInner<'a> { #[cfg(not(feature = "socket-raw"))] let handled_by_raw_socket = false; - #[cfg(feature = "socket-dhcpv4")] { if ipv4_repr.protocol == IpProtocol::Udp && self.ethernet_addr.is_some() { // First check for source and dest ports, then do `UdpRepr::parse` if they match. // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) let udp_packet = UdpPacket::new_checked(ip_payload)?; - 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() { + 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 udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; + let udp_repr = + UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; let udp_payload = udp_packet.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. - Err(e) => return Err(e) + Err(e) => return Err(e), } } } } } - if !self.has_ip_addr(ipv4_repr.dst_addr) && - !self.has_multicast_group(ipv4_repr.dst_addr) && - !self.is_broadcast_v4(ipv4_repr.dst_addr) { - + if !self.has_ip_addr(ipv4_repr.dst_addr) + && !self.has_multicast_group(ipv4_repr.dst_addr) + && !self.is_broadcast_v4(ipv4_repr.dst_addr) + { // 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), cx.now) - .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) { + if !self.any_ip + || 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); } } match ipv4_repr.protocol { - IpProtocol::Icmp => - self.process_icmpv4(cx, sockets, ip_repr, ip_payload), + IpProtocol::Icmp => self.process_icmpv4(cx, sockets, ip_repr, ip_payload), #[cfg(feature = "proto-igmp")] - IpProtocol::Igmp => - self.process_igmp(cx, ipv4_repr, ip_payload), + IpProtocol::Igmp => self.process_igmp(cx, ipv4_repr, ip_payload), #[cfg(feature = "socket-udp")] - IpProtocol::Udp => - self.process_udp(cx, sockets, ip_repr, handled_by_raw_socket, ip_payload), + IpProtocol::Udp => { + self.process_udp(cx, sockets, ip_repr, handled_by_raw_socket, ip_payload) + } #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => - self.process_tcp(cx, sockets, ip_repr, ip_payload), + IpProtocol::Tcp => self.process_tcp(cx, sockets, ip_repr, ip_payload), - _ if handled_by_raw_socket => - Ok(None), + _ if handled_by_raw_socket => Ok(None), _ => { // Send back as much of the original payload as we can. - let payload_len = icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, - ipv4_repr.buffer_len()); + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); let icmp_reply_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::ProtoUnreachable, header: ipv4_repr, - data: &ip_payload[0..payload_len] + data: &ip_payload[0..payload_len], }; Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)) } @@ -1153,15 +1282,16 @@ impl<'a> InterfaceInner<'a> { /// associated ipv4 addresses. #[cfg(feature = "proto-ipv4")] fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { - self.ip_addrs.iter() + self.ip_addrs + .iter() .filter_map(|own_cidr| match own_cidr { IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?), #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None + IpCidr::Ipv6(_) => None, }) .any(|broadcast_address| address == broadcast_address) - } - + } + /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses #[cfg(feature = "proto-ipv4")] fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { @@ -1180,22 +1310,30 @@ 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, cx: &Context, ipv4_repr: Ipv4Repr, - ip_payload: &'frame [u8]) -> Result>> { + fn process_igmp<'frame>( + &mut self, + cx: &Context, + ipv4_repr: Ipv4Repr, + ip_payload: &'frame [u8], + ) -> Result>> { let igmp_packet = IgmpPacket::new_checked(ip_payload)?; let igmp_repr = IgmpRepr::parse(&igmp_packet)?; // FIXME: report membership after a delay match igmp_repr { - IgmpRepr::MembershipQuery { group_addr, version, max_resp_time } => { + IgmpRepr::MembershipQuery { + group_addr, + version, + max_resp_time, + } => { // General query - if group_addr.is_unspecified() && - ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS { + if group_addr.is_unspecified() + && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS + { // Are we member in any groups? if self.ipv4_multicast_groups.iter().next().is_some() { let interval = match version { - IgmpVersion::Version1 => - Duration::from_millis(100), + IgmpVersion::Version1 => Duration::from_millis(100), IgmpVersion::Version2 => { // No dependence on a random generator // (see [#24](https://github.com/m-labs/smoltcp/issues/24)) @@ -1205,7 +1343,10 @@ impl<'a> InterfaceInner<'a> { } }; self.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, timeout: cx.now + interval, interval, next_index: 0 + version, + timeout: cx.now + interval, + interval, + next_index: 0, }; } } else { @@ -1214,34 +1355,46 @@ impl<'a> InterfaceInner<'a> { // Don't respond immediately let timeout = max_resp_time / 4; self.igmp_report_state = IgmpReportState::ToSpecificQuery { - version, timeout: cx.now + timeout, group: group_addr + version, + timeout: cx.now + timeout, + group: group_addr, }; } } - }, + } // Ignore membership reports IgmpRepr::MembershipReport { .. } => (), // Ignore hosts leaving groups - IgmpRepr::LeaveGroup{ .. } => (), + IgmpRepr::LeaveGroup { .. } => (), } Ok(None) } #[cfg(feature = "proto-ipv6")] - fn process_icmpv6<'frame>(&mut self, cx: &Context, _sockets: &mut SocketSet, - ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> - { + fn process_icmpv6<'frame>( + &mut self, + cx: &Context, + _sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Result>> { let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; - let icmp_repr = Icmpv6Repr::parse(&ip_repr.src_addr(), &ip_repr.dst_addr(), - &icmp_packet, &cx.caps.checksum)?; + let icmp_repr = Icmpv6Repr::parse( + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &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(cx, &ip_repr, &icmp_repr.into()) { continue } + if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { + continue; + } match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) { // The packet is valid and handled by socket. @@ -1255,15 +1408,21 @@ impl<'a> InterfaceInner<'a> { match icmp_repr { // Respond to echo requests. - Icmpv6Repr::EchoRequest { ident, seq_no, data } => { - match ip_repr { - IpRepr::Ipv6(ipv6_repr) => { - let icmp_reply_repr = Icmpv6Repr::EchoReply { ident, seq_no, data }; - Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) - }, - _ => Err(Error::Unrecognized), + Icmpv6Repr::EchoRequest { + ident, + seq_no, + data, + } => match ip_repr { + IpRepr::Ipv6(ipv6_repr) => { + let icmp_reply_repr = Icmpv6Repr::EchoReply { + ident, + seq_no, + data, + }; + Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) } - } + _ => Err(Error::Unrecognized), + }, // Ignore any echo replies. Icmpv6Repr::EchoReply { .. } => Ok(None), @@ -1272,7 +1431,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ethernet")] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx.now, ipv6_repr, repr), - _ => Ok(None) + _ => Ok(None), }, // Don't report an error if a packet with unknown type @@ -1286,56 +1445,83 @@ impl<'a> InterfaceInner<'a> { } #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] - fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, - repr: NdiscRepr<'frame>) -> Result>> { + fn process_ndisc<'frame>( + &mut self, + timestamp: Instant, + ip_repr: Ipv6Repr, + repr: NdiscRepr<'frame>, + ) -> Result>> { match repr { - NdiscRepr::NeighborAdvert { lladdr, target_addr, flags } => { + NdiscRepr::NeighborAdvert { + lladdr, + target_addr, + flags, + } => { let ip_addr = ip_repr.src_addr.into(); match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - if flags.contains(NdiscNeighborFlags::OVERRIDE) || - !self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.as_mut().unwrap().fill(ip_addr, lladdr, timestamp) + if flags.contains(NdiscNeighborFlags::OVERRIDE) + || !self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&ip_addr, timestamp) + .found() + { + self.neighbor_cache + .as_mut() + .unwrap() + .fill(ip_addr, lladdr, timestamp) } - }, + } _ => (), } Ok(None) } - NdiscRepr::NeighborSolicit { target_addr, lladdr, .. } => { + NdiscRepr::NeighborSolicit { + target_addr, + lladdr, + .. + } => { match lladdr { - Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - self.neighbor_cache.as_mut().unwrap().fill(ip_repr.src_addr.into(), lladdr, timestamp) - }, + Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => self + .neighbor_cache + .as_mut() + .unwrap() + .fill(ip_repr.src_addr.into(), lladdr, timestamp), _ => (), } if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr: target_addr, - lladdr: Some(self.ethernet_addr.unwrap()) + lladdr: Some(self.ethernet_addr.unwrap()), }); let ip_repr = Ipv6Repr { src_addr: target_addr, dst_addr: ip_repr.src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: advert.buffer_len() + payload_len: advert.buffer_len(), }; Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) } else { Ok(None) } } - _ => Ok(None) + _ => Ok(None), } } #[cfg(feature = "proto-ipv6")] - fn process_hopbyhop<'frame>(&mut self, cx: &Context, sockets: &mut SocketSet, - ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, - ip_payload: &'frame [u8]) -> Result>> - { + fn process_hopbyhop<'frame>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ipv6_repr: Ipv6Repr, + handled_by_raw_socket: bool, + ip_payload: &'frame [u8], + ) -> Result>> { let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?; let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?; for result in hbh_repr.options() { @@ -1347,7 +1533,7 @@ impl<'a> InterfaceInner<'a> { Ipv6OptionFailureType::Skip => (), Ipv6OptionFailureType::Discard => { return Ok(None); - }, + } _ => { // FIXME(dlrobertson): Send an ICMPv6 parameter problem message // here. @@ -1357,14 +1543,24 @@ impl<'a> InterfaceInner<'a> { } } } - self.process_nxt_hdr(cx, sockets, ipv6_repr, hbh_repr.next_header, - handled_by_raw_socket, &ip_payload[hbh_repr.buffer_len()..]) + 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, cx: &Context, _sockets: &mut SocketSet, ip_repr: IpRepr, - ip_payload: &'frame [u8]) -> Result>> - { + fn process_icmpv4<'frame>( + &self, + cx: &Context, + _sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Result>> { let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?; let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &cx.caps.checksum)?; @@ -1373,7 +1569,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) { - if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue } + if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { + continue; + } match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) { // The packet is valid and handled by socket. @@ -1388,13 +1586,21 @@ impl<'a> InterfaceInner<'a> { match icmp_repr { // Respond to echo requests. #[cfg(feature = "proto-ipv4")] - Icmpv4Repr::EchoRequest { ident, seq_no, data } => { - let icmp_reply_repr = Icmpv4Repr::EchoReply { ident, seq_no, data }; + Icmpv4Repr::EchoRequest { + ident, + seq_no, + data, + } => { + let icmp_reply_repr = Icmpv4Repr::EchoReply { + ident, + seq_no, + data, + }; match ip_repr { IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)), _ => Err(Error::Unrecognized), } - }, + } // Ignore any echo replies. Icmpv4Repr::EchoReply { .. } => Ok(None), @@ -1410,37 +1616,38 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv4")] - fn icmpv4_reply<'frame, 'icmp: 'frame> - (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) -> - Option> - { + fn icmpv4_reply<'frame, 'icmp: 'frame>( + &self, + ipv4_repr: Ipv4Repr, + icmp_repr: Icmpv4Repr<'icmp>, + ) -> Option> { if !self.is_unicast_v4(ipv4_repr.src_addr) { // Do not send ICMP replies to non-unicast sources None } else if self.is_unicast_v4(ipv4_repr.dst_addr) { // Reply as normal when src_addr and dst_addr are both unicast let ipv4_reply_repr = Ipv4Repr { - src_addr: ipv4_repr.dst_addr, - dst_addr: ipv4_repr.src_addr, - protocol: IpProtocol::Icmp, + src_addr: ipv4_repr.dst_addr, + dst_addr: ipv4_repr.src_addr, + protocol: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { // Only reply to broadcasts for echo replies and not other ICMP messages match icmp_repr { - Icmpv4Repr::EchoReply {..} => match self.ipv4_address() { + Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() { Some(src_addr) => { let ipv4_reply_repr = Ipv4Repr { - src_addr: src_addr, - dst_addr: ipv4_repr.src_addr, - protocol: IpProtocol::Icmp, + src_addr: src_addr, + dst_addr: ipv4_repr.src_addr, + protocol: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) - }, + } None => None, }, _ => None, @@ -1451,17 +1658,18 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv6")] - fn icmpv6_reply<'frame, 'icmp: 'frame> - (&self, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>) -> - Option> - { + fn icmpv6_reply<'frame, 'icmp: 'frame>( + &self, + ipv6_repr: Ipv6Repr, + icmp_repr: Icmpv6Repr<'icmp>, + ) -> Option> { if ipv6_repr.dst_addr.is_unicast() { let ipv6_reply_repr = Ipv6Repr { - src_addr: ipv6_repr.dst_addr, - dst_addr: ipv6_repr.src_addr, + src_addr: ipv6_repr.dst_addr, + dst_addr: ipv6_repr.src_addr, next_header: IpProtocol::Icmpv6, payload_len: icmp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) } else { @@ -1471,78 +1679,87 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "socket-udp")] - fn process_udp<'frame>(&self, cx: &Context, sockets: &mut SocketSet, - ip_repr: IpRepr, handled_by_raw_socket: bool, ip_payload: &'frame [u8]) -> - Result>> - { + fn process_udp<'frame>( + &self, + cx: &Context, + sockets: &mut SocketSet, + ip_repr: IpRepr, + handled_by_raw_socket: bool, + ip_payload: &'frame [u8], + ) -> Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_packet = UdpPacket::new_checked(ip_payload)?; 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 } + if !udp_socket.accepts(&ip_repr, &udp_repr) { + continue; + } 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. - Err(e) => return Err(e) + Err(e) => return Err(e), } } // The packet wasn't handled by a socket, send an ICMP port unreachable packet. match ip_repr { #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) if handled_by_raw_socket => - Ok(None), + IpRepr::Ipv4(_) if handled_by_raw_socket => Ok(None), #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) if handled_by_raw_socket => - Ok(None), + IpRepr::Ipv6(_) if handled_by_raw_socket => Ok(None), #[cfg(feature = "proto-ipv4")] IpRepr::Ipv4(ipv4_repr) => { - let payload_len = icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, - ipv4_repr.buffer_len()); + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::PortUnreachable, header: ipv4_repr, - data: &ip_payload[0..payload_len] + data: &ip_payload[0..payload_len], }; Ok(self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)) - }, + } #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(ipv6_repr) => { - let payload_len = icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, - ipv6_repr.buffer_len()); + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { reason: Icmpv6DstUnreachable::PortUnreachable, header: ipv6_repr, - data: &ip_payload[0..payload_len] + data: &ip_payload[0..payload_len], }; Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) - }, + } IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } } #[cfg(feature = "socket-tcp")] - fn process_tcp<'frame>(&self, cx: &Context, sockets: &mut SocketSet, - ip_repr: IpRepr, ip_payload: &'frame [u8]) -> - Result>> - { + fn process_tcp<'frame>( + &self, + cx: &Context, + sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let tcp_packet = TcpPacket::new_checked(ip_payload)?; 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 } + if !tcp_socket.accepts(&ip_repr, &tcp_repr) { + continue; + } 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, // or the socket buffer is full. - Err(e) => return Err(e) + Err(e) => return Err(e), } } @@ -1551,22 +1768,26 @@ impl<'a> InterfaceInner<'a> { Ok(None) } else { // The packet wasn't handled by a socket, send a TCP RST packet. - Ok(Some(IpPacket::Tcp(TcpSocket::rst_reply(&ip_repr, &tcp_repr)))) + Ok(Some(IpPacket::Tcp(TcpSocket::rst_reply( + &ip_repr, &tcp_repr, + )))) } } #[cfg(feature = "medium-ethernet")] - fn dispatch(&mut self, cx: &Context, tx_token: Tx, - packet: EthernetPacket) -> Result<()> - where Tx: TxToken + fn dispatch(&mut self, cx: &Context, tx_token: Tx, packet: EthernetPacket) -> Result<()> + where + Tx: TxToken, { match packet { #[cfg(feature = "proto-ipv4")] EthernetPacket::Arp(arp_repr) => { - let dst_hardware_addr = - match arp_repr { - ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr, - }; + let dst_hardware_addr = match arp_repr { + ArpRepr::EthernetIpv4 { + target_hardware_addr, + .. + } => target_hardware_addr, + }; self.dispatch_ethernet(cx, tx_token, arp_repr.buffer_len(), |mut frame| { frame.set_dst_addr(dst_hardware_addr); @@ -1575,17 +1796,22 @@ impl<'a> InterfaceInner<'a> { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); arp_repr.emit(&mut packet); }) - }, - EthernetPacket::Ip(packet) => { - self.dispatch_ip(cx, tx_token, packet) - }, + } + EthernetPacket::Ip(packet) => self.dispatch_ip(cx, tx_token, packet), } } #[cfg(feature = "medium-ethernet")] - fn dispatch_ethernet(&mut self, cx: &Context, tx_token: Tx, - buffer_len: usize, f: F) -> Result<()> - where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>) + fn dispatch_ethernet( + &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(cx.now, tx_len, |tx_buffer| { @@ -1600,15 +1826,13 @@ impl<'a> InterfaceInner<'a> { } fn in_same_network(&self, addr: &IpAddress) -> bool { - self.ip_addrs - .iter() - .any(|cidr| cidr.contains_addr(addr)) + self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr)) } fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { // Send directly. if self.in_same_network(addr) || addr.is_broadcast() { - return Ok(*addr) + return Ok(*addr); } // Route via a router. @@ -1620,67 +1844,75 @@ impl<'a> InterfaceInner<'a> { fn has_neighbor(&self, cx: &Context, addr: &IpAddress) -> bool { match self.route(addr, cx.now) { - Ok(_routed_addr) => { - match cx.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => self.neighbor_cache.as_ref().unwrap() - .lookup(&_routed_addr, cx.now) - .found(), - #[cfg(feature = "medium-ip")] - Medium::Ip => true, - } - } - Err(_) => false + Ok(_routed_addr) => match cx.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => self + .neighbor_cache + .as_ref() + .unwrap() + .lookup(&_routed_addr, cx.now) + .found(), + #[cfg(feature = "medium-ip")] + Medium::Ip => true, + }, + Err(_) => false, } } #[cfg(feature = "medium-ethernet")] - fn lookup_hardware_addr(&mut self, cx: &Context, tx_token: Tx, - src_addr: &IpAddress, dst_addr: &IpAddress) -> - Result<(EthernetAddress, Tx)> - where Tx: TxToken + fn lookup_hardware_addr( + &mut self, + cx: &Context, + tx_token: Tx, + src_addr: &IpAddress, + dst_addr: &IpAddress, + ) -> Result<(EthernetAddress, Tx)> + where + Tx: TxToken, { if dst_addr.is_multicast() { let b = dst_addr.as_bytes(); - let hardware_addr = - match *dst_addr { - IpAddress::Unspecified => - None, - #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(_addr) => - Some(EthernetAddress::from_bytes(&[ - 0x01, 0x00, - 0x5e, b[1] & 0x7F, - b[2], b[3], - ])), - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(_addr) => - Some(EthernetAddress::from_bytes(&[ - 0x33, 0x33, - b[12], b[13], - b[14], b[15], - ])), - }; + let hardware_addr = match *dst_addr { + IpAddress::Unspecified => None, + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(_addr) => Some(EthernetAddress::from_bytes(&[ + 0x01, + 0x00, + 0x5e, + b[1] & 0x7F, + b[2], + b[3], + ])), + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(_addr) => Some(EthernetAddress::from_bytes(&[ + 0x33, 0x33, b[12], b[13], b[14], b[15], + ])), + }; if let Some(hardware_addr) = hardware_addr { - return Ok((hardware_addr, tx_token)) + return Ok((hardware_addr, tx_token)); } } let dst_addr = self.route(dst_addr, cx.now)?; - match self.neighbor_cache.as_mut().unwrap().lookup(&dst_addr, cx.now) { - NeighborAnswer::Found(hardware_addr) => - return Ok((hardware_addr, tx_token)), - NeighborAnswer::RateLimited => - return Err(Error::Unaddressable), + match self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&dst_addr, cx.now) + { + NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), + NeighborAnswer::RateLimited => return Err(Error::Unaddressable), NeighborAnswer::NotFound => (), } match (src_addr, dst_addr) { #[cfg(feature = "proto-ipv4")] (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => { - net_debug!("address {} not in neighbor cache, sending ARP request", - dst_addr); + net_debug!( + "address {} not in neighbor cache, sending ARP request", + dst_addr + ); let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, @@ -1700,8 +1932,10 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => { - net_debug!("address {} not in neighbor cache, sending Neighbor Solicitation", - dst_addr); + net_debug!( + "address {} not in neighbor cache, sending Neighbor Solicitation", + dst_addr + ); let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, @@ -1714,7 +1948,7 @@ impl<'a> InterfaceInner<'a> { dst_addr: dst_addr.solicited_node(), next_header: IpProtocol::Icmpv6, payload_len: solicit.buffer_len(), - hop_limit: 0xff + hop_limit: 0xff, }, solicit, )); @@ -1722,23 +1956,30 @@ impl<'a> InterfaceInner<'a> { 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(cx.now); Err(Error::Unaddressable) } - fn dispatch_ip(&mut self, cx: &Context, tx_token: Tx, - packet: IpPacket) -> Result<()> { + fn dispatch_ip( + &mut self, + cx: &Context, + tx_token: Tx, + packet: IpPacket, + ) -> Result<()> { let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; match cx.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - let (dst_hardware_addr, tx_token) = - self.lookup_hardware_addr(cx, tx_token, - &ip_repr.src_addr(), &ip_repr.dst_addr())?; + let (dst_hardware_addr, tx_token) = self.lookup_hardware_addr( + cx, + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )?; self.dispatch_ethernet(cx, tx_token, ip_repr.total_len(), |mut frame| { frame.set_dst_addr(dst_hardware_addr); @@ -1747,7 +1988,7 @@ impl<'a> InterfaceInner<'a> { IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), - _ => return + _ => return, } ip_repr.emit(frame.payload_mut(), &cx.caps.checksum); @@ -1774,22 +2015,29 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-igmp")] - fn igmp_report_packet<'any>(&self, version: IgmpVersion, group_addr: Ipv4Address) -> Option> { + fn igmp_report_packet<'any>( + &self, + version: IgmpVersion, + group_addr: Ipv4Address, + ) -> Option> { let iface_addr = self.ipv4_address()?; let igmp_repr = IgmpRepr::MembershipReport { group_addr, version, }; - let pkt = IpPacket::Igmp((Ipv4Repr { - src_addr: iface_addr, - // Send to the group being reported - dst_addr: group_addr, - protocol: IpProtocol::Igmp, - payload_len: igmp_repr.buffer_len(), - hop_limit: 1, - // TODO: add Router Alert IPv4 header option. See - // [#183](https://github.com/m-labs/smoltcp/issues/183). - }, igmp_repr)); + let pkt = IpPacket::Igmp(( + Ipv4Repr { + src_addr: iface_addr, + // Send to the group being reported + dst_addr: group_addr, + protocol: IpProtocol::Igmp, + payload_len: igmp_repr.buffer_len(), + hop_limit: 1, + // TODO: add Router Alert IPv4 header option. See + // [#183](https://github.com/m-labs/smoltcp/issues/183). + }, + igmp_repr, + )); Some(pkt) } @@ -1797,33 +2045,36 @@ impl<'a> InterfaceInner<'a> { fn igmp_leave_packet<'any>(&self, group_addr: Ipv4Address) -> Option> { self.ipv4_address().map(|iface_addr| { let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; - IpPacket::Igmp((Ipv4Repr { - src_addr: iface_addr, - dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, - protocol: IpProtocol::Igmp, - payload_len: igmp_repr.buffer_len(), - hop_limit: 1, - }, igmp_repr)) + IpPacket::Igmp(( + Ipv4Repr { + src_addr: iface_addr, + dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, + protocol: IpProtocol::Igmp, + payload_len: igmp_repr.buffer_len(), + hop_limit: 1, + }, + igmp_repr, + )) }) } } #[cfg(test)] mod test { + use std::collections::BTreeMap; #[cfg(feature = "proto-igmp")] use std::vec::Vec; - use std::collections::BTreeMap; use super::*; - use crate::{Result, Error}; + use crate::iface::Interface; #[cfg(feature = "medium-ethernet")] use crate::iface::NeighborCache; - use crate::iface::Interface; + use crate::phy::{ChecksumCapabilities, Loopback}; + use crate::socket::SocketSet; #[cfg(feature = "proto-igmp")] use crate::time::Instant; - use crate::socket::SocketSet; - use crate::phy::{Loopback, ChecksumCapabilities}; + use crate::{Error, Result}; #[allow(unused)] fn fill_slice(s: &mut [u8], val: u8) { @@ -1853,13 +2104,10 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - let iface_builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs); + let iface_builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder - .ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder - .finalize(); + let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder.finalize(); (iface, SocketSet::new(vec![])) } @@ -1882,10 +2130,8 @@ mod test { .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder - .ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder - .finalize(); + let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder.finalize(); (iface, SocketSet::new(vec![])) } @@ -1897,7 +2143,8 @@ mod test { rx.consume(timestamp, |pkt| { pkts.push(pkt.to_vec()); Ok(()) - }).unwrap(); + }) + .unwrap(); } pkts } @@ -1908,7 +2155,9 @@ mod test { impl TxToken for MockTxToken { fn consume(self, _: Instant, _: usize, _: F) -> Result - where F: FnOnce(&mut [u8]) -> Result { + where + F: FnOnce(&mut [u8]) -> Result, + { Err(Error::Unaddressable) } } @@ -1931,11 +2180,11 @@ mod test { // this should not trigger and Destination Unreachable // response. See RFC 1122 § 3.2.2. let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Unknown(0x0c), + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Unknown(0x0c), payload_len: 0, - hop_limit: 0x40 + hop_limit: 0x40, }); let mut bytes = vec![0u8; 54]; @@ -1946,8 +2195,10 @@ mod test { // ICMP error response when the destination address is a // broadcast address let cx = iface.context(Instant::from_secs(0)); - assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame), - Ok(None)); + assert_eq!( + iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + Ok(None) + ); } #[test] @@ -1961,11 +2212,11 @@ mod test { // this should not trigger and Destination Unreachable // response. See RFC 1122 § 3.2.2. let repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), + dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, next_header: IpProtocol::Unknown(0x0c), payload_len: 0, - hop_limit: 0x40 + hop_limit: 0x40, }); let mut bytes = vec![0u8; 54]; @@ -1976,8 +2227,10 @@ mod test { // ICMP error response when the destination address is a // broadcast address let cx = iface.context(Instant::from_secs(0)); - assert_eq!(iface.inner.process_ipv6(&cx, &mut socket_set, &frame), - Ok(None)); + assert_eq!( + iface.inner.process_ipv6(&cx, &mut socket_set, &frame), + Ok(None) + ); } #[test] @@ -1988,11 +2241,11 @@ mod test { // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Unknown(0x0c), + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + protocol: IpProtocol::Unknown(0x0c), payload_len: 0, - hop_limit: 0x40 + hop_limit: 0x40, }); let mut bytes = vec![0u8; 34]; @@ -2008,9 +2261,9 @@ mod test { dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), protocol: IpProtocol::Unknown(12), payload_len: 0, - hop_limit: 64 + hop_limit: 64, }, - data: &NO_BYTES + data: &NO_BYTES, }; let expected_repr = IpPacket::Icmpv4(( @@ -2019,16 +2272,18 @@ mod test { dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), protocol: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }, - icmp_repr + icmp_repr, )); // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. let cx = iface.context(Instant::from_secs(0)); - assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame), - Ok(Some(expected_repr))); + assert_eq!( + iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + Ok(Some(expected_repr)) + ); } #[test] @@ -2041,37 +2296,85 @@ mod test { }); }); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), true); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), false); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), + true + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), + false + ); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); }); }); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), true); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), + true + ); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); }); }); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), false); - assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), true); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), + false + ); + assert_eq!( + iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), + true + ); } #[test] #[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] fn test_icmp_error_port_unreachable() { static UDP_PAYLOAD: [u8; 12] = [ - 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x2c, 0x20, 0x57, - 0x6f, 0x6c, 0x64, 0x21 + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; let (iface, mut socket_set) = create_loopback(); @@ -2086,18 +2389,22 @@ mod test { }; let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Udp, + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + protocol: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64 + hop_limit: 64, }); // Emit the representations to a packet - udp_repr.emit(&mut packet_unicast, &ip_repr.src_addr(), - &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet_unicast, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); let data = packet_unicast.into_inner(); @@ -2110,9 +2417,9 @@ mod test { dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), protocol: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64 + hop_limit: 64, }, - data: &data + data: &data, }; let expected_repr = IpPacket::Icmpv4(( Ipv4Repr { @@ -2120,42 +2427,58 @@ mod test { dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), protocol: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }, - icmp_repr + icmp_repr, )); // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. 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))); + assert_eq!( + iface + .inner + .process_udp(&cx, &mut socket_set, ip_repr, false, data), + Ok(Some(expected_repr)) + ); let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64 + hop_limit: 64, }); // Emit the representations to a packet - udp_repr.emit(&mut packet_broadcast, &ip_repr.src_addr(), - &IpAddress::Ipv4(Ipv4Address::BROADCAST), - UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet_broadcast, + &ip_repr.src_addr(), + &IpAddress::Ipv4(Ipv4Address::BROADCAST), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); // 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(&cx, &mut socket_set, ip_repr, - false, packet_broadcast.into_inner()), Ok(None)); + assert_eq!( + iface.inner.process_udp( + &cx, + &mut socket_set, + ip_repr, + false, + packet_broadcast.into_inner() + ), + Ok(None) + ); } #[test] #[cfg(feature = "socket-udp")] fn test_handle_udp_broadcast() { - use crate::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; + use crate::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use crate::wire::IpEndpoint; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; @@ -2184,19 +2507,19 @@ mod test { #[cfg(feature = "proto-ipv6")] let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_ip, - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + src_addr: src_ip, + dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40 + hop_limit: 0x40, }); #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: src_ip, - dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + src_addr: src_ip, + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40 + hop_limit: 0x40, }); { @@ -2207,28 +2530,40 @@ mod test { assert!(socket.can_send()); } - udp_repr.emit(&mut packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); // Packet should be handled by bound UDP socket 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)); + assert_eq!( + iface + .inner + .process_udp(&cx, &mut socket_set, ip_repr, false, packet.into_inner()), + Ok(None) + ); { // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer let mut socket = socket_set.get::(socket_handle); assert!(socket.can_recv()); - assert_eq!(socket.recv(), Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67)))); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) + ); } } #[test] #[cfg(feature = "proto-ipv4")] fn test_handle_ipv4_broadcast() { - use crate::wire::{Ipv4Packet, Icmpv4Repr, Icmpv4Packet}; + use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; let (mut iface, mut socket_set) = create_loopback(); @@ -2238,36 +2573,40 @@ mod test { // ICMPv4 echo request let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; let icmpv4_repr = Icmpv4Repr::EchoRequest { - ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, }; // Send to IPv4 broadcast address let ipv4_repr = Ipv4Repr { - src_addr: src_ipv4_addr, - dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Icmp, - hop_limit: 64, + src_addr: src_ipv4_addr, + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Icmp, + hop_limit: 64, payload_len: icmpv4_repr.buffer_len(), }; // Emit to ip frame - let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + icmpv4_repr.buffer_len() - ]; + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default()); + &ChecksumCapabilities::default(), + ); icmpv4_repr.emit( - &mut Icmpv4Packet::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), - &ChecksumCapabilities::default()); + &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &ChecksumCapabilities::default(), + ); Ipv4Packet::new_unchecked(&bytes) }; // Expected ICMPv4 echo reply let expected_icmpv4_repr = Icmpv4Repr::EchoReply { - ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data }; + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, + }; let expected_ipv4_repr = Ipv4Repr { src_addr: our_ipv4_addr, dst_addr: src_ipv4_addr, @@ -2278,17 +2617,19 @@ mod test { let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); let cx = iface.context(Instant::from_secs(0)); - assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame), - Ok(Some(expected_packet))); + assert_eq!( + iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + Ok(Some(expected_packet)) + ); } #[test] #[cfg(feature = "socket-udp")] fn test_icmp_reply_size() { - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - use crate::wire::IPV4_MIN_MTU as MIN_MTU; #[cfg(feature = "proto-ipv6")] use crate::wire::Icmpv6DstUnreachable; + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + use crate::wire::IPV4_MIN_MTU as MIN_MTU; #[cfg(feature = "proto-ipv6")] use crate::wire::IPV6_MIN_MTU as MIN_MTU; @@ -2316,16 +2657,21 @@ mod test { }; let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), - MAX_PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + MAX_PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let ip_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, }; #[cfg(feature = "proto-ipv6")] let ip_repr = Ipv6Repr { @@ -2333,7 +2679,7 @@ mod test { dst_addr: dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, }; let payload = packet.into_inner(); @@ -2342,7 +2688,7 @@ mod test { let expected_icmp_repr = Icmpv6Repr::DstUnreachable { reason: Icmpv6DstUnreachable::PortUnreachable, header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN] + data: &payload[..MAX_PAYLOAD_LEN], }; #[cfg(feature = "proto-ipv6")] let expected_ip_repr = Ipv6Repr { @@ -2350,13 +2696,13 @@ mod test { dst_addr: src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len() + payload_len: expected_icmp_repr.buffer_len(), }; #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let expected_icmp_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::PortUnreachable, header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN] + data: &payload[..MAX_PAYLOAD_LEN], }; #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let expected_ip_repr = Ipv4Repr { @@ -2364,20 +2710,37 @@ mod test { dst_addr: src_addr, protocol: IpProtocol::Icmp, hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len() + 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); + 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(&cx, &mut socket_set, ip_repr.into(), false, payload), - Ok(Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))))); + 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(&cx, &mut socket_set, ip_repr.into(), false, payload), - Ok(Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))))); + 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 + )))) + ); } #[test] @@ -2412,19 +2775,29 @@ mod test { let cx = iface.context(Instant::from_secs(0)); // Ensure an ARP Request for us triggers an ARP Reply - 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, - source_protocol_addr: local_ip_addr, - target_hardware_addr: remote_hw_addr, - target_protocol_addr: remote_ip_addr - })))); + 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, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + }))) + ); // Ensure the address of the requestor was entered in the cache - assert_eq!(iface.inner.lookup_hardware_addr(&cx, MockTxToken, - &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); + assert_eq!( + iface.inner.lookup_hardware_addr( + &cx, + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr) + ), + Ok((remote_hw_addr, MockTxToken)) + ); } #[test] @@ -2448,7 +2821,7 @@ mod test { dst_addr: local_ip_addr.solicited_node(), next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: solicit.buffer_len() + payload_len: solicit.buffer_len(), }); let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); @@ -2457,16 +2830,18 @@ mod test { frame.set_ethertype(EthernetProtocol::Ipv6); { ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - solicit.emit(&remote_ip_addr.into(), &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked( - &mut frame.payload_mut()[ip_repr.buffer_len()..]), - &ChecksumCapabilities::default()); + solicit.emit( + &remote_ip_addr.into(), + &local_ip_addr.solicited_node().into(), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), + &ChecksumCapabilities::default(), + ); } let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr: local_ip_addr, - lladdr: Some(local_hw_addr) + lladdr: Some(local_hw_addr), }); let ipv6_expected = Ipv6Repr { @@ -2474,19 +2849,32 @@ mod test { dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: icmpv6_expected.buffer_len() + 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(&cx, &mut socket_set, frame.into_inner()), - Ok(Some(EthernetPacket::Ip(IpPacket::Icmpv6((ipv6_expected, icmpv6_expected)))))); + 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(&cx, MockTxToken, - &IpAddress::Ipv6(local_ip_addr), &IpAddress::Ipv6(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); + assert_eq!( + iface.inner.lookup_hardware_addr( + &cx, + MockTxToken, + &IpAddress::Ipv6(local_ip_addr), + &IpAddress::Ipv6(remote_ip_addr) + ), + Ok((remote_hw_addr, MockTxToken)) + ); } #[test] @@ -2519,20 +2907,29 @@ mod test { 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(&cx, &mut socket_set, frame.into_inner()), - Ok(None)); + 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(&cx, MockTxToken, - &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); + 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)) + ); } #[test] #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] fn test_icmpv4_socket() { - use crate::socket::{IcmpSocket, IcmpEndpoint, IcmpSocketBuffer, IcmpPacketMetadata}; + use crate::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; use crate::wire::Icmpv4Packet; let (iface, mut socket_set) = create_loopback(); @@ -2557,16 +2954,20 @@ mod test { // Ensure the ident we bound to and the ident of the packet are the same. let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); - let echo_repr = Icmpv4Repr::EchoRequest{ ident, seq_no, data: echo_data }; + let echo_repr = Icmpv4Repr::EchoRequest { + ident, + seq_no, + data: echo_data, + }; echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); let icmp_data = &packet.into_inner()[..]; let ipv4_repr = Ipv4Repr { - src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), - dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), - protocol: IpProtocol::Icmp, + src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), + dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), + protocol: IpProtocol::Icmp, payload_len: 24, - hop_limit: 64 + hop_limit: 64, }; let ip_repr = IpRepr::Ipv4(ipv4_repr); @@ -2577,22 +2978,34 @@ mod test { } // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening - let echo_reply = Icmpv4Repr::EchoReply{ ident, seq_no, data: echo_data }; + let echo_reply = Icmpv4Repr::EchoReply { + ident, + seq_no, + data: echo_data, + }; let ipv4_reply = Ipv4Repr { src_addr: ipv4_repr.dst_addr, dst_addr: ipv4_repr.src_addr, ..ipv4_repr }; 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))))); + assert_eq!( + iface + .inner + .process_icmpv4(&cx, &mut socket_set, ip_repr, icmp_data), + Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) + ); { let mut socket = socket_set.get::(socket_handle); assert!(socket.can_recv()); - assert_eq!(socket.recv(), - Ok((&icmp_data[..], - IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02))))); + assert_eq!( + socket.recv(), + Ok(( + &icmp_data[..], + IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) + )) + ); } } @@ -2600,15 +3013,23 @@ mod test { #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { let (mut iface, _) = create_loopback(); - let mut new_addrs = vec![IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64)]; + let mut new_addrs = vec![ + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), + ]; iface.update_ip_addrs(|addrs| { new_addrs.extend(addrs.to_vec()); *addrs = From::from(new_addrs); }); - assert!(iface.inner.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); - assert!(iface.inner.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); - assert!(!iface.inner.has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); + assert!(!iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); } #[test] @@ -2621,11 +3042,11 @@ mod test { let payload = [0x12, 0x34, 0x56, 0x78]; let ipv6_repr = Ipv6Repr { - src_addr: remote_ip_addr, - dst_addr: Ipv6Address::LOOPBACK, + src_addr: remote_ip_addr, + dst_addr: Ipv6Address::LOOPBACK, next_header: IpProtocol::HopByHop, payload_len: 12, - hop_limit: 0x40, + hop_limit: 0x40, }; let mut bytes = vec![0; 52]; @@ -2634,8 +3055,7 @@ mod test { ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); let mut offset = ipv6_repr.buffer_len(); { - let mut hbh_pkt = - Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); + let mut hbh_pkt = Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); hbh_pkt.set_header_len(0); offset += 8; @@ -2653,38 +3073,42 @@ mod test { }; let reply_icmp_repr = Icmpv6Repr::ParamProblem { - reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, + reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, pointer: 40, - header: ipv6_repr, - data: &payload[..] + header: ipv6_repr, + data: &payload[..], }; let reply_ipv6_repr = Ipv6Repr { - src_addr: Ipv6Address::LOOPBACK, - dst_addr: remote_ip_addr, + src_addr: Ipv6Address::LOOPBACK, + dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, payload_len: reply_icmp_repr.buffer_len(), - hop_limit: 0x40, + 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(&cx, &mut socket_set, &frame), - Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); + assert_eq!( + iface.inner.process_ipv6(&cx, &mut socket_set, &frame), + Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))) + ); } #[test] #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { - fn recv_igmp(mut iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + fn recv_igmp( + mut iface: &mut Interface<'_, Loopback>, + timestamp: Instant, + ) -> Vec<(Ipv4Repr, IgmpRepr)> { let caps = iface.device.capabilities(); let checksum_caps = &caps.checksum; recv_all(&mut iface, timestamp) .iter() .filter_map(|frame| { - let ipv4_packet = match caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { @@ -2692,7 +3116,7 @@ mod test { Ipv4Packet::new_checked(eth_frame.payload()).ok()? } #[cfg(feature = "medium-ip")] - Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()? + Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, }; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); @@ -2713,8 +3137,7 @@ mod test { // Join multicast groups let timestamp = Instant::now(); for group in &groups { - iface.join_multicast_group(*group, timestamp) - .unwrap(); + iface.join_multicast_group(*group, timestamp).unwrap(); } let reports = recv_igmp(&mut iface, timestamp); @@ -2722,31 +3145,32 @@ mod test { for (i, group_addr) in groups.iter().enumerate() { assert_eq!(reports[i].0.protocol, IpProtocol::Igmp); assert_eq!(reports[i].0.dst_addr, *group_addr); - assert_eq!(reports[i].1, IgmpRepr::MembershipReport { - group_addr: *group_addr, - version: IgmpVersion::Version2, - }); + assert_eq!( + reports[i].1, + IgmpRepr::MembershipReport { + group_addr: *group_addr, + version: IgmpVersion::Version2, + } + ); } // General query let timestamp = Instant::now(); const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, - 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, - 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, - 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, + 0x63, 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, ]; { // Transmit GENERAL_QUERY_BYTES into loopback let tx_token = iface.device.transmit().unwrap(); - tx_token.consume( - timestamp, GENERAL_QUERY_BYTES.len(), - |buffer| { + tx_token + .consume(timestamp, GENERAL_QUERY_BYTES.len(), |buffer| { buffer.copy_from_slice(GENERAL_QUERY_BYTES); Ok(()) - }).unwrap(); + }) + .unwrap(); } // Trigger processing until all packets received through the // loopback have been processed, including responses to @@ -2758,8 +3182,7 @@ mod test { // Leave multicast groups let timestamp = Instant::now(); for group in &groups { - iface.leave_multicast_group(*group, timestamp) - .unwrap(); + iface.leave_multicast_group(*group, timestamp).unwrap(); } let leaves = recv_igmp(&mut iface, timestamp); @@ -2774,14 +3197,18 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_no_reply() { - use crate::socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; + use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let (mut iface, mut socket_set) = create_loopback(); let packets = 1; - let rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]); + let rx_buffer = + RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = RawSocketBuffer::new( + vec![RawPacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); socket_set.add(raw_socket); @@ -2796,51 +3223,62 @@ mod test { }; let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), - PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.header_len() + PAYLOAD_LEN + payload_len: udp_repr.header_len() + PAYLOAD_LEN, }; // Emit to frame - let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN - ]; + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default()); + &ChecksumCapabilities::default(), + ); udp_repr.emit( - &mut UdpPacket::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), - PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default()); + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); Ipv4Packet::new_unchecked(&bytes) }; let cx = iface.context(Instant::from_millis(0)); - assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame), - Ok(None)); + assert_eq!( + iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + Ok(None) + ); } #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_truncated_packet() { - use crate::socket::{RawSocket, RawSocketBuffer, RawPacketMetadata}; + use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let (mut iface, mut socket_set) = create_loopback(); let packets = 1; - let rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]); + let rx_buffer = + RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = RawSocketBuffer::new( + vec![RawPacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); socket_set.add(raw_socket); @@ -2855,32 +3293,37 @@ mod test { }; let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), - PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.header_len() + PAYLOAD_LEN + payload_len: udp_repr.header_len() + PAYLOAD_LEN, }; // Emit to frame - let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN - ]; + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default()); + &ChecksumCapabilities::default(), + ); udp_repr.emit( - &mut UdpPacket::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), - PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default()); + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); Ipv4Packet::new_unchecked(&bytes) }; @@ -2897,9 +3340,11 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] fn test_raw_socket_with_udp_socket() { - use crate::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata, - RawSocket, RawSocketBuffer, RawPacketMetadata}; - use crate::wire::{IpVersion, IpEndpoint, Ipv4Packet, UdpPacket, UdpRepr}; + use crate::socket::{ + RawPacketMetadata, RawSocket, RawSocketBuffer, UdpPacketMetadata, UdpSocket, + UdpSocketBuffer, + }; + use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; @@ -2918,9 +3363,18 @@ mod test { } let packets = 1; - let raw_rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let raw_tx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]); - let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, raw_rx_buffer, raw_tx_buffer); + let raw_rx_buffer = + RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let raw_tx_buffer = RawSocketBuffer::new( + vec![RawPacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); + let raw_socket = RawSocket::new( + IpVersion::Ipv4, + IpProtocol::Udp, + raw_rx_buffer, + raw_tx_buffer, + ); socket_set.add(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); @@ -2932,44 +3386,55 @@ mod test { }; let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), - UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), - &ChecksumCapabilities::default()); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, protocol: IpProtocol::Udp, hop_limit: 64, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len() + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), }; // Emit to frame - let mut bytes = vec![0u8; - ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len() - ]; + let mut bytes = + vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default()); + &ChecksumCapabilities::default(), + ); udp_repr.emit( - &mut UdpPacket::new_unchecked( - &mut bytes[ipv4_repr.buffer_len()..]), + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), - UDP_PAYLOAD.len(), |buf| buf.copy_from_slice( &UDP_PAYLOAD), - &ChecksumCapabilities::default()); + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); Ipv4Packet::new_unchecked(&bytes) }; let cx = iface.context(Instant::from_millis(0)); - assert_eq!(iface.inner.process_ipv4(&cx, &mut socket_set, &frame), - Ok(None)); + assert_eq!( + iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + Ok(None) + ); { // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP let mut socket = socket_set.get::(udp_socket_handle); assert!(socket.can_recv()); - assert_eq!(socket.recv(), Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67)))); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) + ); } } } diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 1b4f32d95..18f0b1227 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,18 +4,18 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ +#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] +mod interface; #[cfg(feature = "medium-ethernet")] mod neighbor; mod route; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] -mod interface; -#[cfg(feature = "medium-ethernet")] -pub use self::neighbor::Neighbor as Neighbor; #[cfg(feature = "medium-ethernet")] pub(crate) use self::neighbor::Answer as NeighborAnswer; #[cfg(feature = "medium-ethernet")] pub use self::neighbor::Cache as NeighborCache; +#[cfg(feature = "medium-ethernet")] +pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 2f08017ce..64d239caf 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -3,8 +3,8 @@ use managed::ManagedMap; -use crate::wire::{EthernetAddress, IpAddress}; use crate::time::{Duration, Instant}; +use crate::wire::{EthernetAddress, IpAddress}; /// A cached neighbor. /// @@ -14,7 +14,7 @@ use crate::time::{Duration, Instant}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Neighbor { hardware_addr: EthernetAddress, - expires_at: Instant, + expires_at: Instant, } /// An answer to a neighbor cache lookup. @@ -27,7 +27,7 @@ pub(crate) enum Answer { NotFound, /// The neighbor address is not in the cache, or has expired, /// and a lookup has been made recently. - RateLimited + RateLimited, } impl Answer { @@ -61,10 +61,9 @@ impl Answer { /// ``` #[derive(Debug)] pub struct Cache<'a> { - storage: ManagedMap<'a, IpAddress, Neighbor>, + storage: ManagedMap<'a, IpAddress, Neighbor>, silent_until: Instant, - gc_threshold: usize - + gc_threshold: usize, } impl<'a> Cache<'a> { @@ -82,21 +81,32 @@ impl<'a> Cache<'a> { /// # Panics /// This function panics if `storage.len() == 0`. pub fn new(storage: T) -> Cache<'a> - where T: Into> { - + where + T: Into>, + { Cache::new_with_limit(storage, Cache::GC_THRESHOLD) } pub fn new_with_limit(storage: T, gc_threshold: usize) -> Cache<'a> - where T: Into> { + where + T: Into>, + { let mut storage = storage.into(); storage.clear(); - Cache { storage, gc_threshold, silent_until: Instant::from_millis(0) } + Cache { + storage, + gc_threshold, + silent_until: Instant::from_millis(0), + } } - pub fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress, - timestamp: Instant) { + pub fn fill( + &mut self, + protocol_addr: IpAddress, + hardware_addr: EthernetAddress, + timestamp: Instant, + ) { debug_assert!(protocol_addr.is_unicast()); debug_assert!(hardware_addr.is_unicast()); @@ -104,11 +114,12 @@ impl<'a> Cache<'a> { let current_storage_size = self.storage.len(); match self.storage { - ManagedMap::Borrowed(_) => (), + ManagedMap::Borrowed(_) => (), #[cfg(any(feature = "std", feature = "alloc"))] ManagedMap::Owned(ref mut map) => { if current_storage_size >= self.gc_threshold { - let new_btree_map = map.iter_mut() + let new_btree_map = map + .iter_mut() .map(|(key, value)| (*key, *value)) .filter(|(_, v)| timestamp < v.expires_at) .collect(); @@ -118,13 +129,18 @@ impl<'a> Cache<'a> { } }; let neighbor = Neighbor { - expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr + expires_at: timestamp + Self::ENTRY_LIFETIME, + hardware_addr, }; match self.storage.insert(protocol_addr, neighbor) { Ok(Some(old_neighbor)) => { if old_neighbor.hardware_addr != hardware_addr { - net_trace!("replaced {} => {} (was {})", - protocol_addr, hardware_addr, old_neighbor.hardware_addr); + net_trace!( + "replaced {} => {} (was {})", + protocol_addr, + hardware_addr, + old_neighbor.hardware_addr + ); } } Ok(None) => { @@ -147,21 +163,23 @@ impl<'a> Cache<'a> { } // Owned maps can extend themselves. #[cfg(any(feature = "std", feature = "alloc"))] - ManagedMap::Owned(_) => unreachable!() + ManagedMap::Owned(_) => unreachable!(), }; - let _old_neighbor = - self.storage.remove(&old_protocol_addr).unwrap(); + let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap(); match self.storage.insert(protocol_addr, neighbor) { Ok(None) => { - net_trace!("filled {} => {} (evicted {} => {})", - protocol_addr, hardware_addr, - old_protocol_addr, _old_neighbor.hardware_addr); + net_trace!( + "filled {} => {} (evicted {} => {})", + protocol_addr, + hardware_addr, + old_protocol_addr, + _old_neighbor.hardware_addr + ); } // We've covered everything else above. - _ => unreachable!() + _ => unreachable!(), } - } } } @@ -171,10 +189,13 @@ impl<'a> Cache<'a> { return Answer::Found(EthernetAddress::BROADCAST); } - if let Some(&Neighbor { expires_at, hardware_addr }) = - self.storage.get(protocol_addr) { + if let Some(&Neighbor { + expires_at, + hardware_addr, + }) = self.storage.get(protocol_addr) + { if timestamp < expires_at { - return Answer::Found(hardware_addr) + return Answer::Found(hardware_addr); } } @@ -193,9 +214,8 @@ impl<'a> Cache<'a> { #[cfg(test)] mod test { use super::*; - use std::collections::BTreeMap; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4}; - + use std::collections::BTreeMap; const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]); const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]); @@ -207,17 +227,47 @@ mod test { let mut cache_storage = [Default::default(); 3]; let mut cache = Cache::new(&mut cache_storage[..]); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) + .found(), + false + ); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found(), + false + ); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), - false); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::Found(HADDR_A) + ); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found(), + false + ); + assert_eq!( + cache + .lookup( + &MOCK_IP_ADDR_1, + Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 + ) + .found(), + false + ); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found(), + false + ); } #[test] @@ -226,9 +276,19 @@ mod test { let mut cache = Cache::new(&mut cache_storage[..]); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), - false); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::Found(HADDR_A) + ); + assert_eq!( + cache + .lookup( + &MOCK_IP_ADDR_1, + Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 + ) + .found(), + false + ); } #[test] @@ -237,9 +297,15 @@ mod test { let mut cache = Cache::new(&mut cache_storage[..]); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A)); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::Found(HADDR_A) + ); cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B)); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::Found(HADDR_B) + ); } #[test] @@ -249,10 +315,20 @@ mod test { cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); // Adding third item after the expiration of the previous // two should garbage collect - cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2); + cache.fill( + MOCK_IP_ADDR_3, + HADDR_C, + Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2, + ); assert_eq!(cache.storage.len(), 1); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C)); + assert_eq!( + cache.lookup( + &MOCK_IP_ADDR_3, + Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2 + ), + Answer::Found(HADDR_C) + ); } #[test] @@ -263,12 +339,28 @@ mod test { cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100)); cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), + Answer::Found(HADDR_B) + ); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)) + .found(), + false + ); cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D)); + assert_eq!( + cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)) + .found(), + false + ); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), + Answer::Found(HADDR_D) + ); } #[test] @@ -276,10 +368,19 @@ mod test { let mut cache_storage = [Default::default(); 3]; let mut cache = Cache::new(&mut cache_storage[..]); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::NotFound + ); cache.limit_rate(Instant::from_millis(0)); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited); - assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), + Answer::RateLimited + ); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), + Answer::NotFound + ); } } diff --git a/src/iface/route.rs b/src/iface/route.rs index e37c6a129..a6704cc23 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -1,13 +1,13 @@ -use managed::ManagedMap; use crate::time::Instant; use core::ops::Bound; +use managed::ManagedMap; -use crate::{Error, Result}; -use crate::wire::{IpCidr, IpAddress}; +use crate::wire::{IpAddress, IpCidr}; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr}; +use crate::{Error, Result}; /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] @@ -70,7 +70,9 @@ impl<'a> Routes<'a> { /// Creates a routing tables. The backing storage is **not** cleared /// upon creation. pub fn new(storage: T) -> Routes<'a> - where T: Into> { + where + T: Into>, + { let storage = storage.into(); Routes { storage } } @@ -89,7 +91,7 @@ impl<'a> Routes<'a> { let route = Route::new_ipv4_gateway(gateway); match self.storage.insert(cidr, route) { Ok(route) => Ok(route), - Err((_cidr, _route)) => Err(Error::Exhausted) + Err((_cidr, _route)) => Err(Error::Exhausted), } } @@ -102,7 +104,7 @@ impl<'a> Routes<'a> { let route = Route::new_ipv6_gateway(gateway); match self.storage.insert(cidr, route) { Ok(route) => Ok(route), - Err((_cidr, _route)) => Err(Error::Exhausted) + Err((_cidr, _route)) => Err(Error::Exhausted), } } @@ -124,8 +126,7 @@ impl<'a> Routes<'a> { self.storage.remove(&cidr) } - pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> - Option { + pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option { assert!(addr.is_unicast()); let cidr = match addr { @@ -133,10 +134,14 @@ impl<'a> Routes<'a> { IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)), #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)), - _ => unimplemented!() + _ => unimplemented!(), }; - for (prefix, route) in self.storage.range((Bound::Unbounded::, Bound::Included(cidr))).rev() { + for (prefix, route) in self + .storage + .range((Bound::Unbounded::, Bound::Included(cidr))) + .rev() + { // TODO: do something with route.preferred_until if let Some(expires_at) = route.expires_at { if timestamp > expires_at { @@ -159,24 +164,28 @@ mod test { #[cfg(feature = "proto-ipv6")] mod mock { use super::super::*; - pub const ADDR_1A: Ipv6Address = Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]); - pub const ADDR_1B: Ipv6Address = Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]); - pub const ADDR_1C: Ipv6Address = Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]); + pub const ADDR_1A: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]); + pub const ADDR_1B: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]); + pub const ADDR_1C: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]); pub fn cidr_1() -> Ipv6Cidr { - Ipv6Cidr::new(Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64) + Ipv6Cidr::new( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), + 64, + ) } - pub const ADDR_2A: Ipv6Address = Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]); - pub const ADDR_2B: Ipv6Address = Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]); + pub const ADDR_2A: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]); + pub const ADDR_2B: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]); pub fn cidr_2() -> Ipv6Cidr { - Ipv6Cidr::new(Ipv6Address( - [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64) + Ipv6Cidr::new( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), + 64, + ) } } @@ -204,25 +213,56 @@ mod test { let mut routes_storage = [None, None, None]; let mut routes = Routes::new(&mut routes_storage[..]); - assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None); - assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None); - assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None); - assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None); - assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None); + assert_eq!( + routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), + None + ); + assert_eq!( + routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), + None + ); + assert_eq!( + routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), + None + ); + assert_eq!( + routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), + None + ); + assert_eq!( + routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), + None + ); let route = Route { via_router: ADDR_1A.into(), - preferred_until: None, expires_at: None, + preferred_until: None, + expires_at: None, }; routes.update(|storage| { storage.insert(cidr_1().into(), route).unwrap(); }); - assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None); - assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None); + assert_eq!( + routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), + None + ); + assert_eq!( + routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), + None + ); let route2 = Route { via_router: ADDR_2A.into(), @@ -233,16 +273,46 @@ mod test { storage.insert(cidr_2().into(), route2).unwrap(); }); - assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into())); - assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into())); - - assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); - assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into())); - assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into())); + assert_eq!( + routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), + Some(ADDR_2A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), + Some(ADDR_2A.into()) + ); + + assert_eq!( + routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), + Some(ADDR_1A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), + Some(ADDR_2A.into()) + ); + assert_eq!( + routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), + Some(ADDR_2A.into()) + ); } } diff --git a/src/lib.rs b/src/lib.rs index 4c8e93fba..c0fd0fac5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,12 @@ #![cfg_attr(not(any(test, feature = "std")), no_std)] #![deny(unsafe_code)] -#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "medium-ethernet"), deny(unused))] +#![cfg_attr( + all( + any(feature = "proto-ipv4", feature = "proto-ipv6"), + feature = "medium-ethernet" + ), + deny(unused) +)] //! The _smoltcp_ library is built in a layered structure, with the layers corresponding //! to the levels of API abstraction. Only the highest layers would be used by a typical @@ -107,10 +113,7 @@ compile_error!("If you enable the socket feature, you must enable at least one o #[cfg(all( feature = "socket", - not(any( - feature = "medium-ethernet", - feature = "medium-ip", - )) + not(any(feature = "medium-ethernet", feature = "medium-ip",)) ))] compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet"); @@ -123,13 +126,13 @@ use core::fmt; mod macros; mod parsers; -pub mod storage; -pub mod phy; -pub mod wire; pub mod iface; +pub mod phy; #[cfg(feature = "socket")] pub mod socket; +pub mod storage; pub mod time; +pub mod wire; /// The error type for the networking stack. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -178,16 +181,16 @@ pub type Result = core::result::Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::Exhausted => write!(f, "buffer space exhausted"), - Error::Illegal => write!(f, "illegal operation"), + Error::Exhausted => write!(f, "buffer space exhausted"), + Error::Illegal => write!(f, "illegal operation"), Error::Unaddressable => write!(f, "unaddressable destination"), - Error::Finished => write!(f, "operation finished"), - Error::Truncated => write!(f, "truncated packet"), - Error::Checksum => write!(f, "checksum error"), - Error::Unrecognized => write!(f, "unrecognized packet"), - Error::Fragmented => write!(f, "fragmented packet"), - Error::Malformed => write!(f, "malformed packet"), - Error::Dropped => write!(f, "dropped by socket"), + Error::Finished => write!(f, "operation finished"), + Error::Truncated => write!(f, "truncated packet"), + Error::Checksum => write!(f, "checksum error"), + Error::Unrecognized => write!(f, "unrecognized packet"), + Error::Fragmented => write!(f, "fragmented packet"), + Error::Malformed => write!(f, "malformed packet"), + Error::Dropped => write!(f, "dropped by socket"), } } } diff --git a/src/macros.rs b/src/macros.rs index 28e49ee25..3dc8305e0 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,3 @@ - #[cfg(feature = "log")] macro_rules! net_log { (trace, $($arg:expr),*) => { log::trace!($($arg),*); }; diff --git a/src/parsers.rs b/src/parsers.rs index 136e58b80..bc6175ee0 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -1,7 +1,10 @@ -#![cfg_attr(not(all(feature = "proto-ipv6", feature = "proto-ipv4")), allow(dead_code))] +#![cfg_attr( + not(all(feature = "proto-ipv6", feature = "proto-ipv4")), + allow(dead_code) +)] -use core::str::FromStr; use core::result; +use core::str::FromStr; #[cfg(feature = "medium-ethernet")] use crate::wire::EthernetAddress; @@ -15,14 +18,14 @@ type Result = result::Result; struct Parser<'a> { data: &'a [u8], - pos: usize + pos: usize, } impl<'a> Parser<'a> { fn new(data: &'a str) -> Parser<'a> { Parser { data: data.as_bytes(), - pos: 0 + pos: 0, } } @@ -40,12 +43,14 @@ impl<'a> Parser<'a> { self.pos += 1; Ok(chr) } - None => Err(()) + None => Err(()), } } fn try_do(&mut self, f: F) -> Option - where F: FnOnce(&mut Parser<'a>) -> Result { + where + F: FnOnce(&mut Parser<'a>) -> Result, + { let pos = self.pos; match f(self) { Ok(res) => Some(res), @@ -65,7 +70,9 @@ impl<'a> Parser<'a> { } fn until_eof(&mut self, f: F) -> Result - where F: FnOnce(&mut Parser<'a>) -> Result { + where + F: FnOnce(&mut Parser<'a>) -> Result, + { let res = f(self)?; self.accept_eof()?; Ok(res) @@ -99,8 +106,7 @@ impl<'a> Parser<'a> { } } - fn accept_number(&mut self, max_digits: usize, max_value: u32, - hex: bool) -> Result { + fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result { let mut value = self.accept_digit(hex)? as u32; for _ in 1..max_digits { match self.try_do(|p| p.accept_digit(hex)) { @@ -108,7 +114,7 @@ impl<'a> Parser<'a> { value *= if hex { 16 } else { 10 }; value += digit as u32; } - None => break + None => break, } } if value < max_value { @@ -133,10 +139,10 @@ impl<'a> Parser<'a> { #[cfg(feature = "medium-ethernet")] fn accept_mac(&mut self) -> Result { if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) { - return Ok(mac) + return Ok(mac); } if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) { - return Ok(mac) + return Ok(mac); } Err(()) } @@ -154,9 +160,13 @@ impl<'a> Parser<'a> { } #[cfg(feature = "proto-ipv6")] - fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]), - (head_idx, tail_idx): (&mut usize, &mut usize), - mut use_tail: bool, is_cidr: bool) -> Result<()> { + fn accept_ipv6_part( + &mut self, + (head, tail): (&mut [u16; 8], &mut [u16; 6]), + (head_idx, tail_idx): (&mut usize, &mut usize), + mut use_tail: bool, + is_cidr: bool, + ) -> Result<()> { let double_colon = match self.try_do(|p| p.accept_str(b"::")) { Some(_) if !use_tail && *head_idx < 7 => { // Found a double colon. Start filling out the @@ -164,7 +174,7 @@ impl<'a> Parser<'a> { // this is the last character we can parse. use_tail = true; true - }, + } Some(_) => { // This is a bad address. Only one double colon is // allowed and an address is only 128 bits. @@ -193,21 +203,20 @@ impl<'a> Parser<'a> { }); } Ok(()) - }, + } Some(part) if *tail_idx < 6 => { // Valid u16 to be added to the address tail[*tail_idx] = part as u16; *tail_idx += 1; - if *tail_idx == 1 && tail[0] == 0xffff - && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] { + if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] { self.try_do(|p| { p.accept_char(b':')?; p.accept_ipv4_mapped_ipv6_part(tail, tail_idx) }); } Ok(()) - }, + } Some(_) => { // Tail or head section is too long Err(()) @@ -259,7 +268,12 @@ impl<'a> Parser<'a> { let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]); let (mut head_idx, mut tail_idx) = (0, 0); - self.accept_ipv6_part((&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, is_cidr)?; + self.accept_ipv6_part( + (&mut addr, &mut tail), + (&mut head_idx, &mut tail_idx), + false, + is_cidr, + )?; // We need to copy the tail portion (the portion following the "::") to the // end of the address. @@ -290,14 +304,14 @@ impl<'a> Parser<'a> { #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv4()) { Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)), - None => () + None => (), } #[cfg(feature = "proto-ipv6")] #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv6(false)) { Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)), - None => () + None => (), } Err(()) @@ -314,7 +328,10 @@ impl<'a> Parser<'a> { self.accept_number(5, 65535, false)? }; - Ok(IpEndpoint { addr: IpAddress::Ipv4(ip), port: port as u16 }) + Ok(IpEndpoint { + addr: IpAddress::Ipv4(ip), + port: port as u16, + }) } #[cfg(feature = "proto-ipv6")] @@ -326,10 +343,16 @@ impl<'a> Parser<'a> { self.accept_char(b':')?; let port = self.accept_number(5, 65535, false)?; - Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: port as u16 }) + Ok(IpEndpoint { + addr: IpAddress::Ipv6(ip), + port: port as u16, + }) } else { let ip = self.accept_ipv6(false)?; - Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0 }) + Ok(IpEndpoint { + addr: IpAddress::Ipv6(ip), + port: 0, + }) } } @@ -338,14 +361,14 @@ impl<'a> Parser<'a> { #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv4_endpoint()) { Some(ipv4) => return Ok(ipv4), - None => () + None => (), } #[cfg(feature = "proto-ipv6")] #[allow(clippy::single_match)] match self.try_do(|p| p.accept_ipv6_endpoint()) { Some(ipv6) => return Ok(ipv6), - None => () + None => (), } Err(()) @@ -431,14 +454,14 @@ impl FromStr for IpCidr { #[allow(clippy::single_match)] match Ipv4Cidr::from_str(s) { Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)), - Err(_) => () + Err(_) => (), } #[cfg(feature = "proto-ipv6")] #[allow(clippy::single_match)] match Ipv6Cidr::from_str(s) { Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)), - Err(_) => () + Err(_) => (), } Err(()) @@ -465,29 +488,40 @@ mod test { if let Ok(cidr) = cidr { assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr)); - assert_eq!(IpCidr::from_str(&format!("{}", cidr)), - Ok($variant(cidr))); + assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr))); } } - } + }; } #[test] #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] fn test_mac() { assert_eq!(EthernetAddress::from_str(""), Err(())); - assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"), - Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))); - assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"), - Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))); - assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"), - Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))); - assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"), - Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))); - assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"), - Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))); - assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"), - Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))); + assert_eq!( + EthernetAddress::from_str("02:00:00:00:00:00"), + Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00])) + ); + assert_eq!( + EthernetAddress::from_str("01:23:45:67:89:ab"), + Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])) + ); + assert_eq!( + EthernetAddress::from_str("cd:ef:10:00:00:00"), + Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00])) + ); + assert_eq!( + EthernetAddress::from_str("00:00:00:ab:cd:ef"), + Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])) + ); + assert_eq!( + EthernetAddress::from_str("00-00-00-ab-cd-ef"), + Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])) + ); + assert_eq!( + EthernetAddress::from_str("AB-CD-EF-00-00-00"), + Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00])) + ); assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(())); assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(())); assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(())); @@ -498,10 +532,14 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_ipv4() { assert_eq!(Ipv4Address::from_str(""), Err(())); - assert_eq!(Ipv4Address::from_str("1.2.3.4"), - Ok(Ipv4Address([1, 2, 3, 4]))); - assert_eq!(Ipv4Address::from_str("001.2.3.4"), - Ok(Ipv4Address([1, 2, 3, 4]))); + assert_eq!( + Ipv4Address::from_str("1.2.3.4"), + Ok(Ipv4Address([1, 2, 3, 4])) + ); + assert_eq!( + Ipv4Address::from_str("001.2.3.4"), + Ok(Ipv4Address([1, 2, 3, 4])) + ); assert_eq!(Ipv4Address::from_str("0001.2.3.4"), Err(())); assert_eq!(Ipv4Address::from_str("999.2.3.4"), Err(())); assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(())); @@ -515,73 +553,87 @@ mod test { fn test_ipv6() { // Obviously not valid assert_eq!(Ipv6Address::from_str(""), Err(())); - assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"), - Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))); - assert_eq!(Ipv6Address::from_str("::1"), - Ok(Ipv6Address::LOOPBACK)); - assert_eq!(Ipv6Address::from_str("::"), - Ok(Ipv6Address::UNSPECIFIED)); - assert_eq!(Ipv6Address::from_str("fe80::1"), - Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))); - assert_eq!(Ipv6Address::from_str("1234:5678::"), - Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))); - assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"), - Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))); + assert_eq!( + Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"), + Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)) + ); + assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK)); + assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED)); + assert_eq!( + Ipv6Address::from_str("fe80::1"), + Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)) + ); + assert_eq!( + Ipv6Address::from_str("1234:5678::"), + Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0)) + ); + assert_eq!( + Ipv6Address::from_str("1234:5678::8765:4321"), + Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321)) + ); // Two double colons in address - assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), - Err(())); - assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"), - Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4))); - assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1::"), - Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0))); - assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"), - Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1))); - assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), - Err(())); + assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(())); + assert_eq!( + Ipv6Address::from_str("4444:333:22:1::4"), + Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4)) + ); + assert_eq!( + Ipv6Address::from_str("1:1:1:1:1:1::"), + Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0)) + ); + assert_eq!( + Ipv6Address::from_str("::1:1:1:1:1:1"), + Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1)) + ); + assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(())); // Double colon appears too late indicating an address that is too long - assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), - Err(())); + assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(())); // Section after double colon is too long for a valid address - assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), - Err(())); + assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(())); // Obviously too long - assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), - Err(())); + assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(())); // Address is too short - assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), - Err(())); + assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(())); // Long number - assert_eq!(Ipv6Address::from_str("::000001"), - Err(())); + assert_eq!(Ipv6Address::from_str("::000001"), Err(())); // IPv4-Mapped address - assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"), - Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]))); - assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"), - Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]))); - assert_eq!(Ipv6Address::from_str("0::ffff:192.168.1.1"), - Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]))); + assert_eq!( + Ipv6Address::from_str("::ffff:192.168.1.1"), + Ok(Ipv6Address([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1 + ])) + ); + assert_eq!( + Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"), + Ok(Ipv6Address([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1 + ])) + ); + assert_eq!( + Ipv6Address::from_str("0::ffff:192.168.1.1"), + Ok(Ipv6Address([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1 + ])) + ); // Only ffff is allowed in position 6 when IPv4 mapped - assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), - Err(())); + assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(())); // Positions 1-5 must be 0 when IPv4 mapped - assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), - Err(())); - assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), - Err(())); + assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(())); + assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(())); // Out of range ipv4 octet - assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), - Err(())); + assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(())); // Invalid hex in ipv4 octet - assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), - Err(())); + assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(())); } #[test] #[cfg(feature = "proto-ipv4")] fn test_ip_ipv4() { assert_eq!(IpAddress::from_str(""), Err(())); - assert_eq!(IpAddress::from_str("1.2.3.4"), - Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))); + assert_eq!( + IpAddress::from_str("1.2.3.4"), + Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))) + ); assert_eq!(IpAddress::from_str("x"), Err(())); } @@ -589,8 +641,12 @@ mod test { #[cfg(feature = "proto-ipv6")] fn test_ip_ipv6() { assert_eq!(IpAddress::from_str(""), Err(())); - assert_eq!(IpAddress::from_str("fe80::1"), - Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)))); + assert_eq!( + IpAddress::from_str("fe80::1"), + Ok(IpAddress::Ipv6(Ipv6Address::new( + 0xfe80, 0, 0, 0, 0, 0, 0, 1 + ))) + ); assert_eq!(IpAddress::from_str("x"), Err(())); } @@ -598,14 +654,22 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_cidr_ipv4() { let tests = [ - ("127.0.0.1/8", - Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))), - ("192.168.1.1/24", - Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))), - ("8.8.8.8/32", - Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))), - ("8.8.8.8/0", - Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))), + ( + "127.0.0.1/8", + Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)), + ), + ( + "192.168.1.1/24", + Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)), + ), + ( + "8.8.8.8/32", + Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8)), + ), + ( + "8.8.8.8/0", + Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8)), + ), ("", Err(())), ("1", Err(())), ("127.0.0.1", Err(())), @@ -622,22 +686,32 @@ mod test { #[cfg(feature = "proto-ipv6")] fn test_cidr_ipv6() { let tests = [ - ("fe80::1/64", - Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))), - ("fe80::/64", - Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))), - ("::1/128", - Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))), - ("::/128", - Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))), - ("fe80:0:0:0:0:0:0:1/64", - Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))), - ("fe80:0:0:0:0:0:0:1|64", - Err(())), - ("fe80::|64", - Err(())), - ("fe80::1::/64", - Err(())) + ( + "fe80::1/64", + Ok(Ipv6Cidr::new( + Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), + 64u8, + )), + ), + ( + "fe80::/64", + Ok(Ipv6Cidr::new( + Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), + 64u8, + )), + ), + ("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))), + ("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))), + ( + "fe80:0:0:0:0:0:0:1/64", + Ok(Ipv6Cidr::new( + Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), + 64u8, + )), + ), + ("fe80:0:0:0:0:0:0:1|64", Err(())), + ("fe80::|64", Err(())), + ("fe80::1::/64", Err(())), ]; check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6); } @@ -649,11 +723,17 @@ mod test { assert_eq!(IpEndpoint::from_str("x"), Err(())); assert_eq!( IpEndpoint::from_str("127.0.0.1"), - Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 0 }) + Ok(IpEndpoint { + addr: IpAddress::v4(127, 0, 0, 1), + port: 0 + }) ); assert_eq!( IpEndpoint::from_str("127.0.0.1:12345"), - Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 12345 }) + Ok(IpEndpoint { + addr: IpAddress::v4(127, 0, 0, 1), + port: 12345 + }) ); } @@ -664,11 +744,17 @@ mod test { assert_eq!(IpEndpoint::from_str("x"), Err(())); assert_eq!( IpEndpoint::from_str("fe80::1"), - Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 0 }) + Ok(IpEndpoint { + addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), + port: 0 + }) ); assert_eq!( IpEndpoint::from_str("[fe80::1]:12345"), - Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 12345 }) + Ok(IpEndpoint { + addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), + port: 12345 + }) ); } } diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 8c2e4aa27..49cc7ca6c 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; -use crate::{Error, Result}; -use crate::phy::{self, DeviceCapabilities, Device}; +use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::{Duration, Instant}; +use crate::{Error, Result}; // We use our own RNG to stay compatible with #![no_std]. // The use of the RNG below has a slight bias, but it doesn't matter. @@ -22,21 +22,21 @@ const MTU: usize = 1536; #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Config { corrupt_pct: u8, - drop_pct: u8, + drop_pct: u8, reorder_pct: u8, - max_size: usize, + max_size: usize, max_tx_rate: u64, max_rx_rate: u64, - interval: Duration, + interval: Duration, } #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct State { - rng_seed: u32, + rng_seed: u32, refilled_at: Instant, - tx_bucket: u64, - rx_bucket: u64, + tx_bucket: u64, + rx_bucket: u64, } impl State { @@ -48,7 +48,7 @@ impl State { let buffer = buffer.as_mut(); // We introduce a single bitflip, as the most likely, and the hardest to detect, error. let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len(); - let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8; + let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8; buffer[index] ^= bit; } @@ -61,7 +61,9 @@ impl State { } fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool { - if config.max_tx_rate == 0 { return true } + if config.max_tx_rate == 0 { + return true; + } self.refill(config, timestamp); if self.tx_bucket > 0 { @@ -73,7 +75,9 @@ impl State { } fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool { - if config.max_rx_rate == 0 { return true } + if config.max_rx_rate == 0 { + return true; + } self.refill(config, timestamp); if self.rx_bucket > 0 { @@ -92,19 +96,19 @@ impl State { /// or hardware limitations (such as a limited number or size of usable network buffers). #[derive(Debug)] pub struct FaultInjector Device<'a>> { - inner: D, - state: RefCell, - config: Config, + inner: D, + state: RefCell, + config: Config, } impl Device<'a>> FaultInjector { /// Create a fault injector device, using the given random number generator seed. pub fn new(inner: D, seed: u32) -> FaultInjector { let state = State { - rng_seed: seed, + rng_seed: seed, refilled_at: Instant::from_millis(0), - tx_bucket: 0, - rx_bucket: 0, + tx_bucket: 0, + rx_bucket: 0, }; FaultInjector { inner: inner, @@ -153,7 +157,9 @@ impl Device<'a>> FaultInjector { /// # Panics /// This function panics if the probability is not between 0% and 100%. pub fn set_corrupt_chance(&mut self, pct: u8) { - if pct > 100 { panic!("percentage out of range") } + if pct > 100 { + panic!("percentage out of range") + } self.config.corrupt_pct = pct } @@ -162,7 +168,9 @@ impl Device<'a>> FaultInjector { /// # Panics /// This function panics if the probability is not between 0% and 100%. pub fn set_drop_chance(&mut self, pct: u8) { - if pct > 100 { panic!("percentage out of range") } + if pct > 100 { + panic!("percentage out of range") + } self.config.drop_pct = pct } @@ -189,7 +197,8 @@ impl Device<'a>> FaultInjector { } impl<'a, D> Device<'a> for FaultInjector - where D: for<'b> Device<'b>, +where + D: for<'b> Device<'b>, { type RxToken = RxToken<'a, >::RxToken>; type TxToken = TxToken<'a, >::TxToken>; @@ -203,60 +212,78 @@ impl<'a, D> Device<'a> for FaultInjector } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let &mut Self { ref mut inner, ref state, config } = self; + let &mut Self { + ref mut inner, + ref state, + config, + } = self; inner.receive().map(|(rx_token, tx_token)| { let rx = RxToken { - state: &state, - config: config, - token: rx_token, + state: &state, + config: config, + token: rx_token, corrupt: [0; MTU], }; let tx = TxToken { - state: &state, - config: config, - token: tx_token, - junk: [0; MTU], + state: &state, + config: config, + token: tx_token, + junk: [0; MTU], }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { - let &mut Self { ref mut inner, ref state, config } = self; + let &mut Self { + ref mut inner, + ref state, + config, + } = self; inner.transmit().map(|token| TxToken { - state: &state, + state: &state, config: config, token: token, - junk: [0; MTU], + junk: [0; MTU], }) } } #[doc(hidden)] pub struct RxToken<'a, Rx: phy::RxToken> { - state: &'a RefCell, - config: Config, - token: Rx, + state: &'a RefCell, + config: Config, + token: Rx, corrupt: [u8; MTU], } impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("rx: randomly dropping a packet"); - return Err(Error::Exhausted) + return Err(Error::Exhausted); } - if !self.state.borrow_mut().maybe_receive(&self.config, timestamp) { + if !self + .state + .borrow_mut() + .maybe_receive(&self.config, timestamp) + { net_trace!("rx: dropping a packet because of rate limiting"); - return Err(Error::Exhausted) + return Err(Error::Exhausted); } - let Self { token, config, state, mut corrupt } = self; + let Self { + token, + config, + state, + mut corrupt, + } = self; token.consume(timestamp, |buffer| { if config.max_size > 0 && buffer.as_ref().len() > config.max_size { net_trace!("rx: dropping a packet that is too large"); - return Err(Error::Exhausted) + return Err(Error::Exhausted); } if state.borrow_mut().maybe(config.corrupt_pct) { net_trace!("rx: randomly corrupting a packet"); @@ -273,15 +300,16 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { #[doc(hidden)] pub struct TxToken<'a, Tx: phy::TxToken> { - state: &'a RefCell, + state: &'a RefCell, config: Config, - token: Tx, - junk: [u8; MTU], + token: Tx, + junk: [u8; MTU], } impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { fn consume(mut self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) { net_trace!("tx: randomly dropping a packet"); @@ -289,7 +317,11 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { } else if self.config.max_size > 0 && len > self.config.max_size { net_trace!("tx: dropping a packet that is too large"); true - } else if !self.state.borrow_mut().maybe_transmit(&self.config, timestamp) { + } else if !self + .state + .borrow_mut() + .maybe_transmit(&self.config, timestamp) + { net_trace!("tx: dropping a packet because of rate limiting"); true } else { @@ -300,7 +332,12 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { return f(&mut self.junk[..len]); } - let Self { token, state, config, .. } = self; + let Self { + token, + state, + config, + .. + } = self; token.consume(timestamp, len, |mut buf| { if state.borrow_mut().maybe(config.corrupt_pct) { net_trace!("tx: corrupting a packet"); diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 5e2023b7a..b22b91220 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -1,6 +1,6 @@ -use crate::Result; -use crate::phy::{self, DeviceCapabilities, Device}; +use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::Instant; +use crate::Result; // This could be fixed once associated consts are stable. const MTU: usize = 1536; @@ -20,7 +20,7 @@ pub trait Fuzzer { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FuzzInjector Device<'a>, FTx: Fuzzer, FRx: Fuzzer> { - inner: D, + inner: D, fuzz_tx: FTx, fuzz_rx: FRx, } @@ -29,7 +29,11 @@ pub struct FuzzInjector Device<'a>, FTx: Fuzzer, FRx: Fuzzer> { impl Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector { /// Create a fuzz injector device. pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector { - FuzzInjector { inner, fuzz_tx, fuzz_rx } + FuzzInjector { + inner, + fuzz_tx, + fuzz_rx, + } } /// Return the underlying device, consuming the fuzz injector. @@ -39,9 +43,10 @@ impl Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector } impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector - where D: for<'b> Device<'b>, - FTx: Fuzzer + 'a, - FRx: Fuzzer + 'a +where + D: for<'b> Device<'b>, + FTx: Fuzzer + 'a, + FRx: Fuzzer + 'a, { type RxToken = RxToken<'a, >::RxToken, FRx>; type TxToken = TxToken<'a, >::TxToken, FTx>; @@ -55,38 +60,47 @@ impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let &mut Self { ref mut inner, ref fuzz_rx, ref fuzz_tx } = self; + let &mut Self { + ref mut inner, + ref fuzz_rx, + ref fuzz_tx, + } = self; inner.receive().map(|(rx_token, tx_token)| { let rx = RxToken { fuzzer: fuzz_rx, - token: rx_token, + token: rx_token, }; let tx = TxToken { fuzzer: fuzz_tx, - token: tx_token, + token: tx_token, }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { - let &mut Self { ref mut inner, fuzz_rx: _, ref fuzz_tx } = self; + let &mut Self { + ref mut inner, + fuzz_rx: _, + ref fuzz_tx, + } = self; inner.transmit().map(|token| TxToken { fuzzer: fuzz_tx, - token: token, + token: token, }) } } #[doc(hidden)] -pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{ +pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a> { fuzzer: &'a F, - token: Rx, + token: Rx, } impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let Self { fuzzer, token } = self; token.consume(timestamp, |buffer| { @@ -99,12 +113,13 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { #[doc(hidden)] pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> { fuzzer: &'a F, - token: Tx, + token: Tx, } impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let Self { fuzzer, token } = self; token.consume(timestamp, len, |mut buf| { diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index c44c97ce7..9b39aed33 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -1,12 +1,12 @@ -use alloc::vec::Vec; #[cfg(not(feature = "rust-1_28"))] use alloc::collections::VecDeque; +use alloc::vec::Vec; #[cfg(feature = "rust-1_28")] use alloc::VecDeque; -use crate::Result; use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; +use crate::Result; /// A loopback device. #[derive(Debug)] @@ -44,7 +44,9 @@ impl<'a> Device<'a> for Loopback { fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { self.queue.pop_front().map(move |buffer| { let rx = RxToken { buffer }; - let tx = TxToken { queue: &mut self.queue }; + let tx = TxToken { + queue: &mut self.queue, + }; (rx, tx) }) } @@ -63,7 +65,8 @@ pub struct RxToken { impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { f(&mut self.buffer) } @@ -76,7 +79,8 @@ pub struct TxToken<'a> { impl<'a> phy::TxToken for TxToken<'a> { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let mut buffer = Vec::new(); buffer.resize(len, 0); diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 50cd774ff..c156d58b9 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -11,7 +11,9 @@ and implementations of it: [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames on the host OS. */ -#![cfg_attr(feature = "medium-ethernet", doc = r##" +#![cfg_attr( + feature = "medium-ethernet", + doc = r##" # Examples An implementation of the [Device](trait.Device.html) trait for a simple hardware @@ -84,37 +86,50 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { } } ``` -"##)] +"## +)] -use crate::Result; use crate::time::Instant; +use crate::Result; -#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))] +#[cfg(all( + any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), + unix +))] mod sys; -mod tracer; mod fault_injector; mod fuzz_injector; -mod pcap_writer; #[cfg(any(feature = "std", feature = "alloc"))] mod loopback; +mod pcap_writer; #[cfg(all(feature = "phy-raw_socket", unix))] mod raw_socket; -#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +mod tracer; +#[cfg(all( + feature = "phy-tuntap_interface", + any(target_os = "linux", target_os = "android") +))] mod tuntap_interface; -#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))] +#[cfg(all( + any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), + unix +))] pub use self::sys::wait; -pub use self::tracer::Tracer; pub use self::fault_injector::FaultInjector; -pub use self::fuzz_injector::{Fuzzer, FuzzInjector}; -pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; +pub use self::fuzz_injector::{FuzzInjector, Fuzzer}; #[cfg(any(feature = "std", feature = "alloc"))] pub use self::loopback::Loopback; +pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; #[cfg(all(feature = "phy-raw_socket", unix))] pub use self::raw_socket::RawSocket; -#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub use self::tracer::Tracer; +#[cfg(all( + feature = "phy-tuntap_interface", + any(target_os = "linux", target_os = "android") +))] pub use self::tuntap_interface::TunTapInterface; /// A description of checksum behavior for a particular protocol. @@ -142,7 +157,7 @@ impl Checksum { pub fn rx(&self) -> bool { match *self { Checksum::Both | Checksum::Rx => true, - _ => false + _ => false, } } @@ -150,7 +165,7 @@ impl Checksum { pub fn tx(&self) -> bool { match *self { Checksum::Both | Checksum::Tx => true, - _ => false + _ => false, } } } @@ -234,7 +249,9 @@ 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(), + Medium::Ethernet => { + self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len() + } #[cfg(feature = "medium-ip")] Medium::Ip => self.max_transmission_unit, } @@ -260,7 +277,6 @@ pub enum Medium { Ip, } - impl Default for Medium { fn default() -> Medium { #[cfg(feature = "medium-ethernet")] @@ -306,7 +322,8 @@ pub trait RxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result; + where + F: FnOnce(&mut [u8]) -> Result; } /// A token to transmit a single network packet. @@ -321,5 +338,6 @@ pub trait TxToken { /// The timestamp must be a number of milliseconds, monotonically increasing since an /// arbitrary moment in time, such as system startup. fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result; + where + F: FnOnce(&mut [u8]) -> Result; } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index a2226bbd4..2834e9398 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -1,13 +1,13 @@ +use byteorder::{ByteOrder, NativeEndian}; +use phy::Medium; #[cfg(feature = "std")] use std::cell::RefCell; #[cfg(feature = "std")] use std::io::Write; -use byteorder::{ByteOrder, NativeEndian}; -use phy::Medium; -use crate::Result; -use crate::phy::{self, DeviceCapabilities, Device}; +use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::Instant; +use crate::Result; enum_with_unknown! { /// Captured packet header type. @@ -28,7 +28,7 @@ pub enum PcapMode { /// Capture only received packets. RxOnly, /// Capture only transmitted packets. - TxOnly + TxOnly, } /// A packet capture sink. @@ -54,12 +54,12 @@ pub trait PcapSink { /// /// This method may be overridden e.g. if special synchronization is necessary. fn global_header(&self, link_type: PcapLinkType) { - self.write_u32(0xa1b2c3d4); // magic number - self.write_u16(2); // major version - self.write_u16(4); // minor version - self.write_u32(0); // timezone (= UTC) - self.write_u32(0); // accuracy (not used) - self.write_u32(65535); // maximum packet length + self.write_u32(0xa1b2c3d4); // magic number + self.write_u16(2); // major version + self.write_u16(4); // minor version + self.write_u32(0); // timezone (= UTC) + self.write_u32(0); // accuracy (not used) + self.write_u32(65535); // maximum packet length self.write_u32(link_type.into()); // link-layer header type } @@ -72,10 +72,10 @@ pub trait PcapSink { fn packet_header(&self, timestamp: Instant, length: usize) { assert!(length <= 65535); - self.write_u32(timestamp.secs() as u32); // timestamp seconds - self.write_u32(timestamp.millis() as u32); // timestamp microseconds - self.write_u32(length as u32); // captured length - self.write_u32(length as u32); // original length + self.write_u32(timestamp.secs() as u32); // timestamp seconds + self.write_u32(timestamp.millis() as u32); // timestamp microseconds + self.write_u32(length as u32); // captured length + self.write_u32(length as u32); // original length } /// Write the libpcap packet header followed by packet data into the sink. @@ -121,12 +121,13 @@ impl PcapSink for RefCell { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PcapWriter - where D: for<'a> Device<'a>, - S: PcapSink + Clone, +where + D: for<'a> Device<'a>, + S: PcapSink + Clone, { lower: D, - sink: S, - mode: PcapMode, + sink: S, + mode: PcapMode, } impl Device<'a>, S: PcapSink + Clone> PcapWriter { @@ -145,27 +146,49 @@ impl Device<'a>, S: PcapSink + Clone> PcapWriter { } impl<'a, D, S> Device<'a> for PcapWriter - where D: for<'b> Device<'b>, - S: PcapSink + Clone + 'a, +where + D: for<'b> Device<'b>, + S: PcapSink + Clone + 'a, { type RxToken = RxToken<>::RxToken, S>; type TxToken = TxToken<>::TxToken, S>; - fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() } + fn capabilities(&self) -> DeviceCapabilities { + self.lower.capabilities() + } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let &mut Self { ref mut lower, ref sink, mode, .. } = self; + let &mut Self { + ref mut lower, + ref sink, + mode, + .. + } = self; lower.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { token: rx_token, sink: sink.clone(), mode }; - let tx = TxToken { token: tx_token, sink: sink.clone(), mode }; + let rx = RxToken { + token: rx_token, + sink: sink.clone(), + mode, + }; + let tx = TxToken { + token: tx_token, + sink: sink.clone(), + mode, + }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { - let &mut Self { ref mut lower, ref sink, mode } = self; - lower.transmit().map(|token| { - TxToken { token, sink: sink.clone(), mode } + let &mut Self { + ref mut lower, + ref sink, + mode, + } = self; + lower.transmit().map(|token| TxToken { + token, + sink: sink.clone(), + mode, }) } } @@ -173,8 +196,8 @@ impl<'a, D, S> Device<'a> for PcapWriter #[doc(hidden)] pub struct RxToken { token: Rx, - sink: S, - mode: PcapMode, + sink: S, + mode: PcapMode, } impl phy::RxToken for RxToken { @@ -182,9 +205,8 @@ impl phy::RxToken for RxToken { let Self { token, sink, mode } = self; token.consume(timestamp, |buffer| { match mode { - PcapMode::Both | PcapMode::RxOnly => - sink.packet(timestamp, buffer.as_ref()), - PcapMode::TxOnly => () + PcapMode::Both | PcapMode::RxOnly => sink.packet(timestamp, buffer.as_ref()), + PcapMode::TxOnly => (), } f(buffer) }) @@ -194,21 +216,21 @@ impl phy::RxToken for RxToken { #[doc(hidden)] pub struct TxToken { token: Tx, - sink: S, - mode: PcapMode + sink: S, + mode: PcapMode, } impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let Self { token, sink, mode } = self; token.consume(timestamp, len, |buffer| { let result = f(buffer); match mode { - PcapMode::Both | PcapMode::TxOnly => - sink.packet(timestamp, &buffer), - PcapMode::RxOnly => () + PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, &buffer), + PcapMode::RxOnly => (), }; result }) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index b4d4e68fa..4c12635f5 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -1,18 +1,18 @@ use std::cell::RefCell; -use std::vec::Vec; -use std::rc::Rc; use std::io; -use std::os::unix::io::{RawFd, AsRawFd}; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; +use std::vec::Vec; -use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; +use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; use crate::time::Instant; +use crate::Result; /// A socket that captures or transmits the complete frame. #[derive(Debug)] pub struct RawSocket { - lower: Rc>, - mtu: usize + lower: Rc>, + mtu: usize, } impl AsRawFd for RawSocket { @@ -32,7 +32,7 @@ impl RawSocket { let mtu = lower.interface_mtu()?; Ok(RawSocket { lower: Rc::new(RefCell::new(lower)), - mtu: mtu + mtu: mtu, }) } } @@ -56,13 +56,13 @@ impl<'a> Device<'a> for RawSocket { Ok(size) => { buffer.resize(size, 0); let rx = RxToken { buffer }; - let tx = TxToken { lower: self.lower.clone() }; + let tx = TxToken { + lower: self.lower.clone(), + }; Some((rx, tx)) } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - None - } - Err(err) => panic!("{}", err) + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None, + Err(err) => panic!("{}", err), } } @@ -75,12 +75,13 @@ impl<'a> Device<'a> for RawSocket { #[doc(hidden)] pub struct RxToken { - buffer: Vec + buffer: Vec, } impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { f(&mut self.buffer[..]) } @@ -88,12 +89,13 @@ impl phy::RxToken for RxToken { #[doc(hidden)] pub struct TxToken { - lower: Rc>, + lower: Rc>, } impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; diff --git a/src/phy/sys/bpf.rs b/src/phy/sys/bpf.rs index 0c1c519f4..4999f16f8 100644 --- a/src/phy/sys/bpf.rs +++ b/src/phy/sys/bpf.rs @@ -4,8 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use libc; -use crate::wire::ETHERNET_HEADER_LEN; use super::{ifreq, ifreq_for}; +use crate::wire::ETHERNET_HEADER_LEN; /// set interface #[cfg(any(target_os = "macos", target_os = "openbsd"))] diff --git a/src/phy/sys/linux.rs b/src/phy/sys/linux.rs index d42d83fc1..58d819c9b 100644 --- a/src/phy/sys/linux.rs +++ b/src/phy/sys/linux.rs @@ -1,10 +1,10 @@ #![allow(unused)] -pub const SIOCGIFMTU: libc::c_ulong = 0x8921; +pub const SIOCGIFMTU: libc::c_ulong = 0x8921; pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; -pub const ETH_P_ALL: libc::c_short = 0x0003; +pub const ETH_P_ALL: libc::c_short = 0x0003; -pub const TUNSETIFF: libc::c_ulong = 0x400454CA; -pub const IFF_TUN: libc::c_int = 0x0001; -pub const IFF_TAP: libc::c_int = 0x0002; -pub const IFF_NO_PI: libc::c_int = 0x1000; +pub const TUNSETIFF: libc::c_ulong = 0x400454CA; +pub const IFF_TUN: libc::c_int = 0x0001; +pub const IFF_TAP: libc::c_int = 0x0002; +pub const IFF_NO_PI: libc::c_int = 0x1000; diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 8021879a6..3f42301c5 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -1,25 +1,45 @@ #![allow(unsafe_code)] -use std::{mem, ptr, io}; -use std::os::unix::io::RawFd; use crate::time::Duration; +use std::os::unix::io::RawFd; +use std::{io, mem, ptr}; #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "linux.rs"] mod imp; -#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] -pub mod raw_socket; -#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] +#[cfg(all( + feature = "phy-raw_socket", + not(any(target_os = "linux", target_os = "android")), + unix +))] pub mod bpf; -#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +#[cfg(all( + feature = "phy-raw_socket", + any(target_os = "linux", target_os = "android") +))] +pub mod raw_socket; +#[cfg(all( + feature = "phy-tuntap_interface", + any(target_os = "linux", target_os = "android") +))] pub mod tuntap_interface; -#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] -pub use self::raw_socket::RawSocketDesc; -#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] +#[cfg(all( + feature = "phy-raw_socket", + not(any(target_os = "linux", target_os = "android")), + unix +))] pub use self::bpf::BpfDevice as RawSocketDesc; -#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +#[cfg(all( + feature = "phy-raw_socket", + any(target_os = "linux", target_os = "android") +))] +pub use self::raw_socket::RawSocketDesc; +#[cfg(all( + feature = "phy-tuntap_interface", + any(target_os = "linux", target_os = "android") +))] pub use self::tuntap_interface::TunTapInterfaceDesc; /// Wait until given file descriptor becomes readable, but no longer than given timeout. @@ -44,35 +64,51 @@ pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { exceptfds.assume_init() }; - let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 }; - let timeout_ptr = - if let Some(duration) = duration { - timeout.tv_sec = duration.secs() as libc::time_t; - timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t; - &mut timeout as *mut _ - } else { - ptr::null_mut() - }; + let mut timeout = libc::timeval { + tv_sec: 0, + tv_usec: 0, + }; + let timeout_ptr = if let Some(duration) = duration { + timeout.tv_sec = duration.secs() as libc::time_t; + timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t; + &mut timeout as *mut _ + } else { + ptr::null_mut() + }; - let res = libc::select(fd + 1, &mut readfds, &mut writefds, &mut exceptfds, timeout_ptr); - if res == -1 { return Err(io::Error::last_os_error()) } + let res = libc::select( + fd + 1, + &mut readfds, + &mut writefds, + &mut exceptfds, + timeout_ptr, + ); + if res == -1 { + return Err(io::Error::last_os_error()); + } Ok(()) } } -#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))] +#[cfg(all( + any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), + unix +))] #[repr(C)] #[derive(Debug)] struct ifreq { ifr_name: [libc::c_char; libc::IF_NAMESIZE], - ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */ + ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ } -#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))] +#[cfg(all( + any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), + unix +))] fn ifreq_for(name: &str) -> ifreq { let mut ifreq = ifreq { ifr_name: [0; libc::IF_NAMESIZE], - ifr_data: 0 + ifr_data: 0, }; for (i, byte) in name.as_bytes().iter().enumerate() { ifreq.ifr_name[i] = *byte as libc::c_char @@ -80,13 +116,20 @@ fn ifreq_for(name: &str) -> ifreq { ifreq } -#[cfg(all(any(target_os = "linux", target_os = "android"), - any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))] -fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, - cmd: libc::c_ulong) -> io::Result { +#[cfg(all( + any(target_os = "linux", target_os = "android"), + any(feature = "phy-tuntap_interface", feature = "phy-raw_socket") +))] +fn ifreq_ioctl( + lower: libc::c_int, + ifreq: &mut ifreq, + cmd: libc::c_ulong, +) -> io::Result { unsafe { let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq); - if res == -1 { return Err(io::Error::last_os_error()) } + if res == -1 { + return Err(io::Error::last_os_error()); + } } Ok(ifreq.ifr_data) diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index f45662c1c..8d28b82b4 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -1,12 +1,12 @@ -use std::{mem, io}; -use std::os::unix::io::{RawFd, AsRawFd}; use super::*; use crate::wire::EthernetFrame; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::{io, mem}; #[derive(Debug)] pub struct RawSocketDesc { lower: libc::c_int, - ifreq: ifreq + ifreq: ifreq, } impl AsRawFd for RawSocketDesc { @@ -18,41 +18,51 @@ impl AsRawFd for RawSocketDesc { impl RawSocketDesc { pub fn new(name: &str) -> io::Result { let lower = unsafe { - let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK, - imp::ETH_P_ALL.to_be() as i32); - if lower == -1 { return Err(io::Error::last_os_error()) } + let lower = libc::socket( + libc::AF_PACKET, + libc::SOCK_RAW | libc::SOCK_NONBLOCK, + imp::ETH_P_ALL.to_be() as i32, + ); + if lower == -1 { + return Err(io::Error::last_os_error()); + } lower }; Ok(RawSocketDesc { lower: lower, - ifreq: ifreq_for(name) + ifreq: ifreq_for(name), }) } pub fn interface_mtu(&mut self) -> io::Result { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. - let ip_mtu = ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?; + let ip_mtu = + ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?; Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len()) } pub fn bind_interface(&mut self) -> io::Result<()> { let sockaddr = libc::sockaddr_ll { - sll_family: libc::AF_PACKET as u16, + sll_family: libc::AF_PACKET as u16, sll_protocol: imp::ETH_P_ALL.to_be() as u16, - sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?, - sll_hatype: 1, - sll_pkttype: 0, - sll_halen: 6, - sll_addr: [0; 8] + sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?, + sll_hatype: 1, + sll_pkttype: 0, + sll_halen: 6, + sll_addr: [0; 8], }; unsafe { - let res = libc::bind(self.lower, - &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr, - mem::size_of::() as u32); - if res == -1 { return Err(io::Error::last_os_error()) } + let res = libc::bind( + self.lower, + &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr, + mem::size_of::() as u32, + ); + if res == -1 { + return Err(io::Error::last_os_error()); + } } Ok(()) @@ -60,18 +70,30 @@ impl RawSocketDesc { pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { unsafe { - let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void, - buffer.len(), 0); - if len == -1 { return Err(io::Error::last_os_error()) } + let len = libc::recv( + self.lower, + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + 0, + ); + if len == -1 { + return Err(io::Error::last_os_error()); + } Ok(len as usize) } } pub fn send(&mut self, buffer: &[u8]) -> io::Result { unsafe { - let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void, - buffer.len(), 0); - if len == -1 { Err(io::Error::last_os_error()).unwrap() } + let len = libc::send( + self.lower, + buffer.as_ptr() as *const libc::c_void, + buffer.len(), + 0, + ); + if len == -1 { + Err(io::Error::last_os_error()).unwrap() + } Ok(len as usize) } } @@ -79,6 +101,8 @@ impl RawSocketDesc { impl Drop for RawSocketDesc { fn drop(&mut self) { - unsafe { libc::close(self.lower); } + unsafe { + libc::close(self.lower); + } } } diff --git a/src/phy/sys/tuntap_interface.rs b/src/phy/sys/tuntap_interface.rs index 6b42980b1..54b9c3647 100644 --- a/src/phy/sys/tuntap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -1,7 +1,7 @@ -use std::io; -use std::os::unix::io::{RawFd, AsRawFd}; use super::*; use crate::{phy::Medium, wire::EthernetFrame}; +use std::io; +use std::os::unix::io::{AsRawFd, RawFd}; #[derive(Debug)] pub struct TunTapInterfaceDesc { @@ -19,9 +19,13 @@ impl AsRawFd for TunTapInterfaceDesc { impl TunTapInterfaceDesc { pub fn new(name: &str, medium: Medium) -> io::Result { let lower = unsafe { - let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char, - libc::O_RDWR | libc::O_NONBLOCK); - if lower == -1 { return Err(io::Error::last_os_error()) } + let lower = libc::open( + "/dev/net/tun\0".as_ptr() as *const libc::c_char, + libc::O_RDWR | libc::O_NONBLOCK, + ); + if lower == -1 { + return Err(io::Error::last_os_error()); + } lower }; @@ -46,13 +50,17 @@ impl TunTapInterfaceDesc { pub fn interface_mtu(&mut self) -> io::Result { let lower = unsafe { let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); - if lower == -1 { return Err(io::Error::last_os_error()) } + if lower == -1 { + return Err(io::Error::last_os_error()); + } lower }; let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize); - unsafe { libc::close(lower); } + unsafe { + libc::close(lower); + } // Propagate error after close, to ensure we always close. let ip_mtu = ip_mtu?; @@ -71,18 +79,28 @@ impl TunTapInterfaceDesc { pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { unsafe { - let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void, - buffer.len()); - if len == -1 { return Err(io::Error::last_os_error()) } + let len = libc::read( + self.lower, + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + ); + if len == -1 { + return Err(io::Error::last_os_error()); + } Ok(len as usize) } } pub fn send(&mut self, buffer: &[u8]) -> io::Result { unsafe { - let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void, - buffer.len()); - if len == -1 { Err(io::Error::last_os_error()).unwrap() } + let len = libc::write( + self.lower, + buffer.as_ptr() as *const libc::c_void, + buffer.len(), + ); + if len == -1 { + Err(io::Error::last_os_error()).unwrap() + } Ok(len as usize) } } @@ -90,6 +108,8 @@ impl TunTapInterfaceDesc { impl Drop for TunTapInterfaceDesc { fn drop(&mut self) { - unsafe { libc::close(self.lower); } + unsafe { + libc::close(self.lower); + } } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 8a5e377bd..d5b899146 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -1,8 +1,11 @@ use core::fmt; -use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}}; -use crate::phy::{self, DeviceCapabilities, Device, Medium}; +use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; +use crate::{ + wire::pretty_print::{PrettyIndent, PrettyPrint}, + Result, +}; /// A tracer device. /// @@ -10,7 +13,7 @@ use crate::time::Instant; /// using the provided writer function, and then passes them to another /// device. pub struct Tracer Device<'a>> { - inner: D, + inner: D, writer: fn(Instant, Packet), } @@ -42,50 +45,78 @@ impl Device<'a>> Tracer { } impl<'a, D> Device<'a> for Tracer - where D: for<'b> Device<'b>, +where + D: for<'b> Device<'b>, { type RxToken = RxToken<>::RxToken>; type TxToken = TxToken<>::TxToken>; - fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() } + fn capabilities(&self) -> DeviceCapabilities { + self.inner.capabilities() + } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let &mut Self { ref mut inner, writer, .. } = self; + let &mut Self { + ref mut inner, + writer, + .. + } = self; let medium = inner.capabilities().medium; inner.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { token: rx_token, writer, medium }; - let tx = TxToken { token: tx_token, writer, medium }; + let rx = RxToken { + token: rx_token, + writer, + medium, + }; + let tx = TxToken { + token: tx_token, + writer, + medium, + }; (rx, tx) }) } fn transmit(&'a mut self) -> Option { - let &mut Self { ref mut inner, writer } = self; + let &mut Self { + ref mut inner, + writer, + } = self; let medium = inner.capabilities().medium; - inner.transmit().map(|tx_token| { - TxToken { token: tx_token, medium, writer } + inner.transmit().map(|tx_token| TxToken { + token: tx_token, + medium, + writer, }) } } #[doc(hidden)] pub struct RxToken { - token: Rx, - writer: fn(Instant, Packet), - medium: Medium, + token: Rx, + writer: fn(Instant, Packet), + medium: Medium, } impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { - let Self { token, writer, medium } = self; + let Self { + token, + writer, + medium, + } = self; token.consume(timestamp, |buffer| { - writer(timestamp, Packet{ - buffer, - medium, - prefix: "<- ", - }); + writer( + timestamp, + Packet { + buffer, + medium, + prefix: "<- ", + }, + ); f(buffer) }) } @@ -93,23 +124,31 @@ impl phy::RxToken for RxToken { #[doc(hidden)] pub struct TxToken { - token: Tx, - writer: fn(Instant, Packet), - medium: Medium, + token: Tx, + writer: fn(Instant, Packet), + medium: Medium, } impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { - let Self { token, writer, medium } = self; + let Self { + token, + writer, + medium, + } = self; token.consume(timestamp, len, |buffer| { let result = f(buffer); - writer(timestamp, Packet{ - buffer, - medium, - prefix: "-> ", - }); + writer( + timestamp, + Packet { + buffer, + medium, + prefix: "-> ", + }, + ); result }) } @@ -126,15 +165,31 @@ impl<'a> fmt::Display for Packet<'a> { let mut indent = PrettyIndent::new(self.prefix); match self.medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), + Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print( + &self.buffer, + f, + &mut indent, + ), #[cfg(feature = "medium-ip")] Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) { #[cfg(feature = "proto-ipv4")] - Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), + Ok(crate::wire::IpVersion::Ipv4) => { + crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print( + &self.buffer, + f, + &mut indent, + ) + } #[cfg(feature = "proto-ipv6")] - Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), - _ => f.write_str("unrecognized IP version") - } + Ok(crate::wire::IpVersion::Ipv6) => { + crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print( + &self.buffer, + f, + &mut indent, + ) + } + _ => f.write_str("unrecognized IP version"), + }, } } -} \ No newline at end of file +} diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index cfd4f2d92..6792a7be5 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -1,18 +1,18 @@ use std::cell::RefCell; -use std::vec::Vec; -use std::rc::Rc; use std::io; -use std::os::unix::io::{RawFd, AsRawFd}; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::rc::Rc; +use std::vec::Vec; -use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; +use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; use crate::time::Instant; +use crate::Result; /// A virtual TUN (IP) or TAP (Ethernet) interface. #[derive(Debug)] pub struct TunTapInterface { - lower: Rc>, - mtu: usize, + lower: Rc>, + mtu: usize, medium: Medium, } @@ -59,13 +59,13 @@ impl<'a> Device<'a> for TunTapInterface { Ok(size) => { buffer.resize(size, 0); let rx = RxToken { buffer }; - let tx = TxToken { lower: self.lower.clone() }; + let tx = TxToken { + lower: self.lower.clone(), + }; Some((rx, tx)) } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - None - } - Err(err) => panic!("{}", err) + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None, + Err(err) => panic!("{}", err), } } @@ -78,12 +78,13 @@ impl<'a> Device<'a> for TunTapInterface { #[doc(hidden)] pub struct RxToken { - buffer: Vec + buffer: Vec, } impl phy::RxToken for RxToken { fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { f(&mut self.buffer[..]) } @@ -96,7 +97,8 @@ pub struct TxToken { impl phy::TxToken for TxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + where + F: FnOnce(&mut [u8]) -> Result, { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 7caa16e85..97711e6f1 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -1,12 +1,12 @@ -use crate::{Error, Result}; -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, Context}; -use crate::time::{Instant, Duration}; use crate::socket::SocketHandle; +use crate::socket::{Context, SocketMeta}; +use crate::time::{Duration, Instant}; +use crate::wire::dhcpv4::field as dhcpv4_field; +use crate::wire::{ + DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr, + UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, +}; +use crate::{Error, Result}; use super::{PollAt, Socket}; @@ -31,7 +31,7 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[ #[derive(Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { - /// IP address + /// IP address pub address: Ipv4Cidr, /// Router address, also known as default gateway. Does not necessarily /// match the DHCP server's address. @@ -133,7 +133,7 @@ impl Dhcpv4Socket { pub fn new() -> Self { Dhcpv4Socket { meta: SocketMeta::default(), - state: ClientState::Discovering(DiscoverState{ + state: ClientState::Discovering(DiscoverState { retry_at: Instant::from_millis(0), }), config_changed: true, @@ -159,7 +159,13 @@ impl Dhcpv4Socket { PollAt::Time(t) } - pub(crate) fn process(&mut self, cx: &Context, 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,25 +185,37 @@ impl Dhcpv4Socket { return Ok(()); } }; - if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { return Ok(()) } - if dhcp_repr.transaction_id != self.transaction_id { 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, None => { - net_debug!("DHCP ignoring {:?} because missing server_identifier", dhcp_repr.message_type); + net_debug!( + "DHCP ignoring {:?} because missing server_identifier", + dhcp_repr.message_type + ); return Ok(()); } }; - net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); - - match (&mut self.state, dhcp_repr.message_type){ + net_debug!( + "DHCP recv {:?} from {} ({})", + dhcp_repr.message_type, + src_ip, + server_identifier + ); + + match (&mut self.state, dhcp_repr.message_type) { (ClientState::Discovering(_state), DhcpMessageType::Offer) => { if !dhcp_repr.your_ip.is_unicast() { net_debug!("DHCP ignoring OFFER because your_ip is not unicast"); - return Ok(()) + return Ok(()); } - + self.state = ClientState::Requesting(RequestState { retry_at: cx.now, retry: 0, @@ -205,13 +223,15 @@ impl Dhcpv4Socket { address: src_ip, identifier: server_identifier, }, - requested_ip: dhcp_repr.your_ip // use the offered ip + requested_ip: dhcp_repr.your_ip, // use the offered ip }); } (ClientState::Requesting(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.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{ + self.state = ClientState::Renewing(RenewState { server: state.server, config, renew_at, @@ -223,7 +243,9 @@ impl Dhcpv4Socket { self.reset(); } (ClientState::Renewing(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.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 { @@ -236,19 +258,26 @@ impl Dhcpv4Socket { self.reset(); } _ => { - net_debug!("DHCP ignoring {:?}: unexpected in current state", dhcp_repr.message_type); + net_debug!( + "DHCP ignoring {:?}: unexpected in current state", + dhcp_repr.message_type + ); } } Ok(()) } - fn parse_ack(now: Instant, dhcp_repr: &DhcpRepr, max_lease_duration: Option) -> Option<(Config, Instant, Instant)> { + fn parse_ack( + now: Instant, + dhcp_repr: &DhcpRepr, + max_lease_duration: Option, + ) -> Option<(Config, Instant, Instant)> { let subnet_mask = match dhcp_repr.subnet_mask { Some(subnet_mask) => subnet_mask, None => { net_debug!("DHCP ignoring ACK because missing subnet_mask"); - return None + return None; } }; @@ -256,16 +285,19 @@ impl Dhcpv4Socket { Some(prefix_len) => prefix_len, None => { net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask"); - return None + return None; } }; if !dhcp_repr.your_ip.is_unicast() { net_debug!("DHCP ignoring ACK because your_ip is not unicast"); - return None + return None; } - let mut lease_duration = dhcp_repr.lease_duration.map(|d| Duration::from_secs(d as _)).unwrap_or(DEFAULT_LEASE_DURATION); + let mut lease_duration = dhcp_repr + .lease_duration + .map(|d| Duration::from_secs(d as _)) + .unwrap_or(DEFAULT_LEASE_DURATION); if let Some(max_lease_duration) = max_lease_duration { lease_duration = lease_duration.min(max_lease_duration); } @@ -276,7 +308,7 @@ impl Dhcpv4Socket { if let Some(received) = dhcp_repr.dns_servers { let mut i = 0; for addr in received.iter() { - if let Some(addr) = addr{ + if let Some(addr) = addr { if addr.is_unicast() { // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT dns_servers[i] = Some(*addr); @@ -285,10 +317,10 @@ impl Dhcpv4Socket { } } } - let config = Config{ + let config = Config { address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), router: dhcp_repr.router, - dns_servers: dns_servers + dns_servers: dns_servers, }; // RFC 2131 indicates clients should renew a lease halfway through its expiration. @@ -299,9 +331,10 @@ impl Dhcpv4Socket { } pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> - where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> { - - // note: Dhcpv4Socket is only usable in ethernet mediums, so the + 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(); @@ -337,7 +370,7 @@ impl Dhcpv4Socket { src_port: DHCP_CLIENT_PORT, dst_port: DHCP_SERVER_PORT, }; - + let mut ipv4_repr = Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: Ipv4Address::BROADCAST, @@ -349,11 +382,15 @@ impl Dhcpv4Socket { match &mut self.state { ClientState::Discovering(state) => { if cx.now < state.retry_at { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } // send packet - net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); + net_debug!( + "DHCP send DISCOVER to {}: {:?}", + ipv4_repr.dst_addr, + dhcp_repr + ); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); emit((ipv4_repr, udp_repr, dhcp_repr))?; @@ -364,14 +401,14 @@ impl Dhcpv4Socket { } ClientState::Requesting(state) => { if cx.now < state.retry_at { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } if state.retry >= REQUEST_RETRIES { net_debug!("DHCP request retries exceeded, restarting discovery"); self.reset(); // return Ok so we get polled again - return Ok(()) + return Ok(()); } dhcp_repr.message_type = DhcpMessageType::Request; @@ -379,7 +416,11 @@ impl Dhcpv4Socket { dhcp_repr.requested_ip = Some(state.requested_ip); dhcp_repr.server_identifier = Some(state.server.identifier); - net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); + net_debug!( + "DHCP send request to {}: {:?}", + ipv4_repr.dst_addr, + dhcp_repr + ); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); emit((ipv4_repr, udp_repr, dhcp_repr))?; @@ -395,11 +436,11 @@ impl Dhcpv4Socket { net_debug!("DHCP lease expired"); self.reset(); // return Ok so we get polled again - return Ok(()) + return Ok(()); } - + if cx.now < state.renew_at { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } ipv4_repr.src_addr = state.config.address.address(); @@ -411,7 +452,7 @@ impl Dhcpv4Socket { net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); emit((ipv4_repr, udp_repr, dhcp_repr))?; - + // In both RENEWING and REBINDING states, if the client receives no // response to its DHCPREQUEST message, the client SHOULD wait one-half // of the remaining time until T2 (in RENEWING state) and one-half of @@ -440,7 +481,7 @@ impl Dhcpv4Socket { if let ClientState::Renewing(_) = &self.state { self.config_changed = true; } - self.state = ClientState::Discovering(DiscoverState{ + self.state = ClientState::Discovering(DiscoverState { retry_at: Instant::from_millis(0), }); } diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 39c8b5888..90f2516f3 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -2,20 +2,20 @@ use core::cmp; #[cfg(feature = "async")] use core::task::Waker; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; -use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context}; -use crate::storage::{PacketBuffer, PacketMetadata}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; +use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::storage::{PacketBuffer, PacketMetadata}; +use crate::{Error, Result}; +use crate::wire::IcmpRepr; #[cfg(feature = "proto-ipv4")] -use crate::wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr}; +use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr}; -use crate::wire::IcmpRepr; -use crate::wire::{UdpPacket, UdpRepr}; +use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Address, Ipv6Repr}; use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; +use crate::wire::{UdpPacket, UdpRepr}; /// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for /// more details. @@ -26,7 +26,7 @@ use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; pub enum Endpoint { Unspecified, Ident(u16), - Udp(IpEndpoint) + Udp(IpEndpoint), } impl Endpoint { @@ -34,13 +34,15 @@ impl Endpoint { match *self { Endpoint::Ident(_) => true, Endpoint::Udp(endpoint) => endpoint.port != 0, - Endpoint::Unspecified => false + Endpoint::Unspecified => false, } } } impl Default for Endpoint { - fn default() -> Endpoint { Endpoint::Unspecified } + fn default() -> Endpoint { + Endpoint::Unspecified + } } /// An ICMP packet metadata. @@ -64,7 +66,7 @@ pub struct IcmpSocket<'a> { rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>, /// The endpoint this socket is communicating with - endpoint: Endpoint, + endpoint: Endpoint, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. hop_limit: Option, #[cfg(feature = "async")] @@ -75,13 +77,12 @@ pub struct IcmpSocket<'a> { impl<'a> IcmpSocket<'a> { /// Create an ICMP socket with the given buffers. - pub fn new(rx_buffer: IcmpSocketBuffer<'a>, - tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> { + pub fn new(rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> { IcmpSocket { - meta: SocketMeta::default(), + meta: SocketMeta::default(), rx_buffer: rx_buffer, tx_buffer: tx_buffer, - endpoint: Endpoint::default(), + endpoint: Endpoint::default(), hop_limit: None, #[cfg(feature = "async")] rx_waker: WakerRegistration::new(), @@ -219,7 +220,9 @@ impl<'a> IcmpSocket<'a> { return Err(Error::Unaddressable); } - if self.is_open() { return Err(Error::Illegal) } + if self.is_open() { + return Err(Error::Illegal); + } self.endpoint = endpoint; @@ -282,13 +285,17 @@ impl<'a> IcmpSocket<'a> { /// size, and `Err(Error::Unaddressable)` if the remote address is unspecified. pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8]> { if endpoint.is_unspecified() { - return Err(Error::Unaddressable) + return Err(Error::Unaddressable); } let packet_buf = self.tx_buffer.enqueue(size, endpoint)?; - net_trace!("{}:{}: buffer to send {} octets", - self.meta.handle, endpoint, size); + net_trace!( + "{}:{}: buffer to send {} octets", + self.meta.handle, + endpoint, + size + ); Ok(packet_buf) } @@ -308,8 +315,12 @@ impl<'a> IcmpSocket<'a> { pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> { let (endpoint, packet_buf) = self.rx_buffer.dequeue()?; - net_trace!("{}:{}: receive {} buffered octets", - self.meta.handle, endpoint, packet_buf.len()); + net_trace!( + "{}:{}: receive {} buffered octets", + self.meta.handle, + endpoint, + packet_buf.len() + ); Ok((packet_buf, endpoint)) } @@ -332,19 +343,33 @@ impl<'a> IcmpSocket<'a> { // accept Destination Unreachable messages with the data containing // a UDP packet send from the local port we are bound to. #[cfg(feature = "proto-ipv4")] - (&Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. })) - if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => { + ( + &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(), &cx.caps.checksum) { + match UdpRepr::parse( + &packet, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &cx.caps.checksum, + ) { Ok(repr) => endpoint.port == repr.src_port, Err(_) => false, } } #[cfg(feature = "proto-ipv6")] - (&Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. })) - if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => { + ( + &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(), &cx.caps.checksum) { + match UdpRepr::parse( + &packet, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &cx.caps.checksum, + ) { Ok(repr) => endpoint.port == repr.src_port, Err(_) => false, } @@ -353,44 +378,70 @@ impl<'a> IcmpSocket<'a> { // Echo Request/Reply with the identifier field matching the endpoint // port. #[cfg(feature = "proto-ipv4")] - (&Endpoint::Ident(bound_ident), - &IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. })) | - (&Endpoint::Ident(bound_ident), - &IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. })) => - ident == bound_ident, + ( + &Endpoint::Ident(bound_ident), + &IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. }), + ) + | ( + &Endpoint::Ident(bound_ident), + &IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. }), + ) => ident == bound_ident, #[cfg(feature = "proto-ipv6")] - (&Endpoint::Ident(bound_ident), - &IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. })) | - (&Endpoint::Ident(bound_ident), - &IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. })) => - ident == bound_ident, + ( + &Endpoint::Ident(bound_ident), + &IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. }), + ) + | ( + &Endpoint::Ident(bound_ident), + &IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. }), + ) => ident == bound_ident, _ => false, } } - pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> 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) => { - let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), - ip_repr.src_addr())?; - icmp_repr.emit(&mut Icmpv4Packet::new_unchecked(packet_buf), - &ChecksumCapabilities::default()); - - net_trace!("{}:{}: receiving {} octets", - self.meta.handle, icmp_repr.buffer_len(), packet_buf.len()); - }, + let packet_buf = self + .rx_buffer + .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + icmp_repr.emit( + &mut Icmpv4Packet::new_unchecked(packet_buf), + &ChecksumCapabilities::default(), + ); + + net_trace!( + "{}:{}: receiving {} octets", + self.meta.handle, + icmp_repr.buffer_len(), + packet_buf.len() + ); + } #[cfg(feature = "proto-ipv6")] IcmpRepr::Ipv6(ref icmp_repr) => { - let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), - ip_repr.src_addr())?; - icmp_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(packet_buf), - &ChecksumCapabilities::default()); - - net_trace!("{}:{}: receiving {} octets", - self.meta.handle, icmp_repr.buffer_len(), packet_buf.len()); - }, + let packet_buf = self + .rx_buffer + .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + icmp_repr.emit( + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &mut Icmpv6Packet::new_unchecked(packet_buf), + &ChecksumCapabilities::default(), + ); + + net_trace!( + "{}:{}: receiving {} octets", + self.meta.handle, + icmp_repr.buffer_len(), + packet_buf.len() + ); + } } #[cfg(feature = "async")] @@ -400,42 +451,52 @@ impl<'a> IcmpSocket<'a> { } pub(crate) fn dispatch(&mut self, _cx: &Context, emit: F) -> Result<()> - where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()> + where + F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>, { - let handle = self.meta.handle; + let handle = self.meta.handle; let hop_limit = self.hop_limit.unwrap_or(64); self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { - net_trace!("{}:{}: sending {} octets", - handle, remote_endpoint, packet_buf.len()); + net_trace!( + "{}:{}: sending {} octets", + handle, + remote_endpoint, + packet_buf.len() + ); match *remote_endpoint { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(ipv4_addr) => { let packet = Icmpv4Packet::new_unchecked(&*packet_buf); let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: ipv4_addr, - protocol: IpProtocol::Icmp, + src_addr: Ipv4Address::default(), + dst_addr: ipv4_addr, + protocol: IpProtocol::Icmp, payload_len: repr.buffer_len(), - hop_limit: hop_limit, + hop_limit: hop_limit, }); emit((ip_repr, IcmpRepr::Ipv4(repr))) - }, + } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(ipv6_addr) => { let packet = Icmpv6Packet::new_unchecked(&*packet_buf); let src_addr = Ipv6Address::default(); - let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored())?; + let repr = Icmpv6Repr::parse( + &src_addr.into(), + &ipv6_addr.into(), + &packet, + &ChecksumCapabilities::ignored(), + )?; let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_addr, - dst_addr: ipv6_addr, + src_addr: src_addr, + dst_addr: ipv6_addr, next_header: IpProtocol::Icmpv6, payload_len: repr.buffer_len(), - hop_limit: hop_limit, + hop_limit: hop_limit, }); emit((ip_repr, IcmpRepr::Ipv6(repr))) - }, - _ => Err(Error::Unaddressable) + } + _ => Err(Error::Unaddressable), } })?; @@ -462,20 +523,25 @@ impl<'a> Into> for IcmpSocket<'a> { #[cfg(test)] mod tests_common { + pub use super::*; pub use crate::phy::DeviceCapabilities; pub use crate::wire::IpAddress; - pub use super::*; pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static> { - IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 66 * packets]) + IcmpSocketBuffer::new( + vec![IcmpPacketMetadata::EMPTY; packets], + vec![0; 66 * packets], + ) } - pub fn socket(rx_buffer: IcmpSocketBuffer<'static>, - tx_buffer: IcmpSocketBuffer<'static>) -> IcmpSocket<'static> { + pub fn socket( + rx_buffer: IcmpSocketBuffer<'static>, + tx_buffer: IcmpSocketBuffer<'static>, + ) -> IcmpSocket<'static> { IcmpSocket::new(rx_buffer, tx_buffer) } - pub const LOCAL_PORT: u16 = 53; + pub const LOCAL_PORT: u16 = 53; pub static UDP_REPR: UdpRepr = UdpRepr { src_port: 53, @@ -492,13 +558,16 @@ mod test_ipv4 { use crate::wire::Icmpv4DstUnreachable; const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - const LOCAL_END_V4: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv4(LOCAL_IPV4), port: LOCAL_PORT }; + const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + const LOCAL_END_V4: IpEndpoint = IpEndpoint { + addr: IpAddress::Ipv4(LOCAL_IPV4), + port: LOCAL_PORT, + }; static ECHOV4_REPR: Icmpv4Repr = Icmpv4Repr::EchoRequest { - ident: 0x1234, - seq_no: 0x5678, - data: &[0xff; 16] + ident: 0x1234, + seq_no: 0x5678, + data: &[0xff; 16], }; static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { @@ -506,7 +575,7 @@ mod test_ipv4 { dst_addr: REMOTE_IPV4, protocol: IpProtocol::Icmp, payload_len: 24, - hop_limit: 0x40 + hop_limit: 0x40, }); static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { @@ -514,14 +583,16 @@ mod test_ipv4 { dst_addr: LOCAL_IPV4, protocol: IpProtocol::Icmp, payload_len: 24, - hop_limit: 0x40 + hop_limit: 0x40, }); #[test] fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); - assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()), - Err(Error::Unaddressable)); + assert_eq!( + socket.send_slice(b"abcdef", IpAddress::default()), + Err(Error::Unaddressable) + ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(())); } @@ -530,34 +601,51 @@ mod test_ipv4 { let mut socket = socket(buffer(0), buffer(1)); let checksum = ChecksumCapabilities::default(); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Err(Error::Exhausted)); + assert_eq!( + socket.dispatch(&Context::DUMMY, |_| unreachable!()), + Err(Error::Exhausted) + ); // This buffer is too long - assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()), Err(Error::Truncated)); + assert_eq!( + socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()), + Err(Error::Truncated) + ); assert!(socket.can_send()); let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); ECHOV4_REPR.emit(&mut packet, &checksum); - assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(())); - assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted)); + assert_eq!( + socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), + Ok(()) + ); + assert_eq!( + socket.send_slice(b"123456", REMOTE_IPV4.into()), + Err(Error::Exhausted) + ); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { - assert_eq!(ip_repr, LOCAL_IPV4_REPR); - assert_eq!(icmp_repr, ECHOV4_REPR.into()); + 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) + }), Err(Error::Unaddressable) - }), Err(Error::Unaddressable)); + ); // buffer is not taken off of the tx queue due to the error assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { - assert_eq!(ip_repr, LOCAL_IPV4_REPR); - assert_eq!(icmp_repr, ECHOV4_REPR.into()); + 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(()) + }), Ok(()) - }), Ok(())); + ); // buffer is taken off of the queue this time assert!(socket.can_send()); } @@ -573,17 +661,26 @@ 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(&Context::DUMMY, |(ip_repr, _)| { - assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address::UNSPECIFIED, - dst_addr: REMOTE_IPV4, - protocol: IpProtocol::Icmp, - payload_len: ECHOV4_REPR.buffer_len(), - hop_limit: 0x2a, - })); + assert_eq!( + s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), + Ok(()) + ); + assert_eq!( + s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + assert_eq!( + ip_repr, + IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address::UNSPECIFIED, + dst_addr: REMOTE_IPV4, + protocol: IpProtocol::Icmp, + payload_len: ECHOV4_REPR.buffer_len(), + hop_limit: 0x2a, + }) + ); + Ok(()) + }), Ok(()) - }), Ok(())); + ); } #[test] @@ -602,13 +699,17 @@ mod test_ipv4 { let data = &packet.into_inner()[..]; 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_eq!( + socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), + Ok(()) + ); assert!(socket.can_recv()); 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.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), + Err(Error::Exhausted) + ); assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into()))); assert!(!socket.can_recv()); @@ -623,9 +724,9 @@ mod test_ipv4 { let mut bytes = [0xff; 20]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); let icmp_repr = Icmpv4Repr::EchoRequest { - ident: 0x4321, + ident: 0x4321, seq_no: 0x5678, - data: &[0xff; 16] + data: &[0xff; 16], }; icmp_repr.emit(&mut packet, &checksum); @@ -649,7 +750,8 @@ mod test_ipv4 { &LOCAL_IPV4.into(), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(UDP_PAYLOAD), - &checksum); + &checksum, + ); let data = &packet.into_inner()[..]; @@ -660,16 +762,16 @@ mod test_ipv4 { dst_addr: REMOTE_IPV4, protocol: IpProtocol::Icmp, payload_len: 12, - hop_limit: 0x40 + hop_limit: 0x40, }, - data: data + data: data, }; let ip_repr = IpRepr::Unspecified { src_addr: REMOTE_IPV4.into(), dst_addr: LOCAL_IPV4.into(), protocol: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), - hop_limit: 0x40 + hop_limit: 0x40, }; assert!(!socket.can_recv()); @@ -677,14 +779,19 @@ mod test_ipv4 { // Ensure we can accept ICMP error response to the bound // UDP port assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into())); - assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), - Ok(())); + assert_eq!( + socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), + Ok(()) + ); assert!(socket.can_recv()); let mut bytes = [0x00; 46]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); icmp_repr.emit(&mut packet, &checksum); - assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into()))); + assert_eq!( + socket.recv(), + Ok((&packet.into_inner()[..], REMOTE_IPV4.into())) + ); assert!(!socket.can_recv()); } } @@ -695,15 +802,18 @@ mod test_ipv6 { use crate::wire::Icmpv6DstUnreachable; - const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1]); - const LOCAL_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2]); - const LOCAL_END_V6: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv6(LOCAL_IPV6), port: LOCAL_PORT }; + const REMOTE_IPV6: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + const LOCAL_IPV6: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); + const LOCAL_END_V6: IpEndpoint = IpEndpoint { + addr: IpAddress::Ipv6(LOCAL_IPV6), + port: LOCAL_PORT, + }; static ECHOV6_REPR: Icmpv6Repr = Icmpv6Repr::EchoRequest { - ident: 0x1234, - seq_no: 0x5678, - data: &[0xff; 16] + ident: 0x1234, + seq_no: 0x5678, + data: &[0xff; 16], }; static LOCAL_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr { @@ -711,7 +821,7 @@ mod test_ipv6 { dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, payload_len: 24, - hop_limit: 0x40 + hop_limit: 0x40, }); static REMOTE_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr { @@ -719,14 +829,16 @@ mod test_ipv6 { dst_addr: LOCAL_IPV6, next_header: IpProtocol::Icmpv6, payload_len: 24, - hop_limit: 0x40 + hop_limit: 0x40, }); #[test] fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); - assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()), - Err(Error::Unaddressable)); + assert_eq!( + socket.send_slice(b"abcdef", IpAddress::default()), + Err(Error::Unaddressable) + ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV6.into()), Ok(())); } @@ -735,34 +847,56 @@ mod test_ipv6 { let mut socket = socket(buffer(0), buffer(1)); let checksum = ChecksumCapabilities::default(); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Err(Error::Exhausted)); + assert_eq!( + socket.dispatch(&Context::DUMMY, |_| unreachable!()), + Err(Error::Exhausted) + ); // This buffer is too long - assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()), Err(Error::Truncated)); + assert_eq!( + socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()), + Err(Error::Truncated) + ); assert!(socket.can_send()); let mut bytes = vec![0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); + ECHOV6_REPR.emit( + &LOCAL_IPV6.into(), + &REMOTE_IPV6.into(), + &mut packet, + &checksum, + ); - assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(())); - assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted)); + assert_eq!( + socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), + Ok(()) + ); + assert_eq!( + socket.send_slice(b"123456", REMOTE_IPV6.into()), + Err(Error::Exhausted) + ); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { - assert_eq!(ip_repr, LOCAL_IPV6_REPR); - assert_eq!(icmp_repr, ECHOV6_REPR.into()); + 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) + }), Err(Error::Unaddressable) - }), Err(Error::Unaddressable)); + ); // buffer is not taken off of the tx queue due to the error assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { - assert_eq!(ip_repr, LOCAL_IPV6_REPR); - assert_eq!(icmp_repr, ECHOV6_REPR.into()); + 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(()) + }), Ok(()) - }), Ok(())); + ); // buffer is taken off of the queue this time assert!(socket.can_send()); } @@ -774,21 +908,35 @@ mod test_ipv6 { let mut bytes = vec![0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); + ECHOV6_REPR.emit( + &LOCAL_IPV6.into(), + &REMOTE_IPV6.into(), + &mut packet, + &checksum, + ); s.set_hop_limit(Some(0x2a)); - assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(())); - assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| { - assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::UNSPECIFIED, - dst_addr: REMOTE_IPV6, - next_header: IpProtocol::Icmpv6, - payload_len: ECHOV6_REPR.buffer_len(), - hop_limit: 0x2a, - })); + assert_eq!( + s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()) - }), Ok(())); + ); + assert_eq!( + s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + assert_eq!( + ip_repr, + IpRepr::Ipv6(Ipv6Repr { + src_addr: Ipv6Address::UNSPECIFIED, + dst_addr: REMOTE_IPV6, + next_header: IpProtocol::Icmpv6, + payload_len: ECHOV6_REPR.buffer_len(), + hop_limit: 0x2a, + }) + ); + Ok(()) + }), + Ok(()) + ); } #[test] @@ -803,17 +951,26 @@ mod test_ipv6 { let mut bytes = [0xff; 24]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); - ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); + ECHOV6_REPR.emit( + &LOCAL_IPV6.into(), + &REMOTE_IPV6.into(), + &mut packet, + &checksum, + ); let data = &packet.into_inner()[..]; 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_eq!( + socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), + Ok(()) + ); assert!(socket.can_recv()); 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.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), + Err(Error::Exhausted) + ); assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into()))); assert!(!socket.can_recv()); @@ -828,11 +985,16 @@ mod test_ipv6 { let mut bytes = [0xff; 20]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); let icmp_repr = Icmpv6Repr::EchoRequest { - ident: 0x4321, + ident: 0x4321, seq_no: 0x5678, - data: &[0xff; 16] + data: &[0xff; 16], }; - icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); + icmp_repr.emit( + &LOCAL_IPV6.into(), + &REMOTE_IPV6.into(), + &mut packet, + &checksum, + ); // Ensure that a packet with an identifier that isn't the bound // ID is not accepted @@ -854,7 +1016,8 @@ mod test_ipv6 { &LOCAL_IPV6.into(), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(UDP_PAYLOAD), - &checksum); + &checksum, + ); let data = &packet.into_inner()[..]; @@ -865,16 +1028,16 @@ mod test_ipv6 { dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, payload_len: 12, - hop_limit: 0x40 + hop_limit: 0x40, }, - data: data + data: data, }; let ip_repr = IpRepr::Unspecified { src_addr: REMOTE_IPV6.into(), dst_addr: LOCAL_IPV6.into(), protocol: IpProtocol::Icmpv6, payload_len: icmp_repr.buffer_len(), - hop_limit: 0x40 + hop_limit: 0x40, }; assert!(!socket.can_recv()); @@ -882,14 +1045,24 @@ mod test_ipv6 { // Ensure we can accept ICMP error response to the bound // UDP port assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into())); - assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), - Ok(())); + assert_eq!( + socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), + Ok(()) + ); assert!(socket.can_recv()); let mut bytes = [0x00; 66]; let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]); - icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum); - assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into()))); + icmp_repr.emit( + &LOCAL_IPV6.into(), + &REMOTE_IPV6.into(), + &mut packet, + &checksum, + ); + assert_eq!( + socket.recv(), + Ok((&packet.into_inner()[..], REMOTE_IPV6.into())) + ); assert!(!socket.can_recv()); } } diff --git a/src/socket/meta.rs b/src/socket/meta.rs index fe6873b5f..3d490dbb8 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -1,6 +1,6 @@ -use crate::wire::IpAddress; -use crate::socket::{SocketHandle, PollAt}; +use crate::socket::{PollAt, SocketHandle}; use crate::time::{Duration, Instant}; +use crate::wire::IpAddress; /// Neighbor dependency. /// @@ -16,7 +16,7 @@ enum NeighborState { Waiting { neighbor: IpAddress, silent_until: Instant, - } + }, } impl Default for NeighborState { @@ -36,7 +36,7 @@ pub struct Meta { /// Mainly useful for debug output. pub(crate) handle: SocketHandle, /// See [NeighborState](struct.NeighborState.html). - neighbor_state: NeighborState, + neighbor_state: NeighborState, } impl Meta { @@ -46,34 +46,41 @@ impl Meta { /// See also `iface::NeighborCache::SILENT_TIME`. pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 }; - pub(crate) fn poll_at(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt - where F: Fn(IpAddress) -> bool + pub(crate) fn poll_at(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt + where + F: Fn(IpAddress) -> bool, { match self.neighbor_state { - NeighborState::Active => - socket_poll_at, - NeighborState::Waiting { neighbor, .. } - if has_neighbor(neighbor) => - socket_poll_at, - NeighborState::Waiting { silent_until, .. } => - PollAt::Time(silent_until) + NeighborState::Active => socket_poll_at, + NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at, + NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until), } } pub(crate) fn egress_permitted(&mut self, timestamp: Instant, has_neighbor: F) -> bool - where F: Fn(IpAddress) -> bool + where + F: Fn(IpAddress) -> bool, { match self.neighbor_state { - NeighborState::Active => - true, - NeighborState::Waiting { neighbor, silent_until } => { + NeighborState::Active => true, + NeighborState::Waiting { + neighbor, + silent_until, + } => { if has_neighbor(neighbor) { - net_trace!("{}: neighbor {} discovered, unsilencing", - self.handle, neighbor); + net_trace!( + "{}: neighbor {} discovered, unsilencing", + self.handle, + neighbor + ); self.neighbor_state = NeighborState::Active; true } else if timestamp >= silent_until { - net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor); + net_trace!( + "{}: neighbor {} silence timer expired, rediscovering", + self.handle, + neighbor + ); true } else { false @@ -83,10 +90,15 @@ impl Meta { } pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) { - net_trace!("{}: neighbor {} missing, silencing until t+{}", - self.handle, neighbor, Self::DISCOVERY_SILENT_TIME); + net_trace!( + "{}: neighbor {} missing, silencing until t+{}", + self.handle, + neighbor, + Self::DISCOVERY_SILENT_TIME + ); self.neighbor_state = NeighborState::Waiting { - neighbor, silent_until: timestamp + Self::DISCOVERY_SILENT_TIME + neighbor, + silent_until: timestamp + Self::DISCOVERY_SILENT_TIME, }; } } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 1c01efb05..b37c228eb 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -14,19 +14,22 @@ size for a buffer, allocate it, and let the networking stack use it. use crate::phy::DeviceCapabilities; use crate::time::Instant; +#[cfg(feature = "socket-dhcpv4")] +mod dhcpv4; +#[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") +))] +mod icmp; mod meta; #[cfg(feature = "socket-raw")] mod raw; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -mod icmp; -#[cfg(feature = "socket-udp")] -mod udp; +mod ref_; +mod set; #[cfg(feature = "socket-tcp")] mod tcp; -#[cfg(feature = "socket-dhcpv4")] -mod dhcpv4; -mod set; -mod ref_; +#[cfg(feature = "socket-udp")] +mod udp; #[cfg(feature = "async")] mod waker; @@ -36,30 +39,24 @@ pub(crate) use self::meta::Meta as SocketMeta; pub(crate) use self::waker::WakerRegistration; #[cfg(feature = "socket-raw")] -pub use self::raw::{RawPacketMetadata, - RawSocketBuffer, - RawSocket}; +pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer}; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -pub use self::icmp::{IcmpPacketMetadata, - IcmpSocketBuffer, - Endpoint as IcmpEndpoint, - IcmpSocket}; +#[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") +))] +pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; #[cfg(feature = "socket-udp")] -pub use self::udp::{UdpPacketMetadata, - UdpSocketBuffer, - UdpSocket}; +pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; #[cfg(feature = "socket-tcp")] -pub use self::tcp::{SocketBuffer as TcpSocketBuffer, - State as TcpState, - TcpSocket}; +pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket}; #[cfg(feature = "socket-dhcpv4")] -pub use self::dhcpv4::{Dhcpv4Socket, Config as Dhcpv4Config, Event as Dhcpv4Event}; +pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; -pub use self::set::{Set as SocketSet, Item as SocketSetItem, Handle as SocketHandle}; +pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; pub use self::ref_::Ref as SocketRef; @@ -91,7 +88,10 @@ pub(crate) enum PollAt { pub enum Socket<'a> { #[cfg(feature = "socket-raw")] Raw(RawSocket<'a>), - #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] + #[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") + ))] Icmp(IcmpSocket<'a>), #[cfg(feature = "socket-udp")] Udp(UdpSocket<'a>), @@ -152,15 +152,13 @@ impl<'a> SocketSession for Socket<'a> { /// A conversion trait for network sockets. pub trait AnySocket<'a>: SocketSession + Sized { - fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> - Option>; + fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option>; } macro_rules! from_socket { ($socket:ty, $variant:ident) => { impl<'a> AnySocket<'a> for $socket { - fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> - Option> { + fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option> { if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) { Some(SocketRef::new(socket)) } else { @@ -168,12 +166,15 @@ macro_rules! from_socket { } } } - } + }; } #[cfg(feature = "socket-raw")] from_socket!(RawSocket<'a>, Raw); -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] +#[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") +))] from_socket!(IcmpSocket<'a>, Icmp); #[cfg(feature = "socket-udp")] from_socket!(UdpSocket<'a>, Udp); @@ -193,14 +194,13 @@ pub(crate) struct Context { #[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{ + checksum: crate::phy::ChecksumCapabilities { #[cfg(feature = "proto-ipv4")] icmpv4: crate::phy::Checksum::Both, #[cfg(feature = "proto-ipv6")] @@ -216,7 +216,6 @@ impl Context { max_transmission_unit: 1500, }, ethernet_address: None, - now: Instant{millis: 0}, + now: Instant { millis: 0 }, }; - -} \ No newline at end of file +} diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 0baa70238..bf149c7ac 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -2,18 +2,18 @@ use core::cmp::min; #[cfg(feature = "async")] use core::task::Waker; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; -use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context}; -use crate::storage::{PacketBuffer, PacketMetadata}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; +use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::storage::{PacketBuffer, PacketMetadata}; +use crate::{Error, Result}; -use crate::wire::{IpVersion, IpRepr, IpProtocol}; +use crate::wire::{IpProtocol, IpRepr, IpVersion}; #[cfg(feature = "proto-ipv4")] -use crate::wire::{Ipv4Repr, Ipv4Packet}; +use crate::wire::{Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6Repr, Ipv6Packet}; +use crate::wire::{Ipv6Packet, Ipv6Repr}; /// A UDP packet metadata. pub type RawPacketMetadata = PacketMetadata<()>; @@ -28,10 +28,10 @@ pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>; #[derive(Debug)] pub struct RawSocket<'a> { pub(crate) meta: SocketMeta, - ip_version: IpVersion, + ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a>, - tx_buffer: RawSocketBuffer<'a>, + rx_buffer: RawSocketBuffer<'a>, + tx_buffer: RawSocketBuffer<'a>, #[cfg(feature = "async")] rx_waker: WakerRegistration, #[cfg(feature = "async")] @@ -41,9 +41,12 @@ pub struct RawSocket<'a> { impl<'a> RawSocket<'a> { /// Create a raw IP socket bound to the given IP version and datagram protocol, /// with the given buffers. - pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a>, - tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> { + pub fn new( + ip_version: IpVersion, + ip_protocol: IpProtocol, + rx_buffer: RawSocketBuffer<'a>, + tx_buffer: RawSocketBuffer<'a>, + ) -> RawSocket<'a> { RawSocket { meta: SocketMeta::default(), ip_version, @@ -61,12 +64,12 @@ impl<'a> RawSocket<'a> { /// /// The waker is woken on state changes that might affect the return value /// of `recv` method calls, such as receiving data, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has /// necessarily changed. #[cfg(feature = "async")] @@ -79,12 +82,12 @@ impl<'a> RawSocket<'a> { /// The waker is woken on state changes that might affect the return value /// of `send` method calls, such as space becoming available in the transmit /// buffer, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has /// necessarily changed. #[cfg(feature = "async")] @@ -160,9 +163,13 @@ impl<'a> RawSocket<'a> { pub fn send(&mut self, size: usize) -> Result<&mut [u8]> { let packet_buf = self.tx_buffer.enqueue(size, ())?; - net_trace!("{}:{}:{}: buffer to send {} octets", - self.meta.handle, self.ip_version, self.ip_protocol, - packet_buf.len()); + net_trace!( + "{}:{}:{}: buffer to send {} octets", + self.meta.handle, + self.ip_version, + self.ip_protocol, + packet_buf.len() + ); Ok(packet_buf) } @@ -183,9 +190,13 @@ impl<'a> RawSocket<'a> { pub fn recv(&mut self) -> Result<&[u8]> { let ((), packet_buf) = self.rx_buffer.dequeue()?; - net_trace!("{}:{}:{}: receive {} buffered octets", - self.meta.handle, self.ip_version, self.ip_protocol, - packet_buf.len()); + net_trace!( + "{}:{}:{}: receive {} buffered octets", + self.meta.handle, + self.ip_version, + self.ip_protocol, + packet_buf.len() + ); Ok(packet_buf) } @@ -200,8 +211,12 @@ impl<'a> RawSocket<'a> { } pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool { - if ip_repr.version() != self.ip_version { return false } - if ip_repr.protocol() != self.ip_protocol { return false } + if ip_repr.version() != self.ip_version { + return false; + } + if ip_repr.protocol() != self.ip_protocol { + return false; + } true } @@ -210,14 +225,18 @@ impl<'a> RawSocket<'a> { debug_assert!(self.accepts(ip_repr)); let header_len = ip_repr.buffer_len(); - let total_len = header_len + payload.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], &cx.caps.checksum); packet_buf[header_len..].copy_from_slice(payload); - net_trace!("{}:{}:{}: receiving {} octets", - self.meta.handle, self.ip_version, self.ip_protocol, - packet_buf.len()); + net_trace!( + "{}:{}:{}: receiving {} octets", + self.meta.handle, + self.ip_version, + self.ip_protocol, + packet_buf.len() + ); #[cfg(feature = "async")] self.rx_waker.wake(); @@ -226,14 +245,21 @@ impl<'a> RawSocket<'a> { } pub(crate) fn dispatch(&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])> { + where + F: FnOnce((IpRepr, &[u8])) -> Result<()>, + { + fn prepare<'a>( + protocol: IpProtocol, + buffer: &'a mut [u8], + _checksum_caps: &ChecksumCapabilities, + ) -> Result<(IpRepr, &'a [u8])> { match IpVersion::of_packet(buffer)? { #[cfg(feature = "proto-ipv4")] IpVersion::Ipv4 => { let mut packet = Ipv4Packet::new_checked(buffer)?; - if packet.protocol() != protocol { return Err(Error::Unaddressable) } + if packet.protocol() != protocol { + return Err(Error::Unaddressable); + } if _checksum_caps.ipv4.tx() { packet.fill_checksum(); } else { @@ -249,7 +275,9 @@ impl<'a> RawSocket<'a> { #[cfg(feature = "proto-ipv6")] IpVersion::Ipv6 => { let packet = Ipv6Packet::new_checked(buffer)?; - if packet.next_header() != protocol { return Err(Error::Unaddressable) } + if packet.next_header() != protocol { + return Err(Error::Unaddressable); + } let packet = Ipv6Packet::new_unchecked(&*packet.into_inner()); let ipv6_repr = Ipv6Repr::parse(&packet)?; Ok((IpRepr::Ipv6(ipv6_repr), packet.payload())) @@ -258,21 +286,29 @@ impl<'a> RawSocket<'a> { } } - let handle = self.meta.handle; + let handle = self.meta.handle; let ip_protocol = self.ip_protocol; - let ip_version = self.ip_version; + let ip_version = self.ip_version; self.tx_buffer.dequeue_with(|&mut (), packet_buf| { match prepare(ip_protocol, packet_buf, &cx.caps.checksum) { Ok((ip_repr, raw_packet)) => { - net_trace!("{}:{}:{}: sending {} octets", - handle, ip_version, ip_protocol, - ip_repr.buffer_len() + raw_packet.len()); + net_trace!( + "{}:{}:{}: sending {} octets", + handle, + ip_version, + ip_protocol, + ip_repr.buffer_len() + raw_packet.len() + ); emit((ip_repr, raw_packet)) } Err(error) => { - net_debug!("{}:{}:{}: dropping outgoing packet ({})", - handle, ip_version, ip_protocol, - error); + net_debug!( + "{}:{}:{}: dropping outgoing packet ({})", + handle, + ip_version, + ip_protocol, + error + ); // Return Ok(()) so the packet is dequeued. Ok(()) } @@ -302,26 +338,34 @@ impl<'a> Into> for RawSocket<'a> { #[cfg(test)] mod test { + use super::*; use crate::wire::IpRepr; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Repr}; - use super::*; fn buffer(packets: usize) -> RawSocketBuffer<'static> { - RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets]) + RawSocketBuffer::new( + vec![RawPacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ) } #[cfg(feature = "proto-ipv4")] mod ipv4_locals { use super::*; - pub fn socket(rx_buffer: RawSocketBuffer<'static>, - tx_buffer: RawSocketBuffer<'static>) - -> RawSocket<'static> { - RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO), - rx_buffer, tx_buffer) + pub fn socket( + rx_buffer: RawSocketBuffer<'static>, + tx_buffer: RawSocketBuffer<'static>, + ) -> RawSocket<'static> { + RawSocket::new( + IpVersion::Ipv4, + IpProtocol::Unknown(IP_PROTO), + rx_buffer, + tx_buffer, + ) } pub const IP_PROTO: u8 = 63; @@ -330,60 +374,54 @@ mod test { dst_addr: Ipv4Address([10, 0, 0, 2]), protocol: IpProtocol::Unknown(IP_PROTO), payload_len: 4, - hop_limit: 64 + hop_limit: 64, }); pub const PACKET_BYTES: [u8; 24] = [ - 0x45, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x40, 0x00, - 0x40, 0x3f, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x01, - 0x0a, 0x00, 0x00, 0x02, - 0xaa, 0x00, 0x00, 0xff - ]; - pub const PACKET_PAYLOAD: [u8; 4] = [ - 0xaa, 0x00, 0x00, 0xff + 0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x3f, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00, 0xff, ]; + pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; } #[cfg(feature = "proto-ipv6")] mod ipv6_locals { use super::*; - pub fn socket(rx_buffer: RawSocketBuffer<'static>, - tx_buffer: RawSocketBuffer<'static>) - -> RawSocket<'static> { - RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO), - rx_buffer, tx_buffer) + pub fn socket( + rx_buffer: RawSocketBuffer<'static>, + tx_buffer: RawSocketBuffer<'static>, + ) -> RawSocket<'static> { + RawSocket::new( + IpVersion::Ipv6, + IpProtocol::Unknown(IP_PROTO), + rx_buffer, + tx_buffer, + ) } pub const IP_PROTO: u8 = 63; pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), - dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]), + src_addr: Ipv6Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ]), + dst_addr: Ipv6Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, + ]), next_header: IpProtocol::Unknown(IP_PROTO), payload_len: 4, - hop_limit: 64 + hop_limit: 64, }); pub const PACKET_BYTES: [u8; 44] = [ - 0x60, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x3f, 0x40, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0xaa, 0x00, 0x00, 0xff + 0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xaa, 0x00, + 0x00, 0xff, ]; - pub const PACKET_PAYLOAD: [u8; 4] = [ - 0xaa, 0x00, 0x00, 0xff - ]; + pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; } macro_rules! reusable_ip_specific_tests { @@ -402,25 +440,33 @@ mod test { let mut socket = $socket(buffer(0), buffer(1)); assert!(socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Err(Error::Exhausted)); + 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(&Context::DUMMY, |(ip_repr, ip_payload)| { - assert_eq!(ip_repr, $hdr); - assert_eq!(ip_payload, &$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) - }), Err(Error::Unaddressable)); + ); assert!(!socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { - assert_eq!(ip_repr, $hdr); - assert_eq!(ip_payload, &$payload); + assert_eq!( + socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { + assert_eq!(ip_repr, $hdr); + assert_eq!(ip_payload, &$payload); + Ok(()) + }), Ok(()) - }), Ok(())); + ); assert!(socket.can_send()); } @@ -444,20 +490,32 @@ mod test { buffer[..$packet.len()].copy_from_slice(&$packet[..]); assert!(socket.accepts(&$hdr)); - assert_eq!(socket.process(&Context::DUMMY, &$hdr, &buffer), Err(Error::Truncated)); + assert_eq!( + socket.process(&Context::DUMMY, &$hdr, &buffer), + Err(Error::Truncated) + ); } } - } + }; } #[cfg(feature = "proto-ipv4")] - reusable_ip_specific_tests!(ipv4, ipv4_locals::socket, ipv4_locals::HEADER_REPR, - ipv4_locals::PACKET_BYTES, ipv4_locals::PACKET_PAYLOAD); + reusable_ip_specific_tests!( + ipv4, + ipv4_locals::socket, + ipv4_locals::HEADER_REPR, + ipv4_locals::PACKET_BYTES, + ipv4_locals::PACKET_PAYLOAD + ); #[cfg(feature = "proto-ipv6")] - reusable_ip_specific_tests!(ipv6, ipv6_locals::socket, ipv6_locals::HEADER_REPR, - ipv6_locals::PACKET_BYTES, ipv6_locals::PACKET_PAYLOAD); - + reusable_ip_specific_tests!( + ipv6, + ipv6_locals::socket, + ipv6_locals::HEADER_REPR, + ipv6_locals::PACKET_BYTES, + ipv6_locals::PACKET_PAYLOAD + ); #[test] #[cfg(feature = "proto-ipv4")] @@ -470,15 +528,13 @@ mod test { Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Ok(())); + 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(&Context::DUMMY, |_| unreachable!()), - Ok(())); + assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(())); } #[cfg(feature = "proto-ipv6")] { @@ -488,15 +544,13 @@ mod test { Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Ok(())); + 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(&Context::DUMMY, |_| unreachable!()), - Ok(())); + assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(())); } } @@ -512,13 +566,25 @@ mod test { assert_eq!(socket.recv(), Err(Error::Exhausted)); assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); - assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD), - Ok(())); + 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(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD), - Err(Error::Exhausted)); + 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()); } @@ -529,13 +595,25 @@ mod test { assert_eq!(socket.recv(), Err(Error::Exhausted)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); - assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD), - Ok(())); + 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(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD), - Err(Error::Exhausted)); + 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()); } @@ -545,16 +623,24 @@ mod test { fn test_doesnt_accept_wrong_proto() { #[cfg(feature = "proto-ipv4")] { - let socket = RawSocket::new(IpVersion::Ipv4, - IpProtocol::Unknown(ipv4_locals::IP_PROTO+1), buffer(1), buffer(1)); + let socket = RawSocket::new( + IpVersion::Ipv4, + IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1), + buffer(1), + buffer(1), + ); assert!(!socket.accepts(&ipv4_locals::HEADER_REPR)); #[cfg(feature = "proto-ipv6")] assert!(!socket.accepts(&ipv6_locals::HEADER_REPR)); } #[cfg(feature = "proto-ipv6")] { - let socket = RawSocket::new(IpVersion::Ipv6, - IpProtocol::Unknown(ipv6_locals::IP_PROTO+1), buffer(1), buffer(1)); + let socket = RawSocket::new( + IpVersion::Ipv6, + IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1), + buffer(1), + buffer(1), + ); assert!(!socket.accepts(&ipv6_locals::HEADER_REPR)); #[cfg(feature = "proto-ipv4")] assert!(!socket.accepts(&ipv4_locals::HEADER_REPR)); diff --git a/src/socket/ref_.rs b/src/socket/ref_.rs index 3a52a779a..93992c93a 100644 --- a/src/socket/ref_.rs +++ b/src/socket/ref_.rs @@ -1,6 +1,5 @@ use core::ops::{Deref, DerefMut}; - /// A trait for tracking a socket usage session. /// /// Allows implementation of custom drop logic that runs only if the socket was changed @@ -13,7 +12,10 @@ pub trait Session { #[cfg(feature = "socket-raw")] impl<'a> Session for crate::socket::RawSocket<'a> {} -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] +#[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") +))] impl<'a> Session for crate::socket::IcmpSocket<'a> {} #[cfg(feature = "socket-udp")] impl<'a> Session for crate::socket::UdpSocket<'a> {} @@ -41,7 +43,9 @@ impl<'a, T: Session + 'a> Ref<'a, T> { /// /// [into_inner]: #method.into_inner pub fn new(socket: &'a mut T) -> Self { - Ref { socket: Some(socket) } + Ref { + socket: Some(socket), + } } /// Unwrap a smart pointer to a socket. diff --git a/src/socket/set.rs b/src/socket/set.rs index 74af3177f..cdfd75756 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -1,9 +1,9 @@ use core::{fmt, slice}; use managed::ManagedSlice; -use crate::socket::{Socket, SocketRef, AnySocket}; #[cfg(feature = "socket-tcp")] use crate::socket::TcpState; +use crate::socket::{AnySocket, Socket, SocketRef}; /// An item of a socket set. /// @@ -12,7 +12,7 @@ use crate::socket::TcpState; #[derive(Debug)] pub struct Item<'a> { socket: Socket<'a>, - refs: usize + refs: usize, } /// A handle, identifying a socket in a set. @@ -31,13 +31,15 @@ impl fmt::Display for Handle { /// The lifetime `'a` is used when storing a `Socket<'a>`. #[derive(Debug)] pub struct Set<'a> { - sockets: ManagedSlice<'a, Option>> + sockets: ManagedSlice<'a, Option>>, } impl<'a> Set<'a> { /// Create a socket set using the provided storage. pub fn new(sockets: SocketsT) -> Set<'a> - where SocketsT: Into>>> { + where + SocketsT: Into>>>, + { let sockets = sockets.into(); Set { sockets } } @@ -47,10 +49,10 @@ impl<'a> Set<'a> { /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. pub fn add(&mut self, socket: T) -> Handle - where T: Into> + where + T: Into>, { - fn put<'a>(index: usize, slot: &mut Option>, - mut socket: Socket<'a>) -> Handle { + fn put<'a>(index: usize, slot: &mut Option>, mut socket: Socket<'a>) -> Handle { net_trace!("[{}]: adding", index); let handle = Handle(index); socket.meta_mut().handle = handle; @@ -62,7 +64,7 @@ impl<'a> Set<'a> { for (index, slot) in self.sockets.iter_mut().enumerate() { if slot.is_none() { - return put(index, slot, socket) + return put(index, slot, socket); } } @@ -86,11 +88,9 @@ impl<'a> Set<'a> { /// or the socket has the wrong type. pub fn get>(&mut self, handle: Handle) -> SocketRef { match self.sockets[handle.0].as_mut() { - Some(item) => { - T::downcast(SocketRef::new(&mut item.socket)) - .expect("handle refers to a socket of a wrong type") - } - None => panic!("handle does not refer to a valid socket") + Some(item) => T::downcast(SocketRef::new(&mut item.socket)) + .expect("handle refers to a socket of a wrong type"), + None => panic!("handle does not refer to a valid socket"), } } @@ -102,7 +102,7 @@ impl<'a> Set<'a> { net_trace!("[{}]: removing", handle.0); match self.sockets[handle.0].take() { Some(item) => item.socket, - None => panic!("handle does not refer to a valid socket") + None => panic!("handle does not refer to a valid socket"), } } @@ -124,10 +124,12 @@ impl<'a> Set<'a> { /// or if the reference count is already zero. pub fn release(&mut self, handle: Handle) { let refs = &mut self.sockets[handle.0] - .as_mut() - .expect("handle does not refer to a valid socket") - .refs; - if *refs == 0 { panic!("decreasing reference count past zero") } + .as_mut() + .expect("handle does not refer to a valid socket") + .refs; + if *refs == 0 { + panic!("decreasing reference count past zero") + } *refs -= 1 } @@ -138,27 +140,31 @@ impl<'a> Set<'a> { pub fn prune(&mut self) { for (index, item) in self.sockets.iter_mut().enumerate() { let mut may_remove = false; - if let Some(Item { refs: 0, ref mut socket }) = *item { + if let Some(Item { + refs: 0, + ref mut socket, + }) = *item + { match *socket { #[cfg(feature = "socket-raw")] - Socket::Raw(_) => - may_remove = true, - #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - Socket::Icmp(_) => - may_remove = true, + Socket::Raw(_) => may_remove = true, + #[cfg(all( + feature = "socket-icmp", + any(feature = "proto-ipv4", feature = "proto-ipv6") + ))] + Socket::Icmp(_) => may_remove = true, #[cfg(feature = "socket-udp")] - Socket::Udp(_) => - may_remove = true, + Socket::Udp(_) => may_remove = true, #[cfg(feature = "socket-tcp")] - Socket::Tcp(ref mut socket) => + Socket::Tcp(ref mut socket) => { if socket.state() == TcpState::Closed { may_remove = true } else { socket.close() - }, + } + } #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(_) => - may_remove = true, + Socket::Dhcpv4(_) => may_remove = true, } } if may_remove { @@ -170,12 +176,16 @@ impl<'a> Set<'a> { /// Iterate every socket in this set. pub fn iter<'d>(&'d self) -> Iter<'d, 'a> { - Iter { lower: self.sockets.iter() } + Iter { + lower: self.sockets.iter(), + } } /// Iterate every socket in this set, as SocketRef. pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> { - IterMut { lower: self.sockets.iter_mut() } + IterMut { + lower: self.sockets.iter_mut(), + } } } @@ -184,7 +194,7 @@ impl<'a> Set<'a> { /// This struct is created by the [iter](struct.SocketSet.html#method.iter) /// on [socket sets](struct.SocketSet.html). pub struct Iter<'a, 'b: 'a> { - lower: slice::Iter<'a, Option>> + lower: slice::Iter<'a, Option>>, } impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> { @@ -193,7 +203,7 @@ impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> { fn next(&mut self) -> Option { while let Some(item_opt) = self.lower.next() { if let Some(item) = item_opt.as_ref() { - return Some(&item.socket) + return Some(&item.socket); } } None @@ -214,7 +224,7 @@ impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> { fn next(&mut self) -> Option { while let Some(item_opt) = self.lower.next() { if let Some(item) = item_opt.as_mut() { - return Some(SocketRef::new(&mut item.socket)) + return Some(SocketRef::new(&mut item.socket)); } } None diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 35e60ebfa..dc8cceaa3 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2,17 +2,19 @@ // the parts of RFC 1122 that discuss TCP. Consult RFC 7414 when implementing // a new feature. -use core::{cmp, fmt, mem}; #[cfg(feature = "async")] use core::task::Waker; +use core::{cmp, fmt, mem}; -use crate::{Error, Result}; -use crate::time::{Duration, Instant}; -use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context}; -use crate::storage::{Assembler, RingBuffer}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl, TCP_HEADER_LEN}; +use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::storage::{Assembler, RingBuffer}; +use crate::time::{Duration, Instant}; +use crate::wire::{ + IpAddress, IpEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber, TCP_HEADER_LEN, +}; +use crate::{Error, Result}; /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -33,23 +35,23 @@ pub enum State { CloseWait, Closing, LastAck, - TimeWait + TimeWait, } impl fmt::Display for State { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - State::Closed => write!(f, "CLOSED"), - State::Listen => write!(f, "LISTEN"), - State::SynSent => write!(f, "SYN-SENT"), + State::Closed => write!(f, "CLOSED"), + State::Listen => write!(f, "LISTEN"), + State::SynSent => write!(f, "SYN-SENT"), State::SynReceived => write!(f, "SYN-RECEIVED"), State::Established => write!(f, "ESTABLISHED"), - State::FinWait1 => write!(f, "FIN-WAIT-1"), - State::FinWait2 => write!(f, "FIN-WAIT-2"), - State::CloseWait => write!(f, "CLOSE-WAIT"), - State::Closing => write!(f, "CLOSING"), - State::LastAck => write!(f, "LAST-ACK"), - State::TimeWait => write!(f, "TIME-WAIT") + State::FinWait1 => write!(f, "FIN-WAIT-1"), + State::FinWait2 => write!(f, "FIN-WAIT-2"), + State::CloseWait => write!(f, "CLOSE-WAIT"), + State::Closing => write!(f, "CLOSING"), + State::LastAck => write!(f, "LAST-ACK"), + State::TimeWait => write!(f, "TIME-WAIT"), } } } @@ -103,17 +105,27 @@ impl RttEstimator { fn sample(&mut self, new_rtt: u32) { // "Congestion Avoidance and Control", Van Jacobson, Michael J. Karels, 1988 self.rtt = (self.rtt * 7 + new_rtt + 7) / 8; - let diff = (self.rtt as i32 - new_rtt as i32 ).abs() as u32; + let diff = (self.rtt as i32 - new_rtt as i32).abs() as u32; self.deviation = (self.deviation * 3 + diff + 3) / 4; - + self.rto_count = 0; let rto = self.retransmission_timeout().millis(); - net_trace!("rtte: sample={:?} rtt={:?} dev={:?} rto={:?}", new_rtt, self.rtt, self.deviation, rto); + net_trace!( + "rtte: sample={:?} rtt={:?} dev={:?} rto={:?}", + new_rtt, + self.rtt, + self.deviation, + rto + ); } fn on_send(&mut self, timestamp: Instant, seq: TcpSeqNumber) { - if self.max_seq_sent.map(|max_seq_sent| seq > max_seq_sent).unwrap_or(true) { + if self + .max_seq_sent + .map(|max_seq_sent| seq > max_seq_sent) + .unwrap_or(true) + { self.max_seq_sent = Some(seq); if self.timestamp.is_none() { self.timestamp = Some((timestamp, seq)); @@ -145,9 +157,14 @@ impl RttEstimator { // all packets sent would incur a retransmit. To avoid this, force an estimate // increase if we see 3 consecutive retransmissions without any successful sample. self.rto_count = 0; - self.rtt = RTTE_MAX_RTO.min(self.rtt*2); + self.rtt = RTTE_MAX_RTO.min(self.rtt * 2); let rto = self.retransmission_timeout().millis(); - net_trace!("rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", self.rtt, self.deviation, rto); + net_trace!( + "rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", + self.rtt, + self.deviation, + rto + ); } } } @@ -160,59 +177,60 @@ enum Timer { }, Retransmit { expires_at: Instant, - delay: Duration + delay: Duration, }, FastRetransmit, Close { - expires_at: Instant - } + expires_at: Instant, + }, } const ACK_DELAY_DEFAULT: Duration = Duration { millis: 10 }; -const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; +const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; impl Default for Timer { fn default() -> Timer { - Timer::Idle { keep_alive_at: None } + Timer::Idle { + keep_alive_at: None, + } } } impl Timer { fn should_keep_alive(&self, timestamp: Instant) -> bool { match *self { - Timer::Idle { keep_alive_at: Some(keep_alive_at) } - if timestamp >= keep_alive_at => { - true - } - _ => false + Timer::Idle { + keep_alive_at: Some(keep_alive_at), + } if timestamp >= keep_alive_at => true, + _ => false, } } fn should_retransmit(&self, timestamp: Instant) -> Option { match *self { - Timer::Retransmit { expires_at, delay } - if timestamp >= expires_at => { + Timer::Retransmit { expires_at, delay } if timestamp >= expires_at => { Some(timestamp - expires_at + delay) - }, + } Timer::FastRetransmit => Some(Duration::from_millis(0)), - _ => None + _ => None, } } fn should_close(&self, timestamp: Instant) -> bool { match *self { - Timer::Close { expires_at } - if timestamp >= expires_at => { - true - } - _ => false + Timer::Close { expires_at } if timestamp >= expires_at => true, + _ => false, } } fn poll_at(&self) -> PollAt { match *self { - Timer::Idle { keep_alive_at: Some(keep_alive_at) } => PollAt::Time(keep_alive_at), - Timer::Idle { keep_alive_at: None } => PollAt::Ingress, + Timer::Idle { + keep_alive_at: Some(keep_alive_at), + } => PollAt::Time(keep_alive_at), + Timer::Idle { + keep_alive_at: None, + } => PollAt::Ingress, Timer::Retransmit { expires_at, .. } => PollAt::Time(expires_at), Timer::FastRetransmit => PollAt::Now, Timer::Close { expires_at } => PollAt::Time(expires_at), @@ -221,12 +239,15 @@ impl Timer { fn set_for_idle(&mut self, timestamp: Instant, interval: Option) { *self = Timer::Idle { - keep_alive_at: interval.map(|interval| timestamp + interval) + keep_alive_at: interval.map(|interval| timestamp + interval), } } fn set_keep_alive(&mut self) { - if let Timer::Idle { ref mut keep_alive_at } = *self { + if let Timer::Idle { + ref mut keep_alive_at, + } = *self + { if keep_alive_at.is_none() { *keep_alive_at = Some(Instant::from_millis(0)) } @@ -234,7 +255,10 @@ impl Timer { } fn rewind_keep_alive(&mut self, timestamp: Instant, interval: Option) { - if let Timer::Idle { ref mut keep_alive_at } = *self { + if let Timer::Idle { + ref mut keep_alive_at, + } = *self + { *keep_alive_at = interval.map(|interval| timestamp + interval) } } @@ -244,18 +268,17 @@ impl Timer { Timer::Idle { .. } | Timer::FastRetransmit { .. } => { *self = Timer::Retransmit { expires_at: timestamp + delay, - delay: delay, + delay: delay, } } - Timer::Retransmit { expires_at, delay } - if timestamp >= expires_at => { + Timer::Retransmit { expires_at, delay } if timestamp >= expires_at => { *self = Timer::Retransmit { expires_at: timestamp + delay, - delay: delay * 2 + delay: delay * 2, } } Timer::Retransmit { .. } => (), - Timer::Close { .. } => () + Timer::Close { .. } => (), } } @@ -265,13 +288,13 @@ impl Timer { fn set_for_close(&mut self, timestamp: Instant) { *self = Timer::Close { - expires_at: timestamp + CLOSE_DELAY + expires_at: timestamp + CLOSE_DELAY, } } fn is_retransmit(&self) -> bool { match *self { - Timer::Retransmit {..} | Timer::FastRetransmit => true, + Timer::Retransmit { .. } | Timer::FastRetransmit => true, _ => false, } } @@ -286,27 +309,27 @@ impl Timer { #[derive(Debug)] pub struct TcpSocket<'a> { pub(crate) meta: SocketMeta, - state: State, - timer: Timer, - rtte: RttEstimator, - assembler: Assembler, - rx_buffer: SocketBuffer<'a>, + state: State, + timer: Timer, + rtte: RttEstimator, + assembler: Assembler, + rx_buffer: SocketBuffer<'a>, rx_fin_received: bool, - tx_buffer: SocketBuffer<'a>, + tx_buffer: SocketBuffer<'a>, /// Interval after which, if no inbound packets are received, the connection is aborted. - timeout: Option, + timeout: Option, /// Interval at which keep-alive packets will be sent. - keep_alive: Option, + keep_alive: Option, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. - hop_limit: Option, + hop_limit: Option, /// Address passed to listen(). Listen address is set when listen() is called and /// used every time the socket is reset back to the LISTEN state. - listen_address: IpAddress, + listen_address: IpAddress, /// Current local endpoint. This is used for both filtering the incoming packets and /// setting the source address. When listening or initiating connection on/from /// an unspecified address, this field is updated with the chosen source address before /// any packets are sent. - local_endpoint: IpEndpoint, + local_endpoint: IpEndpoint, /// Current remote endpoint. This is used for both filtering the incoming packets and /// setting the destination address. If the remote endpoint is unspecified, it means that /// aborting the connection will not send an RST, and, in TIME-WAIT state, will not @@ -314,10 +337,10 @@ pub struct TcpSocket<'a> { remote_endpoint: IpEndpoint, /// The sequence number corresponding to the beginning of the transmit buffer. /// I.e. an ACK(local_seq_no+n) packet removes n bytes from the transmit buffer. - local_seq_no: TcpSeqNumber, + local_seq_no: TcpSeqNumber, /// The sequence number corresponding to the beginning of the receive buffer. /// I.e. userspace reading n bytes adds n to remote_seq_no. - remote_seq_no: TcpSeqNumber, + remote_seq_no: TcpSeqNumber, /// The last sequence number sent. /// I.e. in an idle socket, local_seq_no+tx_buffer.len(). remote_last_seq: TcpSeqNumber, @@ -331,15 +354,15 @@ pub struct TcpSocket<'a> { remote_win_shift: u8, /// The remote window size, relative to local_seq_no /// I.e. we're allowed to send octets until local_seq_no+remote_win_len - remote_win_len: usize, + remote_win_len: usize, /// The receive window scaling factor for remotes which support RFC 1323, None if unsupported. remote_win_scale: Option, /// Whether or not the remote supports selective ACK as described in RFC 2018. remote_has_sack: bool, /// The maximum number of data octets that the remote side may receive. - remote_mss: usize, + remote_mss: usize, /// The timestamp of the last packet received. - remote_last_ts: Option, + remote_last_ts: Option, /// The sequence number of the last packet recived, used for sACK local_rx_last_seq: Option, /// The ACK number of the last packet recived. @@ -349,7 +372,7 @@ pub struct TcpSocket<'a> { local_rx_dup_acks: u8, /// Duration for Delayed ACK. If None no ACKs will be delayed. - ack_delay: Option, + ack_delay: Option, /// Delayed ack timer. If set, packets containing exclusively /// ACK or window updates (ie, no data) won't be sent until expiry. ack_delay_until: Option, @@ -361,7 +384,6 @@ pub struct TcpSocket<'a> { rx_waker: WakerRegistration, #[cfg(feature = "async")] tx_waker: WakerRegistration, - } const DEFAULT_MSS: usize = 536; @@ -370,7 +392,9 @@ impl<'a> TcpSocket<'a> { #[allow(unused_comparisons)] // small usize platforms always pass rx_capacity check /// Create a socket using the given buffers. pub fn new(rx_buffer: T, tx_buffer: T) -> TcpSocket<'a> - where T: Into> { + where + T: Into>, + { let (rx_buffer, tx_buffer) = (rx_buffer.into(), tx_buffer.into()); let rx_capacity = rx_buffer.capacity(); @@ -381,39 +405,38 @@ impl<'a> TcpSocket<'a> { if rx_capacity > (1 << 30) { panic!("receiving buffer too large, cannot exceed 1 GiB") } - let rx_cap_log2 = mem::size_of::() * 8 - - rx_capacity.leading_zeros() as usize; + let rx_cap_log2 = mem::size_of::() * 8 - rx_capacity.leading_zeros() as usize; TcpSocket { - meta: SocketMeta::default(), - state: State::Closed, - timer: Timer::default(), - rtte: RttEstimator::default(), - assembler: Assembler::new(rx_buffer.capacity()), - tx_buffer: tx_buffer, - rx_buffer: rx_buffer, + meta: SocketMeta::default(), + state: State::Closed, + timer: Timer::default(), + rtte: RttEstimator::default(), + assembler: Assembler::new(rx_buffer.capacity()), + tx_buffer: tx_buffer, + rx_buffer: rx_buffer, rx_fin_received: false, - timeout: None, - keep_alive: None, - hop_limit: None, - listen_address: IpAddress::default(), - local_endpoint: IpEndpoint::default(), + timeout: None, + keep_alive: None, + hop_limit: None, + listen_address: IpAddress::default(), + local_endpoint: IpEndpoint::default(), remote_endpoint: IpEndpoint::default(), - local_seq_no: INITIAL_SEQ_NO, - remote_seq_no: TcpSeqNumber::default(), + local_seq_no: INITIAL_SEQ_NO, + remote_seq_no: TcpSeqNumber::default(), remote_last_seq: TcpSeqNumber::default(), remote_last_ack: None, remote_last_win: 0, - remote_win_len: 0, + remote_win_len: 0, remote_win_shift: rx_cap_log2.saturating_sub(16) as u8, remote_win_scale: None, remote_has_sack: false, - remote_mss: DEFAULT_MSS, - remote_last_ts: None, + remote_mss: DEFAULT_MSS, + remote_last_ts: None, local_rx_last_ack: None, local_rx_last_seq: None, local_rx_dup_acks: 0, - ack_delay: Some(ACK_DELAY_DEFAULT), + ack_delay: Some(ACK_DELAY_DEFAULT), ack_delay_until: None, nagle: true, @@ -428,12 +451,12 @@ impl<'a> TcpSocket<'a> { /// /// The waker is woken on state changes that might affect the return value /// of `recv` method calls, such as receiving data, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has /// necessarily changed. #[cfg(feature = "async")] @@ -446,12 +469,12 @@ impl<'a> TcpSocket<'a> { /// The waker is woken on state changes that might affect the return value /// of `send` method calls, such as space becoming available in the transmit /// buffer, or the socket closing. - /// + /// /// Notes: /// /// - Only one waker can be registered at a time. If another waker was previously registered, /// it is overwritten and will no longer be woken. - /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has /// necessarily changed. #[cfg(feature = "async")] @@ -492,8 +515,10 @@ impl<'a> TcpSocket<'a> { /// #[inline] fn scaled_window(&self) -> u16 { - cmp::min(self.rx_buffer.window() >> self.remote_win_shift as usize, - (1 << 16) - 1) as u16 + cmp::min( + self.rx_buffer.window() >> self.remote_win_shift as usize, + (1 << 16) - 1, + ) as u16 } /// Set the timeout duration. @@ -520,10 +545,10 @@ impl<'a> TcpSocket<'a> { /// Enable or disable Nagle's Algorithm. /// - /// Also known as "tinygram prevention". By default, it is enabled. + /// Also known as "tinygram prevention". By default, it is enabled. /// Disabling it is equivalent to Linux's TCP_NODELAY flag. /// - /// When enabled, Nagle's Algorithm prevents sending segments smaller than MSS if + /// When enabled, Nagle's Algorithm prevents sending segments smaller than MSS if /// there is data in flight (sent but not acknowledged). In other words, it ensures /// at most only one segment smaller than MSS is in flight at a time. /// @@ -608,33 +633,33 @@ impl<'a> TcpSocket<'a> { } fn reset(&mut self) { - let rx_cap_log2 = mem::size_of::() * 8 - - self.rx_buffer.capacity().leading_zeros() as usize; + let rx_cap_log2 = + mem::size_of::() * 8 - self.rx_buffer.capacity().leading_zeros() as usize; - self.state = State::Closed; - self.timer = Timer::default(); - self.rtte = RttEstimator::default(); - self.assembler = Assembler::new(self.rx_buffer.capacity()); + self.state = State::Closed; + self.timer = Timer::default(); + self.rtte = RttEstimator::default(); + self.assembler = Assembler::new(self.rx_buffer.capacity()); self.tx_buffer.clear(); self.rx_buffer.clear(); self.rx_fin_received = false; - self.keep_alive = None; - self.timeout = None; - self.hop_limit = None; - self.listen_address = IpAddress::default(); - self.local_endpoint = IpEndpoint::default(); + self.keep_alive = None; + self.timeout = None; + self.hop_limit = None; + self.listen_address = IpAddress::default(); + self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); - self.local_seq_no = INITIAL_SEQ_NO; - self.remote_seq_no = TcpSeqNumber::default(); + self.local_seq_no = INITIAL_SEQ_NO; + self.remote_seq_no = TcpSeqNumber::default(); self.remote_last_seq = TcpSeqNumber::default(); self.remote_last_ack = None; self.remote_last_win = 0; - self.remote_win_len = 0; + self.remote_win_len = 0; self.remote_win_scale = None; self.remote_win_shift = rx_cap_log2.saturating_sub(16) as u8; - self.remote_mss = DEFAULT_MSS; - self.remote_last_ts = None; - self.ack_delay = Some(ACK_DELAY_DEFAULT); + self.remote_mss = DEFAULT_MSS; + self.remote_last_ts = None; + self.ack_delay = Some(ACK_DELAY_DEFAULT); self.ack_delay_until = None; self.nagle = true; @@ -651,15 +676,21 @@ impl<'a> TcpSocket<'a> { /// (see [is_open](#method.is_open)), and `Err(Error::Unaddressable)` /// if the port in the given endpoint is zero. pub fn listen(&mut self, local_endpoint: T) -> Result<()> - where T: Into { + where + T: Into, + { let local_endpoint = local_endpoint.into(); - if local_endpoint.port == 0 { return Err(Error::Unaddressable) } + if local_endpoint.port == 0 { + return Err(Error::Unaddressable); + } - if self.is_open() { return Err(Error::Illegal) } + if self.is_open() { + return Err(Error::Illegal); + } self.reset(); - self.listen_address = local_endpoint.addr; - self.local_endpoint = local_endpoint; + self.listen_address = local_endpoint.addr; + self.local_endpoint = local_endpoint; self.remote_endpoint = IpEndpoint::default(); self.set_state(State::Listen); Ok(()) @@ -680,13 +711,22 @@ impl<'a> TcpSocket<'a> { /// It also returns an error if the local or remote port is zero, or if the remote address /// is unspecified. pub fn connect(&mut self, remote_endpoint: T, local_endpoint: U) -> Result<()> - where T: Into, U: Into { + where + T: Into, + U: Into, + { let remote_endpoint = remote_endpoint.into(); - let local_endpoint = local_endpoint.into(); + let local_endpoint = local_endpoint.into(); - if self.is_open() { return Err(Error::Illegal) } - if !remote_endpoint.is_specified() { return Err(Error::Unaddressable) } - if local_endpoint.port == 0 { return Err(Error::Unaddressable) } + if self.is_open() { + return Err(Error::Illegal); + } + if !remote_endpoint.is_specified() { + return Err(Error::Unaddressable); + } + if local_endpoint.port == 0 { + return Err(Error::Unaddressable); + } // If local address is not provided, use an unspecified address but a specified protocol. // This lets us lower IpRepr later to determine IP header size and calculate MSS, @@ -695,15 +735,18 @@ impl<'a> TcpSocket<'a> { IpAddress::Unspecified => remote_endpoint.addr.to_unspecified(), ip => ip, }; - let local_endpoint = IpEndpoint { addr: local_addr, ..local_endpoint }; + let local_endpoint = IpEndpoint { + addr: local_addr, + ..local_endpoint + }; // Carry over the local sequence number. let local_seq_no = self.local_seq_no; self.reset(); - self.local_endpoint = local_endpoint; + self.local_endpoint = local_endpoint; self.remote_endpoint = remote_endpoint; - self.local_seq_no = local_seq_no; + self.local_seq_no = local_seq_no; self.remote_last_seq = local_seq_no; self.set_state(State::SynSent); Ok(()) @@ -717,23 +760,23 @@ impl<'a> TcpSocket<'a> { pub fn close(&mut self) { match self.state { // In the LISTEN state there is no established connection. - State::Listen => - self.set_state(State::Closed), + State::Listen => self.set_state(State::Closed), // In the SYN-SENT state the remote endpoint is not yet synchronized and, upon // receiving an RST, will abort the connection. - State::SynSent => - self.set_state(State::Closed), + State::SynSent => self.set_state(State::Closed), // In the SYN-RECEIVED, ESTABLISHED and CLOSE-WAIT states the transmit half // of the connection is open, and needs to be explicitly closed with a FIN. - State::SynReceived | State::Established => - self.set_state(State::FinWait1), - State::CloseWait => - self.set_state(State::LastAck), + State::SynReceived | State::Established => self.set_state(State::FinWait1), + State::CloseWait => self.set_state(State::LastAck), // In the FIN-WAIT-1, FIN-WAIT-2, CLOSING, LAST-ACK, TIME-WAIT and CLOSED states, // the transmit half of the connection is already closed, and no further // action is needed. - State::FinWait1 | State::FinWait2 | State::Closing | - State::TimeWait | State::LastAck | State::Closed => () + State::FinWait1 + | State::FinWait2 + | State::Closing + | State::TimeWait + | State::LastAck + | State::Closed => (), } } @@ -755,7 +798,7 @@ impl<'a> TcpSocket<'a> { pub fn is_listening(&self) -> bool { match self.state { State::Listen => true, - _ => false + _ => false, } } @@ -772,7 +815,7 @@ impl<'a> TcpSocket<'a> { match self.state { State::Closed => false, State::TimeWait => false, - _ => true + _ => true, } } @@ -794,7 +837,7 @@ impl<'a> TcpSocket<'a> { State::Closed => false, State::TimeWait => false, State::Listen => false, - _ => true + _ => true, } } @@ -814,7 +857,7 @@ impl<'a> TcpSocket<'a> { // In CLOSE-WAIT, the remote endpoint has closed our receive half of the connection // but we still can transmit indefinitely. State::CloseWait => true, - _ => false + _ => false, } } @@ -835,7 +878,7 @@ impl<'a> TcpSocket<'a> { State::FinWait1 | State::FinWait2 => true, // If we have something in the receive buffer, we can receive that. _ if !self.rx_buffer.is_empty() => true, - _ => false + _ => false, } } @@ -843,7 +886,9 @@ impl<'a> TcpSocket<'a> { /// (see [may_send](#method.may_send), and the transmit buffer is not full. #[inline] pub fn can_send(&self) -> bool { - if !self.may_send() { return false } + if !self.may_send() { + return false; + } !self.tx_buffer.is_full() } @@ -864,27 +909,40 @@ impl<'a> TcpSocket<'a> { /// (see [may_recv](#method.may_recv), and the receive buffer is not empty. #[inline] pub fn can_recv(&self) -> bool { - if !self.may_recv() { return false } + if !self.may_recv() { + return false; + } !self.rx_buffer.is_empty() } fn send_impl<'b, F, R>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R) { - if !self.may_send() { return Err(Error::Illegal) } + where + F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R), + { + if !self.may_send() { + return Err(Error::Illegal); + } // The connection might have been idle for a long time, and so remote_last_ts // would be far in the past. Unless we clear it here, we'll abort the connection // down over in dispatch() by erroneously detecting it as timed out. - if self.tx_buffer.is_empty() { self.remote_last_ts = None } + if self.tx_buffer.is_empty() { + self.remote_last_ts = None + } let _old_length = self.tx_buffer.len(); let (size, result) = f(&mut self.tx_buffer); if size > 0 { #[cfg(any(test, feature = "verbose"))] - net_trace!("{}:{}:{}: tx buffer: enqueueing {} octets (now {})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - size, _old_length + size); + net_trace!( + "{}:{}:{}: tx buffer: enqueueing {} octets (now {})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + size, + _old_length + size + ); } Ok(result) } @@ -895,10 +953,10 @@ impl<'a> TcpSocket<'a> { /// This function returns `Err(Error::Illegal)` if the transmit half of /// the connection is not open; see [may_send](#method.may_send). pub fn send<'b, F, R>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut [u8]) -> (usize, R) { - self.send_impl(|tx_buffer| { - tx_buffer.enqueue_many_with(f) - }) + where + F: FnOnce(&'b mut [u8]) -> (usize, R), + { + self.send_impl(|tx_buffer| tx_buffer.enqueue_many_with(f)) } /// Enqueue a sequence of octets to be sent, and fill it from a slice. @@ -920,16 +978,18 @@ impl<'a> TcpSocket<'a> { // another (stale) SYN. (We do not support TCP Fast Open.) if !self.may_recv() { if self.rx_fin_received { - return Err(Error::Finished) + return Err(Error::Finished); } - return Err(Error::Illegal) + return Err(Error::Illegal); } Ok(()) } fn recv_impl<'b, F, R>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R) { + where + F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R), + { self.recv_error_check()?; let _old_length = self.rx_buffer.len(); @@ -937,9 +997,14 @@ impl<'a> TcpSocket<'a> { self.remote_seq_no += size; if size > 0 { #[cfg(any(test, feature = "verbose"))] - net_trace!("{}:{}:{}: rx buffer: dequeueing {} octets (now {})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - size, _old_length - size); + net_trace!( + "{}:{}:{}: rx buffer: dequeueing {} octets (now {})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + size, + _old_length - size + ); } Ok(result) } @@ -955,10 +1020,10 @@ impl<'a> TcpSocket<'a> { /// In all other cases, `Err(Error::Illegal)` is returned and previously received data (if any) /// may be incomplete (truncated). pub fn recv<'b, F, R>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut [u8]) -> (usize, R) { - self.recv_impl(|rx_buffer| { - rx_buffer.dequeue_many_with(f) - }) + where + F: FnOnce(&'b mut [u8]) -> (usize, R), + { + self.recv_impl(|rx_buffer| rx_buffer.dequeue_many_with(f)) } /// Dequeue a sequence of received octets, and fill a slice from it. @@ -984,9 +1049,13 @@ impl<'a> TcpSocket<'a> { let buffer = self.rx_buffer.get_allocated(0, size); if !buffer.is_empty() { #[cfg(any(test, feature = "verbose"))] - net_trace!("{}:{}:{}: rx buffer: peeking at {} octets", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - buffer.len()); + net_trace!( + "{}:{}:{}: rx buffer: peeking at {} octets", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + buffer.len() + ); } Ok(buffer) } @@ -1021,13 +1090,22 @@ impl<'a> TcpSocket<'a> { fn set_state(&mut self, state: State) { if self.state != state { if self.remote_endpoint.addr.is_unspecified() { - net_trace!("{}:{}: state={}=>{}", - self.meta.handle, self.local_endpoint, - self.state, state); + net_trace!( + "{}:{}: state={}=>{}", + self.meta.handle, + self.local_endpoint, + self.state, + state + ); } else { - net_trace!("{}:{}:{}: state={}=>{}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - self.state, state); + net_trace!( + "{}:{}:{}: state={}=>{}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + self.state, + state + ); } } @@ -1045,24 +1123,24 @@ impl<'a> TcpSocket<'a> { pub(crate) fn reply(ip_repr: &IpRepr, repr: &TcpRepr) -> (IpRepr, TcpRepr<'static>) { let reply_repr = TcpRepr { - src_port: repr.dst_port, - dst_port: repr.src_port, - control: TcpControl::None, - seq_number: TcpSeqNumber(0), - ack_number: None, - window_len: 0, + src_port: repr.dst_port, + dst_port: repr.src_port, + control: TcpControl::None, + seq_number: TcpSeqNumber(0), + ack_number: None, + window_len: 0, window_scale: None, max_seg_size: None, sack_permitted: false, - sack_ranges: [None, None, None], - payload: &[] + sack_ranges: [None, None, None], + payload: &[], }; let ip_reply_repr = IpRepr::Unspecified { - src_addr: ip_repr.dst_addr(), - dst_addr: ip_repr.src_addr(), - protocol: IpProtocol::Tcp, + src_addr: ip_repr.dst_addr(), + dst_addr: ip_repr.src_addr(), + protocol: IpProtocol::Tcp, payload_len: reply_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; (ip_reply_repr, reply_repr) } @@ -1112,8 +1190,9 @@ impl<'a> TcpSocket<'a> { reply_repr.sack_ranges[0] = None; if let Some(last_seg_seq) = self.local_rx_last_seq.map(|s| s.0 as u32) { - reply_repr.sack_ranges[0] = self.assembler.iter_data( - reply_repr.ack_number.map(|s| s.0 as usize).unwrap_or(0)) + reply_repr.sack_ranges[0] = self + .assembler + .iter_data(reply_repr.ack_number.map(|s| s.0 as usize).unwrap_or(0)) .map(|(left, right)| (left as u32, right as u32)) .find(|(left, right)| *left <= last_seg_seq && *right >= last_seg_seq); } @@ -1126,8 +1205,9 @@ impl<'a> TcpSocket<'a> { // through those, that is currently infeasable. Instead, we offer the range with // the lowest sequence number (if one exists) to hint at what segments would // most quickly advance the acknowledgement number. - reply_repr.sack_ranges[0] = self.assembler.iter_data( - reply_repr.ack_number.map(|s| s.0 as usize).unwrap_or(0)) + reply_repr.sack_ranges[0] = self + .assembler + .iter_data(reply_repr.ack_number.map(|s| s.0 as usize).unwrap_or(0)) .map(|(left, right)| (left as u32, right as u32)) .next(); } @@ -1139,29 +1219,46 @@ impl<'a> TcpSocket<'a> { } pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &TcpRepr) -> bool { - if self.state == State::Closed { return false } + if self.state == State::Closed { + return false; + } // If we're still listening for SYNs and the packet has an ACK, it cannot // be destined to this socket, but another one may well listen on the same // local endpoint. - if self.state == State::Listen && repr.ack_number.is_some() { return false } + if self.state == State::Listen && repr.ack_number.is_some() { + return false; + } // Reject packets with a wrong destination. - if self.local_endpoint.port != repr.dst_port { return false } - if !self.local_endpoint.addr.is_unspecified() && - self.local_endpoint.addr != ip_repr.dst_addr() { return false } + if self.local_endpoint.port != repr.dst_port { + return false; + } + if !self.local_endpoint.addr.is_unspecified() + && self.local_endpoint.addr != ip_repr.dst_addr() + { + return false; + } // Reject packets from a source to which we aren't connected. - if self.remote_endpoint.port != 0 && - self.remote_endpoint.port != repr.src_port { return false } - if !self.remote_endpoint.addr.is_unspecified() && - self.remote_endpoint.addr != ip_repr.src_addr() { return false } + if self.remote_endpoint.port != 0 && self.remote_endpoint.port != repr.src_port { + return false; + } + if !self.remote_endpoint.addr.is_unspecified() + && self.remote_endpoint.addr != ip_repr.src_addr() + { + return false; + } true } - pub(crate) fn process(&mut self, cx: &Context, ip_repr: &IpRepr, repr: &TcpRepr) -> - Result)>> { + pub(crate) fn process( + &mut self, + cx: &Context, + ip_repr: &IpRepr, + repr: &TcpRepr, + ) -> Result)>> { debug_assert!(self.accepts(ip_repr, repr)); // Consider how much the sequence number space differs from the transmit buffer space. @@ -1172,7 +1269,7 @@ impl<'a> TcpSocket<'a> { State::FinWait1 | State::LastAck | State::Closing => (false, true), // In all other states we've already got acknowledgemetns for // all of the control flags we sent. - _ => (false, false) + _ => (false, false), }; let control_len = (sent_syn as usize) + (sent_fin as usize); @@ -1180,56 +1277,124 @@ impl<'a> TcpSocket<'a> { match (self.state, repr) { // An RST received in response to initial SYN is acceptable if it acknowledges // the initial SYN. - (State::SynSent, &TcpRepr { - control: TcpControl::Rst, ack_number: None, .. - }) => { - net_debug!("{}:{}:{}: unacceptable RST (expecting RST|ACK) \ + ( + State::SynSent, + &TcpRepr { + control: TcpControl::Rst, + ack_number: None, + .. + }, + ) => { + net_debug!( + "{}:{}:{}: unacceptable RST (expecting RST|ACK) \ in response to initial SYN", - self.meta.handle, self.local_endpoint, self.remote_endpoint); - return Err(Error::Dropped) + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Err(Error::Dropped); } - (State::SynSent, &TcpRepr { - control: TcpControl::Rst, ack_number: Some(ack_number), .. - }) => { + ( + State::SynSent, + &TcpRepr { + control: TcpControl::Rst, + ack_number: Some(ack_number), + .. + }, + ) => { if ack_number != self.local_seq_no + 1 { - net_debug!("{}:{}:{}: unacceptable RST|ACK in response to initial SYN", - self.meta.handle, self.local_endpoint, self.remote_endpoint); - return Err(Error::Dropped) + net_debug!( + "{}:{}:{}: unacceptable RST|ACK in response to initial SYN", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Err(Error::Dropped); } } // Any other RST need only have a valid sequence number. - (_, &TcpRepr { control: TcpControl::Rst, .. }) => (), + ( + _, + &TcpRepr { + control: TcpControl::Rst, + .. + }, + ) => (), // The initial SYN cannot contain an acknowledgement. - (State::Listen, &TcpRepr { ack_number: None, .. }) => (), + ( + State::Listen, + &TcpRepr { + ack_number: None, .. + }, + ) => (), // This case is handled above. - (State::Listen, &TcpRepr { ack_number: Some(_), .. }) => unreachable!(), + ( + State::Listen, + &TcpRepr { + ack_number: Some(_), + .. + }, + ) => unreachable!(), // Every packet after the initial SYN must be an acknowledgement. - (_, &TcpRepr { ack_number: None, .. }) => { - net_debug!("{}:{}:{}: expecting an ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint); - return Err(Error::Dropped) + ( + _, + &TcpRepr { + ack_number: None, .. + }, + ) => { + net_debug!( + "{}:{}:{}: expecting an ACK", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Err(Error::Dropped); } // Any ACK in the SYN-SENT state must have the SYN flag set. - (State::SynSent, &TcpRepr { - control: TcpControl::None, ack_number: Some(_), .. - }) => { - net_debug!("{}:{}:{}: expecting a SYN|ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + ( + State::SynSent, + &TcpRepr { + control: TcpControl::None, + ack_number: Some(_), + .. + }, + ) => { + net_debug!( + "{}:{}:{}: expecting a SYN|ACK", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); self.abort(); - return Err(Error::Dropped) + return Err(Error::Dropped); } // SYN|ACK in the SYN-SENT state must have the exact ACK number. - (State::SynSent, &TcpRepr { - control: TcpControl::Syn, ack_number: Some(ack_number), .. - }) => { + ( + State::SynSent, + &TcpRepr { + control: TcpControl::Syn, + ack_number: Some(ack_number), + .. + }, + ) => { if ack_number != self.local_seq_no + 1 { - net_debug!("{}:{}:{}: unacceptable SYN|ACK in response to initial SYN", - self.meta.handle, self.local_endpoint, self.remote_endpoint); - return Err(Error::Dropped) + net_debug!( + "{}:{}:{}: unacceptable SYN|ACK in response to initial SYN", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Err(Error::Dropped); } } // Every acknowledgement must be for transmitted but unacknowledged data. - (_, &TcpRepr { ack_number: Some(ack_number), .. }) => { + ( + _, + &TcpRepr { + ack_number: Some(ack_number), + .. + }, + ) => { let unacknowledged = self.tx_buffer.len() + control_len; // Acceptable ACK range (both inclusive) @@ -1237,53 +1402,80 @@ impl<'a> TcpSocket<'a> { let ack_max = self.local_seq_no + unacknowledged; if ack_number < ack_min { - net_debug!("{}:{}:{}: duplicate ACK ({} not in {}...{})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - ack_number, ack_min, ack_max); - return Err(Error::Dropped) + net_debug!( + "{}:{}:{}: duplicate ACK ({} not in {}...{})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + ack_number, + ack_min, + ack_max + ); + return Err(Error::Dropped); } if ack_number > ack_max { - net_debug!("{}:{}:{}: unacceptable ACK ({} not in {}...{})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - ack_number, ack_min, ack_max); - return Ok(Some(self.ack_reply(ip_repr, &repr))) + net_debug!( + "{}:{}:{}: unacceptable ACK ({} not in {}...{})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + ack_number, + ack_min, + ack_max + ); + return Ok(Some(self.ack_reply(ip_repr, &repr))); } } } - let window_start = self.remote_seq_no + self.rx_buffer.len(); - let window_end = self.remote_seq_no + self.rx_buffer.capacity(); + let window_start = self.remote_seq_no + self.rx_buffer.len(); + let window_end = self.remote_seq_no + self.rx_buffer.capacity(); let segment_start = repr.seq_number; - let segment_end = repr.seq_number + repr.segment_len(); + let segment_end = repr.seq_number + repr.segment_len(); let payload_offset; match self.state { // In LISTEN and SYN-SENT states, we have not yet synchronized with the remote end. - State::Listen | State::SynSent => - payload_offset = 0, + State::Listen | State::SynSent => payload_offset = 0, // In all other states, segments must occupy a valid portion of the receive window. _ => { let mut segment_in_window = true; if window_start == window_end && segment_start != segment_end { - net_debug!("{}:{}:{}: non-zero-length segment with zero receive window, \ + net_debug!( + "{}:{}:{}: non-zero-length segment with zero receive window, \ will only send an ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); segment_in_window = false; } if segment_start == segment_end && segment_end == window_start - 1 { - net_debug!("{}:{}:{}: received a keep-alive or window probe packet, \ + net_debug!( + "{}:{}:{}: received a keep-alive or window probe packet, \ will send an ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); segment_in_window = false; - } else if !((window_start <= segment_start && segment_start <= window_end) && - (window_start <= segment_end && segment_end <= window_end)) { - net_debug!("{}:{}:{}: segment not in receive window \ + } else if !((window_start <= segment_start && segment_start <= window_end) + && (window_start <= segment_end && segment_end <= window_end)) + { + net_debug!( + "{}:{}:{}: segment not in receive window \ ({}..{} not intersecting {}..{}), will send challenge ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - segment_start, segment_end, window_start, window_end); + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + segment_start, + segment_end, + window_start, + window_end + ); segment_in_window = false; } @@ -1298,7 +1490,7 @@ impl<'a> TcpSocket<'a> { self.timer.set_for_close(cx.now); } - return Ok(Some(self.ack_reply(ip_repr, &repr))) + return Ok(Some(self.ack_reply(ip_repr, &repr))); } } } @@ -1319,8 +1511,12 @@ impl<'a> TcpSocket<'a> { // space if all of that data is acknowledged. if sent_fin && self.tx_buffer.len() + 1 == ack_len { ack_len -= 1; - net_trace!("{}:{}:{}: received ACK of FIN", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_trace!( + "{}:{}:{}: received ACK of FIN", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); ack_of_fin = true; } @@ -1341,38 +1537,44 @@ impl<'a> TcpSocket<'a> { // Validate and update the state. match (self.state, control) { // RSTs are not accepted in the LISTEN state. - (State::Listen, TcpControl::Rst) => - return Err(Error::Dropped), + (State::Listen, TcpControl::Rst) => return Err(Error::Dropped), // RSTs in SYN-RECEIVED flip the socket back to the LISTEN state. (State::SynReceived, TcpControl::Rst) => { - net_trace!("{}:{}:{}: received RST", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_trace!( + "{}:{}:{}: received RST", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); self.local_endpoint.addr = self.listen_address; - self.remote_endpoint = IpEndpoint::default(); + self.remote_endpoint = IpEndpoint::default(); self.set_state(State::Listen); - return Ok(None) + return Ok(None); } // RSTs in any other state close the socket. (_, TcpControl::Rst) => { - net_trace!("{}:{}:{}: received RST", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_trace!( + "{}:{}:{}: received RST", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); self.set_state(State::Closed); - self.local_endpoint = IpEndpoint::default(); + self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); - return Ok(None) + return Ok(None); } // SYN packets in the LISTEN state change it to SYN-RECEIVED. (State::Listen, TcpControl::Syn) => { - net_trace!("{}:{}: received SYN", - self.meta.handle, self.local_endpoint); - self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); + net_trace!("{}:{}: received SYN", self.meta.handle, self.local_endpoint); + self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); // FIXME: use something more secure here - self.local_seq_no = TcpSeqNumber(-repr.seq_number.0); - self.remote_seq_no = repr.seq_number + 1; + self.local_seq_no = TcpSeqNumber(-repr.seq_number.0); + self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; self.remote_has_sack = repr.sack_permitted; if let Some(max_seg_size) = repr.max_seg_size { @@ -1397,7 +1599,7 @@ impl<'a> TcpSocket<'a> { // It's not obvious from RFC 793 that this is permitted, but // 7th and 8th steps in the "SEGMENT ARRIVES" event describe this behavior. (State::SynReceived, TcpControl::Fin) => { - self.remote_seq_no += 1; + self.remote_seq_no += 1; self.rx_fin_received = true; self.set_state(State::CloseWait); self.timer.set_for_idle(cx.now, self.keep_alive); @@ -1405,10 +1607,14 @@ impl<'a> TcpSocket<'a> { // SYN|ACK packets in the SYN-SENT state change it to ESTABLISHED. (State::SynSent, TcpControl::Syn) => { - net_trace!("{}:{}:{}: received SYN|ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint); - self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); - self.remote_seq_no = repr.seq_number + 1; + net_trace!( + "{}:{}:{}: received SYN|ACK", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); + self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no + 1; self.remote_last_ack = Some(repr.seq_number); self.remote_win_scale = repr.window_scale; @@ -1430,11 +1636,11 @@ impl<'a> TcpSocket<'a> { if !self.timer.is_retransmit() || ack_len != 0 { self.timer.set_for_idle(cx.now, self.keep_alive); } - }, + } // FIN packets in ESTABLISHED state indicate the remote side has closed. (State::Established, TcpControl::Fin) => { - self.remote_seq_no += 1; + self.remote_seq_no += 1; self.rx_fin_received = true; self.set_state(State::CloseWait); self.timer.set_for_idle(cx.now, self.keep_alive); @@ -1452,7 +1658,7 @@ impl<'a> TcpSocket<'a> { // FIN packets in FIN-WAIT-1 state change it to CLOSING, or to TIME-WAIT // if they also acknowledge our FIN. (State::FinWait1, TcpControl::Fin) => { - self.remote_seq_no += 1; + self.remote_seq_no += 1; self.rx_fin_received = true; if ack_of_fin { self.set_state(State::TimeWait); @@ -1470,7 +1676,7 @@ impl<'a> TcpSocket<'a> { // FIN packets in FIN-WAIT-2 state change it to TIME-WAIT. (State::FinWait2, TcpControl::Fin) => { - self.remote_seq_no += 1; + self.remote_seq_no += 1; self.rx_fin_received = true; self.set_state(State::TimeWait); self.timer.set_for_close(cx.now); @@ -1496,7 +1702,7 @@ impl<'a> TcpSocket<'a> { if ack_of_fin { // Clear the remote endpoint, or we'll send an RST there. self.set_state(State::Closed); - self.local_endpoint = IpEndpoint::default(); + self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); } else { self.timer.set_for_idle(cx.now, self.keep_alive); @@ -1504,9 +1710,14 @@ impl<'a> TcpSocket<'a> { } _ => { - net_debug!("{}:{}:{}: unexpected packet {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, repr); - return Err(Error::Dropped) + net_debug!( + "{}:{}:{}: unexpected packet {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + repr + ); + return Err(Error::Dropped); } } @@ -1524,9 +1735,14 @@ impl<'a> TcpSocket<'a> { if ack_len > 0 { // Dequeue acknowledged octets. debug_assert!(self.tx_buffer.len() >= ack_len); - net_trace!("{}:{}:{}: tx buffer: dequeueing {} octets (now {})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - ack_len, self.tx_buffer.len() - ack_len); + net_trace!( + "{}:{}:{}: tx buffer: dequeueing {} octets (now {})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + ack_len, + self.tx_buffer.len() - ack_len + ); self.tx_buffer.dequeue_allocated(ack_len); // There's new room available in tx_buffer, wake the waiting task if any. @@ -1546,29 +1762,48 @@ impl<'a> TcpSocket<'a> { // Duplicate ACK if payload empty and ACK doesn't move send window -> // Increment duplicate ACK count and set for retransmit if we just recived // the third duplicate ACK - Some(ref last_rx_ack) if - repr.payload.is_empty() && - *last_rx_ack == ack_number && - ack_number < self.remote_last_seq => { + Some(ref last_rx_ack) + if repr.payload.is_empty() + && *last_rx_ack == ack_number + && ack_number < self.remote_last_seq => + { // Increment duplicate ACK count self.local_rx_dup_acks = self.local_rx_dup_acks.saturating_add(1); - net_debug!("{}:{}:{}: received duplicate ACK for seq {} (duplicate nr {}{})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, ack_number, - self.local_rx_dup_acks, if self.local_rx_dup_acks == u8::max_value() { "+" } else { "" }); + net_debug!( + "{}:{}:{}: received duplicate ACK for seq {} (duplicate nr {}{})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + ack_number, + self.local_rx_dup_acks, + if self.local_rx_dup_acks == u8::max_value() { + "+" + } else { + "" + } + ); if self.local_rx_dup_acks == 3 { self.timer.set_for_fast_retransmit(); - net_debug!("{}:{}:{}: started fast retransmit", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_debug!( + "{}:{}:{}: started fast retransmit", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } - }, + } // No duplicate ACK -> Reset state and update last recived ACK _ => { if self.local_rx_dup_acks > 0 { self.local_rx_dup_acks = 0; - net_debug!("{}:{}:{}: reset duplicate ACK count", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_debug!( + "{}:{}:{}: reset duplicate ACK count", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } self.local_rx_last_ack = Some(ack_number); } @@ -1587,7 +1822,9 @@ impl<'a> TcpSocket<'a> { } let payload_len = repr.payload.len(); - if payload_len == 0 { return Ok(None) } + if payload_len == 0 { + return Ok(None); + } let assembler_was_empty = self.assembler.is_empty(); @@ -1596,25 +1833,41 @@ impl<'a> TcpSocket<'a> { Ok(()) => { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. - net_trace!("{}:{}:{}: rx buffer: receiving {} octets at offset {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - payload_len, payload_offset); - self.rx_buffer.write_unallocated(payload_offset, repr.payload); + net_trace!( + "{}:{}:{}: rx buffer: receiving {} octets at offset {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + payload_len, + payload_offset + ); + self.rx_buffer + .write_unallocated(payload_offset, repr.payload); } Err(_) => { - net_debug!("{}:{}:{}: assembler: too many holes to add {} octets at offset {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - payload_len, payload_offset); - return Err(Error::Dropped) + net_debug!( + "{}:{}:{}: assembler: too many holes to add {} octets at offset {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + payload_len, + payload_offset + ); + return Err(Error::Dropped); } } if let Some(contig_len) = self.assembler.remove_front() { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Enqueue the contiguous data octets in front of the buffer. - net_trace!("{}:{}:{}: rx buffer: enqueueing {} octets (now {})", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - contig_len, self.rx_buffer.len() + contig_len); + net_trace!( + "{}:{}:{}: rx buffer: enqueueing {} octets (now {})", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + contig_len, + self.rx_buffer.len() + contig_len + ); self.rx_buffer.enqueue_unallocated(contig_len); // There's new data in rx_buffer, notify waiting task if any. @@ -1624,9 +1877,13 @@ impl<'a> TcpSocket<'a> { if !self.assembler.is_empty() { // Print the ranges recorded in the assembler. - net_trace!("{}:{}:{}: assembler: {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - self.assembler); + net_trace!( + "{}:{}:{}: assembler: {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + self.assembler + ); } // Handle delayed acks @@ -1634,8 +1891,11 @@ impl<'a> TcpSocket<'a> { if self.ack_to_transmit() || self.window_to_update() { self.ack_delay_until = match self.ack_delay_until { None => { - net_trace!("{}:{}:{}: starting delayed ack timer", - self.meta.handle, self.local_endpoint, self.remote_endpoint + net_trace!( + "{}:{}:{}: starting delayed ack timer", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint ); Some(cx.now + ack_delay) @@ -1644,8 +1904,11 @@ impl<'a> TcpSocket<'a> { // for at least every second segment". // For now, we send an ACK every second received packet, full-sized or not. Some(_) => { - net_trace!("{}:{}:{}: delayed ack timer already started, forcing expiry", - self.meta.handle, self.local_endpoint, self.remote_endpoint + net_trace!( + "{}:{}:{}: delayed ack timer already started, forcing expiry", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint ); None } @@ -1660,8 +1923,12 @@ impl<'a> TcpSocket<'a> { // Note that we change the transmitter state here. // This is fine because smoltcp assumes that it can always transmit zero or one // packets for every packet it receives. - net_trace!("{}:{}:{}: ACKing incoming segment", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_trace!( + "{}:{}:{}: ACKing incoming segment", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); Ok(Some(self.ack_reply(ip_repr, &repr))) } else { Ok(None) @@ -1670,10 +1937,8 @@ impl<'a> TcpSocket<'a> { fn timed_out(&self, timestamp: Instant) -> bool { match (self.remote_last_ts, self.timeout) { - (Some(remote_last_ts), Some(timeout)) => - timestamp >= remote_last_ts + timeout, - (_, _) => - false + (Some(remote_last_ts), Some(timeout)) => timestamp >= remote_last_ts + timeout, + (_, _) => false, } } @@ -1696,7 +1961,8 @@ impl<'a> TcpSocket<'a> { let data_in_flight = self.remote_last_seq != self.local_seq_no; // max sequence number we can send. - let max_send_seq = self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); + let max_send_seq = + self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); // Max amount of octets we can send. let max_send = if max_send_seq >= self.remote_last_seq { @@ -1726,8 +1992,7 @@ impl<'a> TcpSocket<'a> { // 1. We have unsent data that fits in the remote window. // 2. We have no unsent data. // This condition matches only if #2, because #1 is already covered by can_data and we're ORing them. - let can_fin = - want_fin && self.remote_last_seq == self.local_seq_no + self.tx_buffer.len(); + let can_fin = want_fin && self.remote_last_seq == self.local_seq_no + self.tx_buffer.len(); can_send || can_fin } @@ -1749,15 +2014,22 @@ impl<'a> TcpSocket<'a> { fn window_to_update(&self) -> bool { match self.state { - State::SynSent | State::SynReceived | State::Established | State::FinWait1 | State::FinWait2 => - self.scaled_window() > self.remote_last_win, + State::SynSent + | State::SynReceived + | State::Established + | State::FinWait1 + | State::FinWait2 => self.scaled_window() > self.remote_last_win, _ => false, } } pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> - where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> { - if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) } + where + F: FnOnce((IpRepr, TcpRepr)) -> Result<()>, + { + if !self.remote_endpoint.is_specified() { + return Err(Error::Exhausted); + } if self.remote_last_ts.is_none() { // We get here in exactly two cases: @@ -1773,15 +2045,23 @@ impl<'a> TcpSocket<'a> { // Check if any state needs to be changed because of a timer. 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); + net_debug!( + "{}:{}:{}: timeout exceeded", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); self.set_state(State::Closed); } else if !self.seq_to_transmit(cx) { 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, - retransmit_delta); + net_debug!( + "{}:{}:{}: retransmitting at t+{}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + retransmit_delta + ); self.remote_last_seq = self.local_seq_no; self.rtte.on_retransmit(); } @@ -1790,62 +2070,91 @@ impl<'a> TcpSocket<'a> { // Decide whether we're sending a packet. if self.seq_to_transmit(cx) { // 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); + 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(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); + 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(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); + net_trace!( + "{}:{}:{}: outgoing segment will update window", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } else if self.state == State::Closed { // 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); + net_trace!( + "{}:{}:{}: outgoing segment will abort connection", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } 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); + net_trace!( + "{}:{}:{}: retransmit timer expired", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } 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); + net_trace!( + "{}:{}:{}: keep-alive timer expired", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } 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); + net_trace!( + "{}:{}:{}: TIME-WAIT timer expired", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); self.reset(); - return Err(Error::Exhausted) + return Err(Error::Exhausted); } else { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } // Construct the lowered IP representation. // We might need this to calculate the MSS, so do it early. let mut ip_repr = IpRepr::Unspecified { - src_addr: self.local_endpoint.addr, - dst_addr: self.remote_endpoint.addr, - protocol: IpProtocol::Tcp, - hop_limit: self.hop_limit.unwrap_or(64), - payload_len: 0 - }.lower(&[])?; + src_addr: self.local_endpoint.addr, + dst_addr: self.remote_endpoint.addr, + protocol: IpProtocol::Tcp, + hop_limit: self.hop_limit.unwrap_or(64), + payload_len: 0, + } + .lower(&[])?; // Construct the basic TCP representation, an empty ACK packet. // We'll adjust this to be more specific as needed. let mut repr = TcpRepr { - src_port: self.local_endpoint.port, - dst_port: self.remote_endpoint.port, - control: TcpControl::None, - seq_number: self.remote_last_seq, - ack_number: Some(self.remote_seq_no + self.rx_buffer.len()), - window_len: self.scaled_window(), + src_port: self.local_endpoint.port, + dst_port: self.remote_endpoint.port, + control: TcpControl::None, + seq_number: self.remote_last_seq, + ack_number: Some(self.remote_seq_no + self.rx_buffer.len()), + window_len: self.scaled_window(), window_scale: None, max_seg_size: None, sack_permitted: false, - sack_ranges: [None, None, None], - payload: &[] + sack_ranges: [None, None, None], + payload: &[], }; match self.state { @@ -1863,21 +2172,24 @@ impl<'a> TcpSocket<'a> { State::SynSent | State::SynReceived => { repr.control = TcpControl::Syn; // window len must NOT be scaled in SYNs. - repr.window_len = self.rx_buffer.window().min((1<<16)-1) as u16; + repr.window_len = self.rx_buffer.window().min((1 << 16) - 1) as u16; if self.state == State::SynSent { repr.ack_number = None; repr.window_scale = Some(self.remote_win_shift); repr.sack_permitted = true; } else { repr.sack_permitted = self.remote_has_sack; - repr.window_scale = self.remote_win_scale.map( - |_| self.remote_win_shift); + repr.window_scale = self.remote_win_scale.map(|_| self.remote_win_shift); } } // We transmit data in all states where we may have data in the buffer, // or the transmit half of the connection is still open. - State::Established | State::FinWait1 | State::Closing | State::CloseWait | State::LastAck => { + State::Established + | State::FinWait1 + | State::Closing + | State::CloseWait + | State::LastAck => { // Extract as much data as the remote side can receive in this packet // from the transmit buffer. @@ -1911,11 +2223,13 @@ impl<'a> TcpSocket<'a> { // flags, depending on whether the transmit half of the connection is open. if offset + repr.payload.len() == self.tx_buffer.len() { match self.state { - State::FinWait1 | State::LastAck | State::Closing => - repr.control = TcpControl::Fin, - State::Established | State::CloseWait if !repr.payload.is_empty() => - repr.control = TcpControl::Psh, - _ => () + State::FinWait1 | State::LastAck | State::Closing => { + repr.control = TcpControl::Fin + } + State::Established | State::CloseWait if !repr.payload.is_empty() => { + repr.control = TcpControl::Psh + } + _ => (), } } } @@ -1931,7 +2245,7 @@ impl<'a> TcpSocket<'a> { let is_keep_alive; 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 + repr.payload = b"\x00"; // RFC 1122 says we should do this is_keep_alive = true; } else { is_keep_alive = false; @@ -1939,27 +2253,39 @@ impl<'a> TcpSocket<'a> { // Trace a summary of what will be sent. if is_keep_alive { - net_trace!("{}:{}:{}: sending a keep-alive", - self.meta.handle, self.local_endpoint, self.remote_endpoint); + net_trace!( + "{}:{}:{}: sending a keep-alive", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); } else if !repr.payload.is_empty() { - net_trace!("{}:{}:{}: tx buffer: sending {} octets at offset {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - repr.payload.len(), self.remote_last_seq - self.local_seq_no); + net_trace!( + "{}:{}:{}: tx buffer: sending {} octets at offset {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + repr.payload.len(), + self.remote_last_seq - self.local_seq_no + ); } if repr.control != TcpControl::None || repr.payload.is_empty() { - let flags = - match (repr.control, repr.ack_number) { - (TcpControl::Syn, None) => "SYN", - (TcpControl::Syn, Some(_)) => "SYN|ACK", - (TcpControl::Fin, Some(_)) => "FIN|ACK", - (TcpControl::Rst, Some(_)) => "RST|ACK", - (TcpControl::Psh, Some(_)) => "PSH|ACK", - (TcpControl::None, Some(_)) => "ACK", - _ => "" - }; - net_trace!("{}:{}:{}: sending {}", - self.meta.handle, self.local_endpoint, self.remote_endpoint, - flags); + let flags = match (repr.control, repr.ack_number) { + (TcpControl::Syn, None) => "SYN", + (TcpControl::Syn, Some(_)) => "SYN|ACK", + (TcpControl::Fin, Some(_)) => "FIN|ACK", + (TcpControl::Rst, Some(_)) => "RST|ACK", + (TcpControl::Psh, Some(_)) => "PSH|ACK", + (TcpControl::None, Some(_)) => "ACK", + _ => "", + }; + net_trace!( + "{}:{}:{}: sending {}", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint, + flags + ); } if repr.control == TcpControl::Syn { @@ -1984,8 +2310,11 @@ impl<'a> TcpSocket<'a> { // Reset delayed-ack timer if self.ack_delay_until.is_some() { - net_trace!("{}:{}:{}: stop delayed ack timer", - self.meta.handle, self.local_endpoint, self.remote_endpoint + net_trace!( + "{}:{}:{}: stop delayed ack timer", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint ); self.ack_delay_until = None; @@ -1993,7 +2322,9 @@ impl<'a> TcpSocket<'a> { // Leave the rest of the state intact if sending a keep-alive packet, since those // carry a fake segment. - if is_keep_alive { return Ok(()) } + if is_keep_alive { + return Ok(()); + } // We've sent a packet successfully, so we can update the internal state now. self.remote_last_seq = repr.seq_number + repr.segment_len(); @@ -2001,18 +2332,20 @@ impl<'a> TcpSocket<'a> { self.remote_last_win = repr.window_len; if repr.segment_len() > 0 { - self.rtte.on_send(cx.now, repr.seq_number + repr.segment_len()); + self.rtte + .on_send(cx.now, repr.seq_number + repr.segment_len()); } if !self.seq_to_transmit(cx) && 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(cx.now, self.rtte.retransmission_timeout()); + self.timer + .set_for_retransmit(cx.now, self.rtte.retransmission_timeout()); } if self.state == State::Closed { // When aborting a connection, forget about it after sending a single RST packet. - self.local_endpoint = IpEndpoint::default(); + self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); } @@ -2053,7 +2386,8 @@ impl<'a> TcpSocket<'a> { // We wait for the earliest of our timers to fire. *[self.timer.poll_at(), timeout_poll_at, delayed_ack_poll_at] .iter() - .min().unwrap_or(&PollAt::Ingress) + .min() + .unwrap_or(&PollAt::Ingress) } } } @@ -2077,52 +2411,68 @@ impl<'a> fmt::Write for TcpSocket<'a> { #[cfg(test)] mod test { + use super::*; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; + use crate::wire::{IpAddress, IpCidr, IpRepr}; use core::i32; use std::vec::Vec; - use crate::wire::{IpAddress, IpRepr, IpCidr}; - use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; - use super::*; // =========================================================================================// // Constants // =========================================================================================// - const LOCAL_PORT: u16 = 80; - const REMOTE_PORT: u16 = 49500; - const LOCAL_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT }; - const REMOTE_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT }; - const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000); - const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10000); + const LOCAL_PORT: u16 = 80; + const REMOTE_PORT: u16 = 49500; + const LOCAL_END: IpEndpoint = IpEndpoint { + addr: MOCK_IP_ADDR_1, + port: LOCAL_PORT, + }; + const REMOTE_END: IpEndpoint = IpEndpoint { + addr: MOCK_IP_ADDR_2, + port: REMOTE_PORT, + }; + const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000); + const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10000); const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Tcp, payload_len: 20, - hop_limit: 64 + src_addr: MOCK_IP_ADDR_1, + dst_addr: MOCK_IP_ADDR_2, + protocol: IpProtocol::Tcp, + payload_len: 20, + hop_limit: 64, }; const SEND_TEMPL: TcpRepr<'static> = TcpRepr { - src_port: REMOTE_PORT, dst_port: LOCAL_PORT, + src_port: REMOTE_PORT, + dst_port: LOCAL_PORT, control: TcpControl::None, - seq_number: TcpSeqNumber(0), ack_number: Some(TcpSeqNumber(0)), - window_len: 256, window_scale: None, + seq_number: TcpSeqNumber(0), + ack_number: Some(TcpSeqNumber(0)), + window_len: 256, + window_scale: None, max_seg_size: None, sack_permitted: false, sack_ranges: [None, None, None], - payload: &[] + payload: &[], }; const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Tcp, payload_len: 20, - hop_limit: 64 + src_addr: MOCK_IP_ADDR_1, + dst_addr: MOCK_IP_ADDR_2, + protocol: IpProtocol::Tcp, + payload_len: 20, + hop_limit: 64, }; - const RECV_TEMPL: TcpRepr<'static> = TcpRepr { - src_port: LOCAL_PORT, dst_port: REMOTE_PORT, + const RECV_TEMPL: TcpRepr<'static> = TcpRepr { + src_port: LOCAL_PORT, + dst_port: REMOTE_PORT, control: TcpControl::None, - seq_number: TcpSeqNumber(0), ack_number: Some(TcpSeqNumber(0)), - window_len: 64, window_scale: None, + seq_number: TcpSeqNumber(0), + ack_number: Some(TcpSeqNumber(0)), + window_len: 64, + window_scale: None, max_seg_size: None, sack_permitted: false, sack_ranges: [None, None, None], - payload: &[] + payload: &[], }; #[cfg(feature = "proto-ipv6")] @@ -2134,14 +2484,17 @@ mod test { // Helper functions // =========================================================================================// - fn send(socket: &mut TcpSocket, timestamp: Instant, repr: &TcpRepr) -> - Result>> { + fn send( + socket: &mut TcpSocket, + timestamp: Instant, + repr: &TcpRepr, + ) -> Result>> { let ip_repr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + src_addr: MOCK_IP_ADDR_2, + dst_addr: MOCK_IP_ADDR_1, + protocol: IpProtocol::Tcp, payload_len: repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; net_trace!("send: {}", repr); @@ -2155,12 +2508,14 @@ mod test { Ok(Some(repr)) } Ok(None) => Ok(None), - Err(err) => Err(err) + Err(err) => Err(err), } } fn recv(socket: &mut TcpSocket, timestamp: Instant, mut f: F) - where F: FnMut(Result) { + where + F: FnMut(Result), + { let mut cx = Context::DUMMY.clone(); cx.now = timestamp; let result = socket.dispatch(&cx, |(ip_repr, tcp_repr)| { @@ -2176,7 +2531,7 @@ mod test { }); match result { Ok(()) => (), - Err(e) => f(Err(e)) + Err(e) => f(Err(e)), } } @@ -2212,20 +2567,20 @@ mod test { } macro_rules! sanity { - ($socket1:expr, $socket2:expr) => ({ + ($socket1:expr, $socket2:expr) => {{ let (s1, s2) = ($socket1, $socket2); - assert_eq!(s1.state, s2.state, "state"); - assert_eq!(s1.listen_address, s2.listen_address, "listen_address"); - assert_eq!(s1.local_endpoint, s2.local_endpoint, "local_endpoint"); - assert_eq!(s1.remote_endpoint, s2.remote_endpoint, "remote_endpoint"); - assert_eq!(s1.local_seq_no, s2.local_seq_no, "local_seq_no"); - assert_eq!(s1.remote_seq_no, s2.remote_seq_no, "remote_seq_no"); - assert_eq!(s1.remote_last_seq, s2.remote_last_seq, "remote_last_seq"); - assert_eq!(s1.remote_last_ack, s2.remote_last_ack, "remote_last_ack"); - assert_eq!(s1.remote_last_win, s2.remote_last_win, "remote_last_win"); - assert_eq!(s1.remote_win_len, s2.remote_win_len, "remote_win_len"); - assert_eq!(s1.timer, s2.timer, "timer"); - }) + assert_eq!(s1.state, s2.state, "state"); + assert_eq!(s1.listen_address, s2.listen_address, "listen_address"); + assert_eq!(s1.local_endpoint, s2.local_endpoint, "local_endpoint"); + assert_eq!(s1.remote_endpoint, s2.remote_endpoint, "remote_endpoint"); + assert_eq!(s1.local_seq_no, s2.local_seq_no, "local_seq_no"); + assert_eq!(s1.remote_seq_no, s2.remote_seq_no, "remote_seq_no"); + assert_eq!(s1.remote_last_seq, s2.remote_last_seq, "remote_last_seq"); + assert_eq!(s1.remote_last_ack, s2.remote_last_ack, "remote_last_ack"); + assert_eq!(s1.remote_last_win, s2.remote_last_win, "remote_last_win"); + assert_eq!(s1.remote_win_len, s2.remote_win_len, "remote_win_len"); + assert_eq!(s1.timer, s2.timer, "timer"); + }}; } #[cfg(feature = "log")] @@ -2242,8 +2597,7 @@ mod test { println!("{}", record.args()); } - fn flush(&self) { - } + fn flush(&self) {} } // If it fails, that just means we've already set it to the same value. @@ -2268,18 +2622,15 @@ mod test { socket } - fn socket_syn_received_with_buffer_sizes( - tx_len: usize, - rx_len: usize - ) -> TcpSocket<'static> { + fn socket_syn_received_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { let mut s = socket_with_buffer_sizes(tx_len, rx_len); - s.state = State::SynReceived; - s.local_endpoint = LOCAL_END; + s.state = State::SynReceived; + s.local_endpoint = LOCAL_END; s.remote_endpoint = REMOTE_END; - s.local_seq_no = LOCAL_SEQ; - s.remote_seq_no = REMOTE_SEQ + 1; + s.local_seq_no = LOCAL_SEQ; + s.remote_seq_no = REMOTE_SEQ + 1; s.remote_last_seq = LOCAL_SEQ; - s.remote_win_len = 256; + s.remote_win_len = 256; s } @@ -2287,15 +2638,12 @@ mod test { socket_syn_received_with_buffer_sizes(64, 64) } - fn socket_syn_sent_with_buffer_sizes( - tx_len: usize, - rx_len: usize - ) -> TcpSocket<'static> { + fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { let mut s = socket_with_buffer_sizes(tx_len, rx_len); - s.state = State::SynSent; - s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT); + s.state = State::SynSent; + s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT); s.remote_endpoint = REMOTE_END; - s.local_seq_no = LOCAL_SEQ; + s.local_seq_no = LOCAL_SEQ; s.remote_last_seq = LOCAL_SEQ; s } @@ -2306,18 +2654,18 @@ mod test { fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TcpSocket<'static> { let mut s = socket(); - s.state = State::SynSent; - s.local_endpoint = local; + s.state = State::SynSent; + s.local_endpoint = local; s.remote_endpoint = REMOTE_END; - s.local_seq_no = LOCAL_SEQ; + s.local_seq_no = LOCAL_SEQ; s.remote_last_seq = LOCAL_SEQ; s } fn socket_established_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { let mut s = socket_syn_received_with_buffer_sizes(tx_len, rx_len); - s.state = State::Established; - s.local_seq_no = LOCAL_SEQ + 1; + s.state = State::Established; + s.local_seq_no = LOCAL_SEQ + 1; s.remote_last_seq = LOCAL_SEQ + 1; s.remote_last_ack = Some(REMOTE_SEQ + 1); s.remote_last_win = 64; @@ -2330,65 +2678,73 @@ mod test { fn socket_fin_wait_1() -> TcpSocket<'static> { let mut s = socket_established(); - s.state = State::FinWait1; + s.state = State::FinWait1; s } fn socket_fin_wait_2() -> TcpSocket<'static> { let mut s = socket_fin_wait_1(); - s.state = State::FinWait2; - s.local_seq_no = LOCAL_SEQ + 1 + 1; + s.state = State::FinWait2; + s.local_seq_no = LOCAL_SEQ + 1 + 1; s.remote_last_seq = LOCAL_SEQ + 1 + 1; s } fn socket_closing() -> TcpSocket<'static> { let mut s = socket_fin_wait_1(); - s.state = State::Closing; + s.state = State::Closing; s.remote_last_seq = LOCAL_SEQ + 1 + 1; - s.remote_seq_no = REMOTE_SEQ + 1 + 1; + s.remote_seq_no = REMOTE_SEQ + 1 + 1; s } fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> { let mut s = socket_fin_wait_2(); - s.state = State::TimeWait; - s.remote_seq_no = REMOTE_SEQ + 1 + 1; + s.state = State::TimeWait; + s.remote_seq_no = REMOTE_SEQ + 1 + 1; if from_closing { s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1); } - s.timer = Timer::Close { expires_at: Instant::from_secs(1) + CLOSE_DELAY }; + s.timer = Timer::Close { + expires_at: Instant::from_secs(1) + CLOSE_DELAY, + }; s } fn socket_close_wait() -> TcpSocket<'static> { let mut s = socket_established(); - s.state = State::CloseWait; - s.remote_seq_no = REMOTE_SEQ + 1 + 1; + s.state = State::CloseWait; + s.remote_seq_no = REMOTE_SEQ + 1 + 1; s.remote_last_ack = Some(REMOTE_SEQ + 1 + 1); s } fn socket_last_ack() -> TcpSocket<'static> { let mut s = socket_close_wait(); - s.state = State::LastAck; + s.state = State::LastAck; s } fn socket_recved() -> TcpSocket<'static> { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + }] + ); s } @@ -2432,47 +2788,59 @@ mod test { // =========================================================================================// fn socket_listen() -> TcpSocket<'static> { let mut s = socket(); - s.state = State::Listen; - s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); + s.state = State::Listen; + s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); s } #[test] fn test_listen_sack_option() { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - sack_permitted: false, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + sack_permitted: false, + ..SEND_TEMPL + } + ); assert!(!s.remote_has_sack); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - sack_permitted: true, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + sack_permitted: true, + ..SEND_TEMPL + } + ); assert!(s.remote_has_sack); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - sack_permitted: true, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + sack_permitted: true, + ..RECV_TEMPL + }] + ); } #[test] @@ -2493,25 +2861,31 @@ mod test { ] { let mut s = socket_with_buffer_sizes(64, *buffer_size); s.state = State::Listen; - s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); + s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); assert_eq!(s.remote_win_shift, *shift_amt); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - window_scale: Some(0), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + window_scale: Some(0), + ..SEND_TEMPL + } + ); assert_eq!(s.remote_win_shift, *shift_amt); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - window_scale: Some(*shift_amt), - window_len: cmp::min(*buffer_size, 65535) as u16, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + window_scale: Some(*shift_amt), + window_len: cmp::min(*buffer_size, 65535) as u16, + ..RECV_TEMPL + }] + ); } } @@ -2538,12 +2912,15 @@ mod test { #[test] fn test_listen_syn() { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + ..SEND_TEMPL + } + ); sanity!(s, socket_syn_received()); } @@ -2565,12 +2942,16 @@ mod test { #[test] fn test_listen_rst() { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, - ack_number: None, - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, + ack_number: None, + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); } #[test] @@ -2587,18 +2968,24 @@ mod test { #[test] fn test_syn_received_ack() { let mut s = socket_syn_received(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Established); sanity!(s, socket_established()); } @@ -2606,81 +2993,111 @@ mod test { #[test] fn test_syn_received_fin() { let mut s = socket_syn_received(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6 + 1), - window_len: 58, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6 + 1), + window_len: 58, + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::CloseWait); - sanity!(s, TcpSocket { - remote_last_ack: Some(REMOTE_SEQ + 1 + 6 + 1), - remote_last_win: 58, - ..socket_close_wait() - }); + sanity!( + s, + TcpSocket { + remote_last_ack: Some(REMOTE_SEQ + 1 + 6 + 1), + remote_last_win: 58, + ..socket_close_wait() + } + ); } #[test] fn test_syn_received_rst() { let mut s = socket_syn_received(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Listen); - assert_eq!(s.local_endpoint, IpEndpoint::new(IpAddress::Unspecified, LOCAL_END.port)); + assert_eq!( + s.local_endpoint, + IpEndpoint::new(IpAddress::Unspecified, LOCAL_END.port) + ); assert_eq!(s.remote_endpoint, IpEndpoint::default()); } #[test] fn test_syn_received_no_window_scaling() { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + ..SEND_TEMPL + } + ); assert_eq!(s.state(), State::SynReceived); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - window_scale: None, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - window_scale: None, - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + window_scale: None, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_scale: None, + ..SEND_TEMPL + } + ); assert_eq!(s.remote_win_shift, 0); assert_eq!(s.remote_win_scale, None); } @@ -2689,30 +3106,39 @@ mod test { fn test_syn_received_window_scaling() { for scale in 0..14 { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - window_scale: Some(scale), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + window_scale: Some(scale), + ..SEND_TEMPL + } + ); assert_eq!(s.state(), State::SynReceived); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - window_scale: Some(0), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - window_scale: None, - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_scale: None, + ..SEND_TEMPL + } + ); assert_eq!(s.remote_win_scale, Some(scale)); } } @@ -2731,15 +3157,24 @@ mod test { #[test] fn test_connect_validation() { let mut s = socket(); - assert_eq!(s.connect((IpAddress::Unspecified, 80), LOCAL_END), - Err(Error::Unaddressable)); - assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)), - Err(Error::Unaddressable)); - assert_eq!(s.connect((MOCK_UNSPECIFIED, 0), LOCAL_END), - Err(Error::Unaddressable)); - assert_eq!(s.connect((IpAddress::Unspecified, 80), LOCAL_END), - Err(Error::Unaddressable)); - s.connect(REMOTE_END, LOCAL_END).expect("Connect failed with valid parameters"); + assert_eq!( + s.connect((IpAddress::Unspecified, 80), LOCAL_END), + Err(Error::Unaddressable) + ); + assert_eq!( + s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)), + Err(Error::Unaddressable) + ); + assert_eq!( + s.connect((MOCK_UNSPECIFIED, 0), LOCAL_END), + Err(Error::Unaddressable) + ); + assert_eq!( + s.connect((IpAddress::Unspecified, 80), LOCAL_END), + Err(Error::Unaddressable) + ); + s.connect(REMOTE_END, LOCAL_END) + .expect("Connect failed with valid parameters"); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); } @@ -2749,58 +3184,65 @@ mod test { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; s.connect(REMOTE_END, LOCAL_END.port).unwrap(); - assert_eq!(s.local_endpoint, IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port)); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - window_scale: Some(0), - sack_permitted: true, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - max_seg_size: Some(BASE_MSS - 80), - window_scale: Some(0), - ..SEND_TEMPL - }); + assert_eq!( + s.local_endpoint, + IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port) + ); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(0), + ..SEND_TEMPL + } + ); assert_eq!(s.local_endpoint, LOCAL_END); } #[test] fn test_connect_unspecified_local() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)), - Ok(())); + assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)), Ok(())); s.abort(); - assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), - Ok(())); + assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), Ok(())); s.abort(); } #[test] fn test_connect_specified_local() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)), - Ok(())); + assert_eq!(s.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)), Ok(())); } #[test] fn test_connect_twice() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), - Ok(())); - assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), - Err(Error::Illegal)); + assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), Ok(())); + assert_eq!( + s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + Err(Error::Illegal) + ); } #[test] fn test_syn_sent_sanity() { let mut s = socket(); - s.local_seq_no = LOCAL_SEQ; + s.local_seq_no = LOCAL_SEQ; s.connect(REMOTE_END, LOCAL_END).unwrap(); sanity!(s, socket_syn_sent_with_local_ipendpoint(LOCAL_END)); } @@ -2808,28 +3250,37 @@ mod test { #[test] fn test_syn_sent_syn_ack() { let mut s = socket_syn_sent(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - window_scale: Some(0), - sack_permitted: true, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - max_seg_size: Some(BASE_MSS - 80), - window_scale: Some(0), - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(0), + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); recv!(s, time 1000, Err(Error::Exhausted)); assert_eq!(s.state, State::Established); sanity!(s, socket_established()); @@ -2838,70 +3289,92 @@ mod test { #[test] fn test_syn_sent_syn_ack_not_incremented() { let mut s = socket_syn_sent(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - window_scale: Some(0), - sack_permitted: true, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ), // WRONG - max_seg_size: Some(BASE_MSS - 80), - window_scale: Some(0), - ..SEND_TEMPL - }, Err(Error::Dropped)); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ), // WRONG + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(0), + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); assert_eq!(s.state, State::SynSent); } #[test] fn test_syn_sent_rst() { let mut s = socket_syn_sent(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } #[test] fn test_syn_sent_rst_no_ack() { let mut s = socket_syn_sent(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, - ack_number: None, - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, + ack_number: None, + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); assert_eq!(s.state, State::SynSent); } #[test] fn test_syn_sent_rst_bad_ack() { let mut s = socket_syn_sent(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, - ack_number: Some(TcpSeqNumber(1234)), - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, + ack_number: Some(TcpSeqNumber(1234)), + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); assert_eq!(s.state, State::SynSent); } #[test] fn test_syn_sent_bad_ack() { let mut s = socket_syn_sent(); - send!(s, TcpRepr { - control: TcpControl::None, - ack_number: Some(TcpSeqNumber(1)), - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + control: TcpControl::None, + ack_number: Some(TcpSeqNumber(1)), + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); assert_eq!(s.state, State::Closed); } @@ -2932,43 +3405,52 @@ mod test { s.local_seq_no = LOCAL_SEQ; assert_eq!(s.remote_win_shift, *shift_amt); s.connect(REMOTE_END, LOCAL_END).unwrap(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - window_scale: Some(*shift_amt), - window_len: cmp::min(*buffer_size, 65535) as u16, - sack_permitted: true, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(*shift_amt), + window_len: cmp::min(*buffer_size, 65535) as u16, + sack_permitted: true, + ..RECV_TEMPL + }] + ); } } #[test] fn test_syn_sent_syn_ack_no_window_scaling() { let mut s = socket_syn_sent_with_buffer_sizes(1048576, 1048576); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - // scaling does NOT apply to the window value in SYN packets - window_len: 65535, - window_scale: Some(5), - sack_permitted: true, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + // scaling does NOT apply to the window value in SYN packets + window_len: 65535, + window_scale: Some(5), + sack_permitted: true, + ..RECV_TEMPL + }] + ); assert_eq!(s.remote_win_shift, 5); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - max_seg_size: Some(BASE_MSS - 80), - window_scale: None, - window_len: 42, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: None, + window_len: 42, + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Established); assert_eq!(s.remote_win_shift, 0); assert_eq!(s.remote_win_scale, None); @@ -2978,24 +3460,30 @@ mod test { #[test] fn test_syn_sent_syn_ack_window_scaling() { let mut s = socket_syn_sent(); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: None, - max_seg_size: Some(BASE_MSS), - window_scale: Some(0), - sack_permitted: true, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - max_seg_size: Some(BASE_MSS - 80), - window_scale: Some(7), - window_len: 42, - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + max_seg_size: Some(BASE_MSS - 80), + window_scale: Some(7), + window_len: 42, + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Established); assert_eq!(s.remote_win_scale, Some(7)); // scaling does NOT apply to the window value in SYN packets @@ -3009,18 +3497,24 @@ mod test { #[test] fn test_established_recv() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + }] + ); assert_eq!(s.rx_buffer.dequeue_many(6), &b"abcdef"[..]); } @@ -3037,25 +3531,34 @@ mod test { let mut segment: Vec = Vec::with_capacity(500); // move the last ack to 5000 by sending ten of them - for _ in 0..50 { segment.extend_from_slice(b"abcdefghij") } + for _ in 0..50 { + segment.extend_from_slice(b"abcdefghij") + } for offset in (0..5000).step_by(500) { - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + offset, - ack_number: Some(LOCAL_SEQ + 1), - payload: &segment, - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + offset + 500), - window_len: 3500, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + offset, + ack_number: Some(LOCAL_SEQ + 1), + payload: &segment, + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + offset + 500), + window_len: 3500, + ..RECV_TEMPL + }] + ); s.recv(|data| { assert_eq!(data.len(), 500); assert_eq!(data, segment.as_slice()); (500, ()) - }).unwrap(); + }) + .unwrap(); } assert_eq!(s.remote_last_win, 3500); (s, segment) @@ -3088,21 +3591,29 @@ mod test { // 8500 5000 5500 9000 // for offset in (500..3500).step_by(500) { - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + offset + 5000, - ack_number: Some(LOCAL_SEQ + 1), - payload: &segment, - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 5000), - window_len: 4000, - sack_ranges: [ - Some((REMOTE_SEQ.0 as u32 + 1 + 5500, - REMOTE_SEQ.0 as u32 + 1 + 5500 + offset as u32)), - None, None], - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + offset + 5000, + ack_number: Some(LOCAL_SEQ + 1), + payload: &segment, + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 5000), + window_len: 4000, + sack_ranges: [ + Some(( + REMOTE_SEQ.0 as u32 + 1 + 5500, + REMOTE_SEQ.0 as u32 + 1 + 5500 + offset as u32 + )), + None, + None + ], + ..RECV_TEMPL + })) + ); } } @@ -3119,24 +3630,32 @@ mod test { // Create a TCP segment that will mostly fill an IP frame. let mut segment: Vec = Vec::with_capacity(1400); - for _ in 0..100 { segment.extend_from_slice(b"abcdefghijklmn") } + for _ in 0..100 { + segment.extend_from_slice(b"abcdefghijklmn") + } assert_eq!(segment.len(), 1400); // Send the frame - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &segment, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &segment, + ..SEND_TEMPL + } + ); // Ensure that the received window size is shifted right by 2. - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1400), - window_len: 65185, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1400), + window_len: 65185, + ..RECV_TEMPL + }] + ); } #[test] @@ -3144,32 +3663,44 @@ mod test { let mut s = socket_established(); // First roundtrip after establishing. s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); assert_eq!(s.tx_buffer.len(), 6); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6), + ..SEND_TEMPL + } + ); assert_eq!(s.tx_buffer.len(), 0); // Second roundtrip. s.send_slice(b"foobar").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"foobar"[..], - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 6), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"foobar"[..], + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 6), + ..SEND_TEMPL + } + ); assert_eq!(s.tx_buffer.len(), 0); } @@ -3178,19 +3709,25 @@ mod test { let mut s = socket_established(); s.set_nagle_enabled(false); s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); s.send_slice(b"foobar").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"foobar"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"foobar"[..], + ..RECV_TEMPL + }] + ); } #[test] @@ -3203,12 +3740,15 @@ mod test { let mut s = socket_established(); s.remote_win_len = 16; s.send_slice(&data[..]).unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &data[0..16], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &data[0..16], + ..RECV_TEMPL + }] + ); } #[test] @@ -3217,42 +3757,56 @@ mod test { // 6 octets fit on the remote side's window, so we send them. s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); assert_eq!(s.tx_buffer.len(), 6); - println!("local_seq_no={} remote_win_len={} remote_last_seq={}", s.local_seq_no, s.remote_win_len, s.remote_last_seq); + println!( + "local_seq_no={} remote_win_len={} remote_last_seq={}", + s.local_seq_no, s.remote_win_len, s.remote_last_seq + ); // - Peer doesn't ack them yet // - Sends data so we need to reply with an ACK // - ...AND and sends a window announcement that SHRINKS the window, so data we've // previously sent is now outside the window. Yes, this is allowed by TCP. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - window_len: 3, - payload: &b"xyzxyz"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_len: 3, + payload: &b"xyzxyz"[..], + ..SEND_TEMPL + } + ); assert_eq!(s.tx_buffer.len(), 6); - println!("local_seq_no={} remote_win_len={} remote_last_seq={}", s.local_seq_no, s.remote_win_len, s.remote_last_seq); + println!( + "local_seq_no={} remote_win_len={} remote_last_seq={}", + s.local_seq_no, s.remote_win_len, s.remote_last_seq + ); // More data should not get sent since it doesn't fit in the window s.send_slice(b"foobar").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 64 - 6, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 64 - 6, + ..RECV_TEMPL + }] + ); } - #[test] fn test_established_send_wrap() { let mut s = socket_established(); @@ -3271,33 +3825,45 @@ mod test { #[test] fn test_established_no_ack() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: None, - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: None, + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); } #[test] fn test_established_bad_ack() { let mut s = socket_established(); // Already acknowledged data. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(TcpSeqNumber(LOCAL_SEQ.0 - 1)), - ..SEND_TEMPL - }, Err(Error::Dropped)); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(TcpSeqNumber(LOCAL_SEQ.0 - 1)), + ..SEND_TEMPL + }, + Err(Error::Dropped) + ); assert_eq!(s.local_seq_no, LOCAL_SEQ + 1); // Data not yet transmitted. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 10), - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 10), + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); assert_eq!(s.local_seq_no, LOCAL_SEQ + 1); } @@ -3305,32 +3871,42 @@ mod test { fn test_established_bad_seq() { let mut s = socket_established(); // Data outside of receive window. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 256, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 256, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); } #[test] fn test_established_fin() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::CloseWait); sanity!(s, socket_close_wait()); } @@ -3338,29 +3914,37 @@ mod test { #[test] fn test_established_fin_after_missing() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1 + 6, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"123456"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"123456"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); assert_eq!(s.state, State::Established); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6 + 6), - window_len: 52, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6 + 6), + window_len: 52, + ..RECV_TEMPL + })) + ); assert_eq!(s.state, State::Established); } @@ -3368,42 +3952,54 @@ mod test { fn test_established_send_fin() { let mut s = socket_established(); s.send_slice(b"abcdef").unwrap(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::CloseWait); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); } #[test] fn test_established_rst() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } #[test] fn test_established_rst_no_ack() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1, - ack_number: None, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1, + ack_number: None, + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } @@ -3420,55 +4016,68 @@ mod test { let mut s = socket_established(); s.abort(); assert_eq!(s.state, State::Closed); - recv!(s, [TcpRepr { - control: TcpControl::Rst, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Rst, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); } #[test] fn test_established_rst_bad_seq() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, // Wrong seq - ack_number: None, - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, // Wrong seq + ack_number: None, + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); assert_eq!(s.state, State::Established); // Send something to advance seq by 1 - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, // correct seq - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"a"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, // correct seq + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"a"[..], + ..SEND_TEMPL + } + ); // Send wrong rst again, check that the challenge ack is correctly updated // The ack number must be updated even if we don't call dispatch on the socket // See https://github.com/smoltcp-rs/smoltcp/issues/338 - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ, // Wrong seq - ack_number: None, - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 2), // this has changed - window_len: 63, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ, // Wrong seq + ack_number: None, + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 2), // this has changed + window_len: 63, + ..RECV_TEMPL + })) + ); } - // =========================================================================================// // Tests for the FIN-WAIT-1 state. // =========================================================================================// @@ -3476,17 +4085,23 @@ mod test { #[test] fn test_fin_wait_1_fin_ack() { let mut s = socket_fin_wait_1(); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait2); sanity!(s, socket_fin_wait_2()); } @@ -3494,18 +4109,24 @@ mod test { #[test] fn test_fin_wait_1_fin_fin() { let mut s = socket_fin_wait_1(); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closing); sanity!(s, socket_closing()); } @@ -3516,34 +4137,44 @@ mod test { s.remote_win_len = 6; s.send_slice(b"abcdef123456").unwrap(); s.close(); - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - })); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6), - ..SEND_TEMPL - }); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }) + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait1); } #[test] fn test_fin_wait_1_recv() { let mut s = socket_fin_wait_1(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait1); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -3573,22 +4204,29 @@ mod test { #[test] fn test_fin_wait_2_recv() { let mut s = socket_fin_wait_2(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait2); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - ..RECV_TEMPL - }]); + }) + .unwrap(); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + ..RECV_TEMPL + }] + ); } #[test] @@ -3605,11 +4243,14 @@ mod test { #[test] fn test_closing_ack_fin() { let mut s = socket_closing(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); send!(s, time 1_000, TcpRepr { seq_number: REMOTE_SEQ + 1 + 1, ack_number: Some(LOCAL_SEQ + 1 + 1), @@ -3633,11 +4274,14 @@ mod test { #[test] fn test_time_wait_from_fin_wait_2_ack() { let mut s = socket_time_wait(false); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); } #[test] @@ -3656,11 +4300,14 @@ mod test { #[test] fn test_time_wait_retransmit() { let mut s = socket_time_wait(false); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); send!(s, time 5_000, TcpRepr { control: TcpControl::Fin, seq_number: REMOTE_SEQ + 1, @@ -3671,17 +4318,25 @@ mod test { ack_number: Some(REMOTE_SEQ + 1 + 1), ..RECV_TEMPL }))); - assert_eq!(s.timer, Timer::Close { expires_at: Instant::from_secs(5) + CLOSE_DELAY }); + assert_eq!( + s.timer, + Timer::Close { + expires_at: Instant::from_secs(5) + CLOSE_DELAY + } + ); } #[test] fn test_time_wait_timeout() { let mut s = socket_time_wait(false); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::TimeWait); recv!(s, time 60_000, Err(Error::Exhausted)); assert_eq!(s.state, State::Closed); @@ -3695,17 +4350,23 @@ mod test { fn test_close_wait_ack() { let mut s = socket_close_wait(); s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6), + ..SEND_TEMPL + } + ); } #[test] @@ -3722,46 +4383,61 @@ mod test { #[test] fn test_last_ack_fin_ack() { let mut s = socket_last_ack(); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::LastAck); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } #[test] fn test_last_ack_ack_not_of_fin() { let mut s = socket_last_ack(); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); - assert_eq!(s.state, State::LastAck); - - // ACK received that doesn't ack the FIN: socket should stay in LastAck. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); + assert_eq!(s.state, State::LastAck); + + // ACK received that doesn't ack the FIN: socket should stay in LastAck. + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::LastAck); // ACK received of fin: socket should change to Closed. - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } @@ -3779,34 +4455,44 @@ mod test { #[test] fn test_listen() { let mut s = socket(); - s.listen(IpEndpoint::new(IpAddress::default(), LOCAL_PORT)).unwrap(); + s.listen(IpEndpoint::new(IpAddress::default(), LOCAL_PORT)) + .unwrap(); assert_eq!(s.state, State::Listen); } #[test] fn test_three_way_handshake() { let mut s = socket_listen(); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + ..SEND_TEMPL + } + ); assert_eq!(s.state(), State::SynReceived); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state(), State::Established); assert_eq!(s.local_seq_no, LOCAL_SEQ + 1); assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); @@ -3815,31 +4501,43 @@ mod test { #[test] fn test_remote_close() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::CloseWait); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); s.close(); assert_eq!(s.state, State::LastAck); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closed); } @@ -3848,30 +4546,42 @@ mod test { let mut s = socket_established(); s.close(); assert_eq!(s.state, State::FinWait1); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait2); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); } #[test] @@ -3879,30 +4589,43 @@ mod test { let mut s = socket_established(); s.close(); assert_eq!(s.state, State::FinWait1); - recv!(s, [TcpRepr { // due to reordering, this is logically located... - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + // due to reordering, this is logically located... + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closing); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); // ... at this point - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); recv!(s, []); } @@ -3912,24 +4635,33 @@ mod test { let mut s = socket_established(); s.close(); assert_eq!(s.state, State::FinWait1); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); } #[test] @@ -3939,28 +4671,37 @@ mod test { assert_eq!(s.state, State::FinWait1); // Socket receives FIN before it has a chance to send its own FIN - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closing); // FIN + ack-of-FIN - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::Closing); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); recv!(s, []); } @@ -3973,29 +4714,38 @@ mod test { assert_eq!(s.state, State::FinWait1); // Socket receives FIN before it has a chance to send its own data+FIN - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closing); // data + FIN + ack-of-FIN - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::Closing); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); recv!(s, []); } @@ -4005,13 +4755,16 @@ mod test { let mut s = socket_established(); s.send_slice(b"abcdef").unwrap(); s.close(); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]) + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ) } #[test] @@ -4020,19 +4773,25 @@ mod test { s.send_slice(b"abcdef").unwrap(); s.close(); assert_eq!(s.state, State::FinWait1); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), + ..SEND_TEMPL + } + ); } #[test] @@ -4041,30 +4800,42 @@ mod test { s.send_slice(b"abcdef").unwrap(); s.close(); assert_eq!(s.state, State::FinWait1); - recv!(s, [TcpRepr { - control: TcpControl::Fin, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), - ..SEND_TEMPL - }); + recv!( + s, + [TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::FinWait2); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6 + 1, - ack_number: Some(REMOTE_SEQ + 1 + 1), - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 1), + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 1, + ack_number: Some(REMOTE_SEQ + 1 + 1), + ..RECV_TEMPL + }] + ); assert_eq!(s.state, State::TimeWait); } @@ -4076,17 +4847,21 @@ mod test { fn test_duplicate_seq_ack() { let mut s = socket_recved(); // remote retransmission - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + })) + ); } #[test] @@ -4166,19 +4941,25 @@ mod test { max_seg_size: Some(BASE_MSS), ..RECV_TEMPL })); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); assert_eq!(s.state(), State::Established); s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]) + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ) } #[test] @@ -4195,11 +4976,14 @@ mod test { // Retransmit timer is on because all data was sent assert_eq!(s.tx_buffer.len(), 3); // ACK nothing new - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); // Retransmit recv!(s, time 4000, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, @@ -4486,45 +5270,61 @@ mod test { })); // Normal ACK of previously recieved segment - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); // First duplicate - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); // Second duplicate - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); - assert_eq!(s.local_rx_dup_acks, 2, - "duplicate ACK counter is not set"); + assert_eq!(s.local_rx_dup_acks, 2, "duplicate ACK counter is not set"); // This packet has content, hence should not be detected // as a duplicate ACK and should reset the duplicate ACK count - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"xxxxxx"[..], - ..SEND_TEMPL - }); - - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 3, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"xxxxxx"[..], + ..SEND_TEMPL + } + ); + + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 3, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + }] + ); - assert_eq!(s.local_rx_dup_acks, 0, - "duplicate ACK counter is not reset when reciving data"); + assert_eq!( + s.local_rx_dup_acks, 0, + "duplicate ACK counter is not reset when reciving data" + ); } #[test] @@ -4546,8 +5346,10 @@ mod test { ..SEND_TEMPL }); - assert_eq!(s.local_rx_dup_acks, 0, - "duplicate ACK counter is set but wound not transmit data"); + assert_eq!( + s.local_rx_dup_acks, 0, + "duplicate ACK counter is set but wound not transmit data" + ); // Send a long string of text divided into several packets // because of small remote_mss @@ -4598,8 +5400,10 @@ mod test { ..SEND_TEMPL }); - assert_eq!(s.local_rx_dup_acks, 0, - "duplicate ACK counter is not reset when reciving ACK which updates send window"); + assert_eq!( + s.local_rx_dup_acks, 0, + "duplicate ACK counter is not reset when reciving ACK which updates send window" + ); // ACK all recived segments send!(s, time 1120, TcpRepr { @@ -4648,7 +5452,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL }); - assert_eq!(s.local_rx_dup_acks, u8::max_value(), "duplicate ACK count should not overflow but saturate"); + assert_eq!( + s.local_rx_dup_acks, + u8::max_value(), + "duplicate ACK count should not overflow but saturate" + ); } // =========================================================================================// @@ -4659,54 +5467,72 @@ mod test { fn test_maximum_segment_size() { let mut s = socket_listen(); s.tx_buffer = SocketBuffer::new(vec![0; 32767]); - send!(s, TcpRepr { - control: TcpControl::Syn, - seq_number: REMOTE_SEQ, - ack_number: None, - max_seg_size: Some(1000), - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - control: TcpControl::Syn, - seq_number: LOCAL_SEQ, - ack_number: Some(REMOTE_SEQ + 1), - max_seg_size: Some(BASE_MSS), - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - window_len: 32767, - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Syn, + seq_number: REMOTE_SEQ, + ack_number: None, + max_seg_size: Some(1000), + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_len: 32767, + ..SEND_TEMPL + } + ); s.send_slice(&[0; 1200][..]).unwrap(); - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &[0; 1000][..], - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &[0; 1000][..], + ..RECV_TEMPL + }) + ); } #[test] fn test_close_wait_no_window_update() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &[1,2,3,4], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &[1, 2, 3, 4], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::CloseWait); // we ack the FIN, with the reduced window size. - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 6), - window_len: 60, - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 6), + window_len: 60, + ..RECV_TEMPL + }) + ); let rx_buf = &mut [0; 32]; assert_eq!(s.recv_slice(rx_buf), Ok(4)); @@ -4718,22 +5544,28 @@ mod test { #[test] fn test_time_wait_no_window_update() { let mut s = socket_fin_wait_2(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 2), - payload: &[1,2,3,4], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 2), + payload: &[1, 2, 3, 4], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); // we ack the FIN, with the reduced window size. - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 2, - ack_number: Some(REMOTE_SEQ + 6), - window_len: 60, - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 2, + ack_number: Some(REMOTE_SEQ + 6), + window_len: 60, + ..RECV_TEMPL + }) + ); let rx_buf = &mut [0; 32]; assert_eq!(s.recv_slice(rx_buf), Ok(4)); @@ -4771,19 +5603,25 @@ mod test { #[test] fn test_psh_receive() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Psh, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + control: TcpControl::Psh, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + }] + ); } #[test] @@ -4791,29 +5629,39 @@ mod test { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); s.assembler = Assembler::new(s.rx_buffer.capacity()); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 0, - ..RECV_TEMPL - }]); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 6, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"123456"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 0, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 0, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"123456"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 0, + ..RECV_TEMPL + })) + ); } #[test] @@ -4821,23 +5669,30 @@ mod test { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); s.assembler = Assembler::new(s.rx_buffer.capacity()); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 0, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 0, + ..RECV_TEMPL + }] + ); recv!(s, time 0, Err(Error::Exhausted)); s.recv(|buffer| { assert_eq!(&buffer[..3], b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); recv!(s, time 0, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6), @@ -4848,7 +5703,8 @@ mod test { s.recv(|buffer| { assert_eq!(buffer, b"def"); (buffer.len(), ()) - }).unwrap(); + }) + .unwrap(); recv!(s, time 0, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6), @@ -4862,22 +5718,29 @@ mod test { let mut s = socket_established(); s.remote_mss = 6; s.send_slice(b"abcdef123456!@#$%^").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }, TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"123456"[..], - ..RECV_TEMPL - }, TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"!@#$%^"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [ + TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }, + TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"123456"[..], + ..RECV_TEMPL + }, + TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"!@#$%^"[..], + ..RECV_TEMPL + } + ] + ); } #[test] @@ -4885,58 +5748,71 @@ mod test { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); s.assembler = Assembler::new(s.rx_buffer.capacity()); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - window_len: 3, - ..RECV_TEMPL - }]); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 3, + ..RECV_TEMPL + }] + ); // Test that `dispatch` updates `remote_last_win` assert_eq!(s.remote_last_win, s.rx_buffer.window() as u16); - s.recv(|buffer| { - (buffer.len(), ()) - }).unwrap(); + s.recv(|buffer| (buffer.len(), ())).unwrap(); assert!(s.window_to_update()); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - window_len: 6, - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 6, + ..RECV_TEMPL + }] + ); assert_eq!(s.remote_last_win, s.rx_buffer.window() as u16); // Provoke immediate ACK to test that `process` updates `remote_last_win` - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 6, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"def"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - window_len: 6, - ..RECV_TEMPL - }))); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 3, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 9), - window_len: 0, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"def"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 6, + ..RECV_TEMPL + })) + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 9), + window_len: 0, + ..RECV_TEMPL + })) + ); assert_eq!(s.remote_last_win, s.rx_buffer.window() as u16); - s.recv(|buffer| { - (buffer.len(), ()) - }).unwrap(); + s.recv(|buffer| (buffer.len(), ())).unwrap(); assert!(s.window_to_update()); } @@ -4967,7 +5843,10 @@ mod test { ..RECV_TEMPL })); assert_eq!(s.state, State::SynSent); - assert_eq!(s.poll_at(&Context::DUMMY), 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, @@ -4983,7 +5862,10 @@ 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(&Context::DUMMY), 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(&Context::DUMMY), PollAt::Now); recv!(s, time 255, Ok(TcpRepr { @@ -4992,14 +5874,20 @@ mod test { payload: &b"abcdef"[..], ..RECV_TEMPL })); - assert_eq!(s.poll_at(&Context::DUMMY), 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(&Context::DUMMY), 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, @@ -5021,13 +5909,19 @@ mod test { ..RECV_TEMPL })); recv!(s, time 100, Err(Error::Exhausted)); - assert_eq!(s.poll_at(&Context::DUMMY), 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(&Context::DUMMY), 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), @@ -5035,7 +5929,10 @@ mod test { ..RECV_TEMPL })); recv!(s, time 155, Err(Error::Exhausted)); - assert_eq!(s.poll_at(&Context::DUMMY), 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, @@ -5108,15 +6005,19 @@ mod test { #[test] fn test_responds_to_keep_alive() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); } #[test] @@ -5133,7 +6034,10 @@ mod test { ..RECV_TEMPL })); - assert_eq!(s.poll_at(&Context::DUMMY), 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, @@ -5142,7 +6046,10 @@ mod test { ..RECV_TEMPL })); - assert_eq!(s.poll_at(&Context::DUMMY), 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, @@ -5156,7 +6063,10 @@ mod test { ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL }); - assert_eq!(s.poll_at(&Context::DUMMY), 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, @@ -5175,10 +6085,13 @@ mod test { let mut s = socket_syn_received(); s.set_hop_limit(Some(0x2a)); - assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| { - assert_eq!(ip_repr.hop_limit(), 0x2a); + assert_eq!( + s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + assert_eq!(ip_repr.hop_limit(), 0x2a); + Ok(()) + }), Ok(()) - }), Ok(())); + ); } #[test] @@ -5190,40 +6103,50 @@ mod test { // =========================================================================================// // Tests for reassembly. - // =========================================================================================// - - #[test] - fn test_out_of_order() { - let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 3, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"def"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - ..RECV_TEMPL - }))); + // =========================================================================================// + + #[test] + fn test_out_of_order() { + let mut s = socket_established(); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"def"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); s.recv(|buffer| { assert_eq!(buffer, b""); (buffer.len(), ()) - }).unwrap(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - }))); + }) + .unwrap(); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abcdef"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + })) + ); s.recv(|buffer| { assert_eq!(buffer, b"abcdef"); (buffer.len(), ()) - }).unwrap(); + }) + .unwrap(); } #[test] @@ -5231,22 +6154,29 @@ mod test { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); s.assembler = Assembler::new(s.rx_buffer.capacity()); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); s.recv(|buffer| { assert_eq!(buffer, b"abc"); (buffer.len(), ()) - }).unwrap(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 3, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"defghi"[..], - ..SEND_TEMPL - }); + }) + .unwrap(); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"defghi"[..], + ..SEND_TEMPL + } + ); let mut data = [0; 6]; assert_eq!(s.recv_slice(&mut data[..]), Ok(6)); assert_eq!(data, &b"defghi"[..]); @@ -5264,18 +6194,24 @@ mod test { // "abcdef" not contiguous in tx buffer assert_eq!(s.send_slice(b"abcdef"), Ok(6)); - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"yyyabc"[..], - ..RECV_TEMPL - })); - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"def"[..], - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"yyyabc"[..], + ..RECV_TEMPL + }) + ); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"def"[..], + ..RECV_TEMPL + }) + ); } // =========================================================================================// @@ -5285,93 +6221,115 @@ mod test { #[test] fn test_rx_close_fin() { let mut s = socket_established(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); } #[test] fn test_rx_close_fin_in_fin_wait_1() { let mut s = socket_fin_wait_1(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::Closing); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); } #[test] fn test_rx_close_fin_in_fin_wait_2() { let mut s = socket_fin_wait_2(); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); assert_eq!(s.state, State::TimeWait); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); } - - #[test] fn test_rx_close_fin_with_hole() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); - send!(s, TcpRepr { - control: TcpControl::Fin, - seq_number: REMOTE_SEQ + 1 + 6, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"ghi"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - window_len: 61, - ..RECV_TEMPL - }))); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); + send!( + s, + TcpRepr { + control: TcpControl::Fin, + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"ghi"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 61, + ..RECV_TEMPL + })) + ); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); s.recv(|data| { assert_eq!(data, b""); (0, ()) - }).unwrap(); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1 + 9, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + }) + .unwrap(); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1 + 9, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); // Error must be `Illegal` even if we've received a FIN, // because we are missing data. assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); @@ -5380,55 +6338,73 @@ mod test { #[test] fn test_rx_close_rst() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1 + 3, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); } #[test] fn test_rx_close_rst_with_hole() { let mut s = socket_established(); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 6, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"ghi"[..], - ..SEND_TEMPL - }, Ok(Some(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - window_len: 61, - ..RECV_TEMPL - }))); - send!(s, TcpRepr { - control: TcpControl::Rst, - seq_number: REMOTE_SEQ + 1 + 9, - ack_number: Some(LOCAL_SEQ + 1), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"ghi"[..], + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + window_len: 61, + ..RECV_TEMPL + })) + ); + send!( + s, + TcpRepr { + control: TcpControl::Rst, + seq_number: REMOTE_SEQ + 1 + 9, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); } @@ -5440,12 +6416,15 @@ mod test { fn test_delayed_ack() { let mut s = socket_established(); s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); // No ACK is immediately sent. recv!(s, Err(Error::Exhausted)); @@ -5463,18 +6442,22 @@ mod test { fn test_delayed_ack_win() { let mut s = socket_established(); s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); // Reading the data off the buffer should cause a window update. s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); // However, no ACK or window update is immediately sent. recv!(s, Err(Error::Exhausted)); @@ -5491,58 +6474,74 @@ mod test { fn test_delayed_ack_reply() { let mut s = socket_established(); s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); s.recv(|data| { assert_eq!(data, b"abc"); (3, ()) - }).unwrap(); + }) + .unwrap(); s.send_slice(&b"xyz"[..]).unwrap(); // Writing data to the socket causes ACK to not be delayed, // because it is immediately sent with the data. - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 3), - payload: &b"xyz"[..], - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 3), + payload: &b"xyz"[..], + ..RECV_TEMPL + }) + ); } #[test] fn test_delayed_ack_every_second_packet() { let mut s = socket_established(); s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abc"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); // No ACK is immediately sent. recv!(s, Err(Error::Exhausted)); - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1 + 3, - ack_number: Some(LOCAL_SEQ + 1), - payload: &b"def"[..], - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"def"[..], + ..SEND_TEMPL + } + ); // Every 2nd packet, ACK is sent without delay. - recv!(s, Ok(TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1 + 6), - window_len: 58, - ..RECV_TEMPL - })); + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 6), + window_len: 58, + ..RECV_TEMPL + }) + ); } // =========================================================================================// @@ -5555,45 +6554,60 @@ mod test { s.remote_mss = 6; s.send_slice(b"abcdef").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"abcdef"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }] + ); // If there's data in flight, full segments get sent. s.send_slice(b"foobar").unwrap(); - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"foobar"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"foobar"[..], + ..RECV_TEMPL + }] + ); s.send_slice(b"aaabbbccc").unwrap(); // If there's data in flight, not-full segments don't get sent. - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"aaabbb"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"aaabbb"[..], + ..RECV_TEMPL + }] + ); // Data gets ACKd, so there's no longer data in flight - send!(s, TcpRepr { - seq_number: REMOTE_SEQ + 1, - ack_number: Some(LOCAL_SEQ + 1 + 6 + 6 + 6), - ..SEND_TEMPL - }); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6 + 6 + 6), + ..SEND_TEMPL + } + ); // Now non-full segment gets sent. - recv!(s, [TcpRepr { - seq_number: LOCAL_SEQ + 1 + 6 + 6 + 6, - ack_number: Some(REMOTE_SEQ + 1), - payload: &b"ccc"[..], - ..RECV_TEMPL - }]); + recv!( + s, + [TcpRepr { + seq_number: LOCAL_SEQ + 1 + 6 + 6 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"ccc"[..], + ..RECV_TEMPL + }] + ); } // =========================================================================================// @@ -5609,7 +6623,7 @@ mod test { let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - dst_port: LOCAL_PORT + 1, + dst_port: LOCAL_PORT + 1, ..SEND_TEMPL }; assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); @@ -5617,7 +6631,7 @@ mod test { let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - src_port: REMOTE_PORT + 1, + src_port: REMOTE_PORT + 1, ..SEND_TEMPL }; assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); @@ -5630,34 +6644,34 @@ mod test { let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), - payload: &b"abcdef"[..], + payload: &b"abcdef"[..], ..SEND_TEMPL }; let ip_repr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + src_addr: MOCK_IP_ADDR_2, + dst_addr: MOCK_IP_ADDR_1, + protocol: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; assert!(s.accepts(&ip_repr, &tcp_repr)); let ip_repr_wrong_src = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_3, - dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + src_addr: MOCK_IP_ADDR_3, + dst_addr: MOCK_IP_ADDR_1, + protocol: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr)); let ip_repr_wrong_dst = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_3, - protocol: IpProtocol::Tcp, + src_addr: MOCK_IP_ADDR_2, + dst_addr: MOCK_IP_ADDR_3, + protocol: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), - hop_limit: 64 + hop_limit: 64, }; assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr)); } @@ -5674,12 +6688,18 @@ mod test { r.set_for_retransmit(Instant::from_millis(1000), RTO); assert_eq!(r.should_retransmit(Instant::from_millis(1000)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1050)), None); - assert_eq!(r.should_retransmit(Instant::from_millis(1101)), Some(Duration::from_millis(101))); + assert_eq!( + r.should_retransmit(Instant::from_millis(1101)), + Some(Duration::from_millis(101)) + ); r.set_for_retransmit(Instant::from_millis(1101), RTO); assert_eq!(r.should_retransmit(Instant::from_millis(1101)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1150)), None); assert_eq!(r.should_retransmit(Instant::from_millis(1200)), None); - assert_eq!(r.should_retransmit(Instant::from_millis(1301)), Some(Duration::from_millis(300))); + assert_eq!( + r.should_retransmit(Instant::from_millis(1301)), + Some(Duration::from_millis(300)) + ); r.set_for_idle(Instant::from_millis(1301), None); assert_eq!(r.should_retransmit(Instant::from_millis(1350)), None); } @@ -5692,9 +6712,8 @@ mod test { let mut r = RttEstimator::default(); let rtos = &[ - 751, 766, 755, 731, 697, 656, 613, 567, - 523, 484, 445, 411, 378, 350, 322, 299, - 280, 261, 243, 229, 215, 206, 197, 188 + 751, 766, 755, 731, 697, 656, 613, 567, 523, 484, 445, 411, 378, 350, 322, 299, 280, + 261, 243, 229, 215, 206, 197, 188, ]; for &rto in rtos { @@ -5702,5 +6721,4 @@ mod test { assert_eq!(r.retransmission_timeout(), Duration::from_millis(rto)); } } - } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 750371107..ee57c3779 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -2,12 +2,12 @@ use core::cmp::min; #[cfg(feature = "async")] use core::task::Waker; -use crate::{Error, Result}; -use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context}; -use crate::storage::{PacketBuffer, PacketMetadata}; -use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; +use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::storage::{PacketBuffer, PacketMetadata}; +use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr}; +use crate::{Error, Result}; /// A UDP packet metadata. pub type UdpPacketMetadata = PacketMetadata; @@ -22,7 +22,7 @@ pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>; #[derive(Debug)] pub struct UdpSocket<'a> { pub(crate) meta: SocketMeta, - endpoint: IpEndpoint, + endpoint: IpEndpoint, rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -35,11 +35,10 @@ pub struct UdpSocket<'a> { impl<'a> UdpSocket<'a> { /// Create an UDP socket with the given buffers. - pub fn new(rx_buffer: UdpSocketBuffer<'a>, - tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { + pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { UdpSocket { - meta: SocketMeta::default(), - endpoint: IpEndpoint::default(), + meta: SocketMeta::default(), + endpoint: IpEndpoint::default(), rx_buffer: rx_buffer, tx_buffer: tx_buffer, hop_limit: None, @@ -131,9 +130,13 @@ impl<'a> UdpSocket<'a> { /// if the port in the given endpoint is zero. pub fn bind>(&mut self, endpoint: T) -> Result<()> { let endpoint = endpoint.into(); - if endpoint.port == 0 { return Err(Error::Unaddressable) } + if endpoint.port == 0 { + return Err(Error::Unaddressable); + } - if self.is_open() { return Err(Error::Illegal) } + if self.is_open() { + return Err(Error::Illegal); + } self.endpoint = endpoint; @@ -212,13 +215,22 @@ impl<'a> UdpSocket<'a> { /// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity /// to ever send this packet. pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> { - if self.endpoint.port == 0 { return Err(Error::Unaddressable) } - if !endpoint.is_specified() { return Err(Error::Unaddressable) } + if self.endpoint.port == 0 { + return Err(Error::Unaddressable); + } + if !endpoint.is_specified() { + return Err(Error::Unaddressable); + } let payload_buf = self.tx_buffer.enqueue(size, endpoint)?; - net_trace!("{}:{}:{}: buffer to send {} octets", - self.meta.handle, self.endpoint, endpoint, size); + net_trace!( + "{}:{}:{}: buffer to send {} octets", + self.meta.handle, + self.endpoint, + endpoint, + size + ); Ok(payload_buf) } @@ -237,9 +249,13 @@ impl<'a> UdpSocket<'a> { pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> { let (endpoint, payload_buf) = self.rx_buffer.dequeue()?; - net_trace!("{}:{}:{}: receive {} buffered octets", - self.meta.handle, self.endpoint, - endpoint, payload_buf.len()); + net_trace!( + "{}:{}:{}: receive {} buffered octets", + self.meta.handle, + self.endpoint, + endpoint, + payload_buf.len() + ); Ok((payload_buf, endpoint)) } @@ -263,10 +279,14 @@ impl<'a> UdpSocket<'a> { let handle = self.meta.handle; let endpoint = self.endpoint; self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| { - net_trace!("{}:{}:{}: peek {} buffered octets", - handle, endpoint, - remote_endpoint, payload_buf.len()); - (payload_buf, remote_endpoint) + net_trace!( + "{}:{}:{}: peek {} buffered octets", + handle, + endpoint, + remote_endpoint, + payload_buf.len() + ); + (payload_buf, remote_endpoint) }) } @@ -284,26 +304,46 @@ impl<'a> UdpSocket<'a> { } pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool { - if self.endpoint.port != repr.dst_port { return false } - if !self.endpoint.addr.is_unspecified() && - self.endpoint.addr != ip_repr.dst_addr() && - !ip_repr.dst_addr().is_broadcast() && - !ip_repr.dst_addr().is_multicast() { return false } + if self.endpoint.port != repr.dst_port { + return false; + } + if !self.endpoint.addr.is_unspecified() + && self.endpoint.addr != ip_repr.dst_addr() + && !ip_repr.dst_addr().is_broadcast() + && !ip_repr.dst_addr().is_multicast() + { + return false; + } true } - pub(crate) fn process(&mut self, _cx: &Context, 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(); - let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port }; - self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(payload); - - net_trace!("{}:{}:{}: receiving {} octets", - self.meta.handle, self.endpoint, - endpoint, size); + let endpoint = IpEndpoint { + addr: ip_repr.src_addr(), + port: repr.src_port, + }; + self.rx_buffer + .enqueue(size, endpoint)? + .copy_from_slice(payload); + + net_trace!( + "{}:{}:{}: receiving {} octets", + self.meta.handle, + self.endpoint, + endpoint, + size + ); #[cfg(feature = "async")] self.rx_waker.wake(); @@ -312,29 +352,36 @@ impl<'a> UdpSocket<'a> { } pub(crate) fn dispatch(&mut self, _cx: &Context, emit: F) -> Result<()> - where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> { - let handle = self.handle(); - let endpoint = self.endpoint; + where + F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>, + { + let handle = self.handle(); + let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); - self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| { - net_trace!("{}:{}:{}: sending {} octets", - handle, endpoint, - endpoint, payload_buf.len()); - - let repr = UdpRepr { - src_port: endpoint.port, - dst_port: remote_endpoint.port, - }; - let ip_repr = IpRepr::Unspecified { - src_addr: endpoint.addr, - dst_addr: remote_endpoint.addr, - protocol: IpProtocol::Udp, - payload_len: repr.header_len() + payload_buf.len(), - hop_limit: hop_limit, - }; - emit((ip_repr, repr, payload_buf)) - })?; + self.tx_buffer + .dequeue_with(|remote_endpoint, payload_buf| { + net_trace!( + "{}:{}:{}: sending {} octets", + handle, + endpoint, + endpoint, + payload_buf.len() + ); + + let repr = UdpRepr { + src_port: endpoint.port, + dst_port: remote_endpoint.port, + }; + let ip_repr = IpRepr::Unspecified { + src_addr: endpoint.addr, + dst_addr: remote_endpoint.addr, + protocol: IpProtocol::Udp, + payload_len: repr.header_len() + payload_buf.len(), + hop_limit: hop_limit, + }; + emit((ip_repr, repr, payload_buf)) + })?; #[cfg(feature = "async")] self.tx_waker.wake(); @@ -359,29 +406,39 @@ impl<'a> Into> for UdpSocket<'a> { #[cfg(test)] mod test { - use crate::wire::{IpAddress, IpRepr, UdpRepr}; + use super::*; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; #[cfg(feature = "proto-ipv4")] use crate::wire::Ipv4Repr; #[cfg(feature = "proto-ipv6")] use crate::wire::Ipv6Repr; - use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; - use super::*; + use crate::wire::{IpAddress, IpRepr, UdpRepr}; fn buffer(packets: usize) -> UdpSocketBuffer<'static> { - UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; packets], vec![0; 16 * packets]) + UdpSocketBuffer::new( + vec![UdpPacketMetadata::EMPTY; packets], + vec![0; 16 * packets], + ) } - fn socket(rx_buffer: UdpSocketBuffer<'static>, - tx_buffer: UdpSocketBuffer<'static>) - -> UdpSocket<'static> { + fn socket( + rx_buffer: UdpSocketBuffer<'static>, + tx_buffer: UdpSocketBuffer<'static>, + ) -> UdpSocket<'static> { UdpSocket::new(rx_buffer, tx_buffer) } - const LOCAL_PORT: u16 = 53; - const REMOTE_PORT: u16 = 49500; + const LOCAL_PORT: u16 = 53; + const REMOTE_PORT: u16 = 49500; - pub const LOCAL_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT }; - pub const REMOTE_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT }; + pub const LOCAL_END: IpEndpoint = IpEndpoint { + addr: MOCK_IP_ADDR_1, + port: LOCAL_PORT, + }; + pub const REMOTE_END: IpEndpoint = IpEndpoint { + addr: MOCK_IP_ADDR_2, + port: REMOTE_PORT, + }; pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, @@ -411,7 +468,7 @@ mod test { dst_addr: dst, protocol: IpProtocol::Udp, payload_len: 8 + 6, - hop_limit: 64 + hop_limit: 64, }), #[cfg(feature = "proto-ipv6")] (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr { @@ -419,9 +476,9 @@ mod test { dst_addr: dst, next_header: IpProtocol::Udp, payload_len: 8 + 6, - hop_limit: 64 + hop_limit: 64, }), - _ => unreachable!() + _ => unreachable!(), } } @@ -448,14 +505,31 @@ mod test { #[test] fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); - assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Err(Error::Unaddressable)); + assert_eq!( + socket.send_slice(b"abcdef", REMOTE_END), + Err(Error::Unaddressable) + ); assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert_eq!(socket.send_slice(b"abcdef", - IpEndpoint { addr: IpAddress::Unspecified, ..REMOTE_END }), - Err(Error::Unaddressable)); - assert_eq!(socket.send_slice(b"abcdef", - IpEndpoint { port: 0, ..REMOTE_END }), - Err(Error::Unaddressable)); + assert_eq!( + socket.send_slice( + b"abcdef", + IpEndpoint { + addr: IpAddress::Unspecified, + ..REMOTE_END + } + ), + Err(Error::Unaddressable) + ); + assert_eq!( + socket.send_slice( + b"abcdef", + IpEndpoint { + port: 0, + ..REMOTE_END + } + ), + Err(Error::Unaddressable) + ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(())); } @@ -465,27 +539,38 @@ mod test { assert_eq!(socket.bind(LOCAL_END), Ok(())); assert!(socket.can_send()); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), - Err(Error::Exhausted)); + 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_eq!( + socket.send_slice(b"123456", REMOTE_END), + Err(Error::Exhausted) + ); assert!(!socket.can_send()); - 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); + 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); + Err(Error::Unaddressable) + }), Err(Error::Unaddressable) - }), Err(Error::Unaddressable)); + ); assert!(!socket.can_send()); - 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); + 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); + Ok(()) + }), Ok(()) - }), Ok(())); + ); assert!(socket.can_send()); } @@ -498,13 +583,27 @@ mod test { assert_eq!(socket.recv(), Err(Error::Exhausted)); assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR)); - assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), - Ok(())); + 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(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), - Err(Error::Exhausted)); + 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,8 +615,15 @@ mod test { assert_eq!(socket.peek(), Err(Error::Exhausted)); - assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), - Ok(())); + 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))); assert_eq!(socket.peek(), Err(Error::Exhausted)); @@ -529,8 +635,15 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR)); - assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), - Ok(())); + assert_eq!( + socket.process( + &Context::DUMMY, + &remote_ip_repr(), + &REMOTE_UDP_REPR, + PAYLOAD + ), + Ok(()) + ); let mut slice = [0; 4]; assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END))); @@ -542,8 +655,15 @@ mod test { let mut socket = socket(buffer(1), buffer(0)); assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), - Ok(())); + assert_eq!( + socket.process( + &Context::DUMMY, + &remote_ip_repr(), + &REMOTE_UDP_REPR, + PAYLOAD + ), + Ok(()) + ); let mut slice = [0; 4]; assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END))); @@ -560,16 +680,22 @@ mod test { s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(())); - 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, - protocol: IpProtocol::Udp, - payload_len: 8 + 6, - hop_limit: 0x2a, - }); + 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, + protocol: IpProtocol::Udp, + payload_len: 8 + 6, + hop_limit: 0x2a, + } + ); + Ok(()) + }), Ok(()) - }), Ok(())); + ); } #[test] @@ -593,7 +719,7 @@ mod test { dst_addr: dst, protocol: IpProtocol::Udp, payload_len: 8 + 6, - hop_limit: 64 + hop_limit: 64, }), #[cfg(feature = "proto-ipv6")] (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr { @@ -601,9 +727,9 @@ mod test { dst_addr: dst, next_header: IpProtocol::Udp, payload_len: 8 + 6, - hop_limit: 64 + hop_limit: 64, }), - _ => unreachable!() + _ => unreachable!(), } } @@ -623,8 +749,11 @@ mod test { assert_eq!(socket.bind(LOCAL_END), Ok(())); let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx"; - assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Truncated)); - assert_eq!(socket.send_slice(&too_large[..16*4], REMOTE_END), Ok(())); + assert_eq!( + socket.send_slice(too_large, REMOTE_END), + Err(Error::Truncated) + ); + assert_eq!(socket.send_slice(&too_large[..16 * 4], REMOTE_END), Ok(())); } #[test] @@ -637,7 +766,10 @@ mod test { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, }; - assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), Ok(())); + assert_eq!( + socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), + Ok(()) + ); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); } diff --git a/src/socket/waker.rs b/src/socket/waker.rs index 4dfb44b4e..4f4219788 100644 --- a/src/socket/waker.rs +++ b/src/socket/waker.rs @@ -30,4 +30,4 @@ impl WakerRegistration { pub fn wake(&mut self) { self.waker.take().map(|w| w.wake()); } -} \ No newline at end of file +} diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 1eb97bcf8..316beeda3 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -8,29 +8,44 @@ pub struct TooManyHolesError; #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Contig { hole_size: usize, - data_size: usize + data_size: usize, } impl fmt::Display for Contig { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.has_hole() { write!(f, "({})", self.hole_size)?; } - if self.has_hole() && self.has_data() { write!(f, " ")?; } - if self.has_data() { write!(f, "{}", self.data_size)?; } + if self.has_hole() { + write!(f, "({})", self.hole_size)?; + } + if self.has_hole() && self.has_data() { + write!(f, " ")?; + } + if self.has_data() { + write!(f, "{}", self.data_size)?; + } Ok(()) } } impl Contig { fn empty() -> Contig { - Contig { hole_size: 0, data_size: 0 } + Contig { + hole_size: 0, + data_size: 0, + } } fn hole(size: usize) -> Contig { - Contig { hole_size: size, data_size: 0 } + Contig { + hole_size: size, + data_size: 0, + } } fn hole_and_data(hole_size: usize, data_size: usize) -> Contig { - Contig { hole_size, data_size } + Contig { + hole_size, + data_size, + } } fn has_hole(&self) -> bool { @@ -66,10 +81,10 @@ impl Contig { } } -#[cfg(feature = "std")] -use std::boxed::Box; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::boxed::Box; +#[cfg(feature = "std")] +use std::boxed::Box; #[cfg(any(feature = "std", feature = "alloc"))] const CONTIG_COUNT: usize = 32; @@ -93,7 +108,9 @@ impl fmt::Display for Assembler { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[ ")?; for contig in self.contigs.iter() { - if contig.is_empty() { break } + if contig.is_empty() { + break; + } write!(f, "{} ", contig)?; } write!(f, "]")?; @@ -115,10 +132,7 @@ impl Assembler { /// FIXME(whitequark): remove this once I'm certain enough that the assembler works well. #[allow(dead_code)] pub(crate) fn total_size(&self) -> usize { - self.contigs - .iter() - .map(|contig| contig.total_size()) - .sum() + self.contigs.iter().map(|contig| contig.total_size()).sum() } fn front(&self) -> Contig { @@ -143,7 +157,7 @@ impl Assembler { self.contigs[i] = self.contigs[i + 1]; if !self.contigs[i].has_data() { self.contigs[i + 1] = Contig::empty(); - return &mut self.contigs[i] + return &mut self.contigs[i]; } } @@ -156,7 +170,9 @@ impl Assembler { fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> { debug_assert!(!self.contigs[at].is_empty()); - if !self.back().is_empty() { return Err(TooManyHolesError) } + if !self.back().is_empty() { + return Err(TooManyHolesError); + } for i in (at + 1..self.contigs.len()).rev() { self.contigs[i] = self.contigs[i - 1]; @@ -201,11 +217,11 @@ impl Assembler { // The range being added covers a part of the hole but not of the data // in this contig, add a new contig containing the range. { - let inserted = self.add_contig_at(index)?; - *inserted = Contig::hole_and_data(offset, size); + let inserted = self.add_contig_at(index)?; + *inserted = Contig::hole_and_data(offset, size); } // Previous contigs[index] got moved to contigs[index+1] - self.contigs[index+1].shrink_hole_by(offset + size); + self.contigs[index + 1].shrink_hole_by(offset + size); index += 2; } else { unreachable!() @@ -215,7 +231,7 @@ impl Assembler { if offset >= contig.total_size() { offset = offset.saturating_sub(contig.total_size()); } else { - size = (offset + size).saturating_sub(contig.total_size()); + size = (offset + size).saturating_sub(contig.total_size()); offset = 0; } } @@ -258,7 +274,7 @@ pub struct AssemblerIter<'a> { offset: usize, index: usize, left: usize, - right: usize + right: usize, } impl<'a> AssemblerIter<'a> { @@ -268,7 +284,7 @@ impl<'a> AssemblerIter<'a> { offset: offset, index: 0, left: 0, - right: 0 + right: 0, } } } @@ -297,8 +313,8 @@ impl<'a> Iterator for AssemblerIter<'a> { #[cfg(test)] mod test { - use std::vec::Vec; use super::*; + use std::vec::Vec; impl From> for Assembler { fn from(vec: Vec<(usize, usize)>) -> Assembler { @@ -307,7 +323,10 @@ mod test { #[cfg(any(feature = "std", feature = "alloc"))] let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]); for (i, &(hole_size, data_size)) in vec.iter().enumerate() { - contigs[i] = Contig { hole_size, data_size }; + contigs[i] = Contig { + hole_size, + data_size, + }; } Assembler { contigs } } @@ -412,9 +431,9 @@ mod test { #[test] fn test_rejected_add_keeps_state() { - let mut assr = Assembler::new(CONTIG_COUNT*20); - for c in 1..=CONTIG_COUNT-1 { - assert_eq!(assr.add(c*10, 3), Ok(())); + let mut assr = Assembler::new(CONTIG_COUNT * 20); + for c in 1..=CONTIG_COUNT - 1 { + assert_eq!(assr.add(c * 10, 3), Ok(())); } // Maximum of allowed holes is reached let assr_before = assr.clone(); @@ -440,7 +459,6 @@ mod test { let mut assr = contigs![(0, 4), (4, 4)]; assert_eq!(assr.remove_front(), Some(4)); assert_eq!(assr, contigs![(4, 4), (4, 0)]); - } #[test] diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 8b9fbe68e..7e1150264 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -6,12 +6,12 @@ or `alloc` crates being available, and heap-allocated memory. */ mod assembler; -mod ring_buffer; mod packet_buffer; +mod ring_buffer; pub use self::assembler::Assembler; -pub use self::ring_buffer::RingBuffer; pub use self::packet_buffer::{PacketBuffer, PacketMetadata}; +pub use self::ring_buffer::RingBuffer; /// A trait for setting a value to a known state. /// diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index b35d0ab68..5bb168136 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -1,31 +1,34 @@ use managed::ManagedSlice; -use crate::{Error, Result}; use crate::storage::RingBuffer; +use crate::{Error, Result}; /// Size and header of a packet. #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PacketMetadata { - size: usize, - header: Option + size: usize, + header: Option, } impl PacketMetadata { /// Empty packet description. - pub const EMPTY: PacketMetadata = PacketMetadata { size: 0, header: None }; + pub const EMPTY: PacketMetadata = PacketMetadata { + size: 0, + header: None, + }; fn padding(size: usize) -> PacketMetadata { PacketMetadata { - size: size, - header: None + size: size, + header: None, } } fn packet(size: usize, header: H) -> PacketMetadata { PacketMetadata { - size: size, - header: Some(header) + size: size, + header: Some(header), } } @@ -36,9 +39,9 @@ impl PacketMetadata { /// An UDP packet ring buffer. #[derive(Debug)] -pub struct PacketBuffer<'a, H: 'a> { +pub struct PacketBuffer<'a, H: 'a> { metadata_ring: RingBuffer<'a, PacketMetadata>, - payload_ring: RingBuffer<'a, u8>, + payload_ring: RingBuffer<'a, u8>, } impl<'a, H> PacketBuffer<'a, H> { @@ -47,12 +50,13 @@ impl<'a, H> PacketBuffer<'a, H> { /// Metadata storage limits the maximum _number_ of packets in the buffer and payload /// storage limits the maximum _total size_ of packets. pub fn new(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, H> - where MS: Into>>, - PS: Into>, + where + MS: Into>>, + PS: Into>, { PacketBuffer { metadata_ring: RingBuffer::new(metadata_storage), - payload_ring: RingBuffer::new(payload_storage), + payload_ring: RingBuffer::new(payload_storage), } } @@ -75,25 +79,25 @@ impl<'a, H> PacketBuffer<'a, H> { /// does not have enough spare payload space. pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> { if self.payload_ring.capacity() < size { - return Err(Error::Truncated) + return Err(Error::Truncated); } if self.metadata_ring.is_full() { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } let window = self.payload_ring.window(); let contig_window = self.payload_ring.contiguous_window(); if window < size { - return Err(Error::Exhausted) + return Err(Error::Exhausted); } else if contig_window < size { if window - contig_window < size { // The buffer length is larger than the current contiguous window // and is larger than the contiguous window will be after adding // the padding necessary to circle around to the beginning of the // ring buffer. - return Err(Error::Exhausted) + return Err(Error::Exhausted); } else { // Add padding to the end of the ring buffer so that the // contiguous window is at the beginning of the ring buffer. @@ -110,7 +114,10 @@ impl<'a, H> PacketBuffer<'a, H> { } fn dequeue_padding(&mut self) { - let Self { ref mut metadata_ring, ref mut payload_ring } = *self; + let Self { + ref mut metadata_ring, + ref mut payload_ring, + } = *self; let _ = metadata_ring.dequeue_one_with(|metadata| { if metadata.is_padding() { @@ -125,22 +132,32 @@ impl<'a, H> PacketBuffer<'a, H> { /// Call `f` with a single packet from the buffer, and dequeue the packet if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result - where F: FnOnce(&mut H, &'c mut [u8]) -> Result { + where + F: FnOnce(&mut H, &'c mut [u8]) -> Result, + { self.dequeue_padding(); - let Self { ref mut metadata_ring, ref mut payload_ring } = *self; + let Self { + ref mut metadata_ring, + ref mut payload_ring, + } = *self; metadata_ring.dequeue_one_with(move |metadata| { - let PacketMetadata { ref mut header, size } = *metadata; - - payload_ring.dequeue_many_with(|payload_buf| { - debug_assert!(payload_buf.len() >= size); - - match f(header.as_mut().unwrap(), &mut payload_buf[..size]) { - Ok(val) => (size, Ok(val)), - Err(err) => (0, Err(err)), - } - }).1 + let PacketMetadata { + ref mut header, + size, + } = *metadata; + + payload_ring + .dequeue_many_with(|payload_buf| { + debug_assert!(payload_buf.len() >= size); + + match f(header.as_mut().unwrap(), &mut payload_buf[..size]) { + Ok(val) => (size, Ok(val)), + Err(err) => (0, Err(err)), + } + }) + .1 }) } @@ -149,7 +166,10 @@ impl<'a, H> PacketBuffer<'a, H> { pub fn dequeue(&mut self) -> Result<(H, &mut [u8])> { self.dequeue_padding(); - let PacketMetadata { ref mut header, size } = *self.metadata_ring.dequeue_one()?; + let PacketMetadata { + ref mut header, + size, + } = *self.metadata_ring.dequeue_one()?; let payload_buf = self.payload_ring.dequeue_many(size); debug_assert!(payload_buf.len() == size); @@ -164,7 +184,10 @@ impl<'a, H> PacketBuffer<'a, H> { self.dequeue_padding(); if let Some(metadata) = self.metadata_ring.get_allocated(0, 1).first() { - Ok((metadata.header.as_ref().unwrap(), self.payload_ring.get_allocated(0, metadata.size))) + Ok(( + metadata.header.as_ref().unwrap(), + self.payload_ring.get_allocated(0, metadata.size), + )) } else { Err(Error::Exhausted) } @@ -193,8 +216,7 @@ mod test { use super::*; fn buffer() -> PacketBuffer<'static, ()> { - PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], - vec![0u8; 16]) + PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; 16]) } #[test] @@ -237,7 +259,10 @@ mod test { let mut buffer = buffer(); assert!(buffer.enqueue(12, ()).is_ok()); assert!(buffer.dequeue().is_ok()); - buffer.enqueue(12, ()).unwrap().copy_from_slice(b"abcdefghijkl"); + buffer + .enqueue(12, ()) + .unwrap() + .copy_from_slice(b"abcdefghijkl"); } #[test] @@ -250,13 +275,17 @@ mod test { assert_eq!(buffer.metadata_ring.len(), 3); assert!(buffer.dequeue().is_ok()); - assert!(buffer.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>).is_err()); + assert!(buffer + .dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>) + .is_err()); assert_eq!(buffer.metadata_ring.len(), 1); - assert!(buffer.dequeue_with(|&mut (), payload| { - assert_eq!(payload, &b"abcd"[..]); - Ok(()) - }).is_ok()); + assert!(buffer + .dequeue_with(|&mut (), payload| { + assert_eq!(payload, &b"abcd"[..]); + Ok(()) + }) + .is_ok()); assert_eq!(buffer.metadata_ring.len(), 0); } diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 063c9f3ae..e802ad122 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -4,8 +4,8 @@ use core::cmp; use managed::ManagedSlice; -use crate::{Error, Result}; use crate::storage::Resettable; +use crate::{Error, Result}; /// A ring buffer. /// @@ -25,7 +25,7 @@ use crate::storage::Resettable; pub struct RingBuffer<'a, T: 'a> { storage: ManagedSlice<'a, T>, read_at: usize, - length: usize, + length: usize, } impl<'a, T: 'a> RingBuffer<'a, T> { @@ -33,19 +33,20 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// /// During creation, every element in `storage` is reset. pub fn new(storage: S) -> RingBuffer<'a, T> - where S: Into>, + where + S: Into>, { RingBuffer { storage: storage.into(), read_at: 0, - length: 0, + length: 0, } } /// Clear the ring buffer. pub fn clear(&mut self) { self.read_at = 0; - self.length = 0; + self.length = 0; } /// Return the maximum number of elements in the ring buffer. @@ -55,7 +56,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Clear the ring buffer, and reset every element. pub fn reset(&mut self) - where T: Resettable { + where + T: Resettable, + { self.clear(); for elem in self.storage.iter_mut() { elem.reset(); @@ -112,8 +115,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Call `f` with a single buffer element, and enqueue the element if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is full. pub fn enqueue_one_with<'b, R, F>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut T) -> Result { - if self.is_full() { return Err(Error::Exhausted) } + where + F: FnOnce(&'b mut T) -> Result, + { + if self.is_full() { + return Err(Error::Exhausted); + } let index = self.get_idx_unchecked(self.length); match f(&mut self.storage[index]) { @@ -121,7 +128,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { self.length += 1; Ok(result) } - Err(error) => Err(error) + Err(error) => Err(error), } } @@ -136,8 +143,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Call `f` with a single buffer element, and dequeue the element if `f` /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result - where F: FnOnce(&'b mut T) -> Result { - if self.is_empty() { return Err(Error::Exhausted) } + where + F: FnOnce(&'b mut T) -> Result, + { + if self.is_empty() { + return Err(Error::Exhausted); + } let next_at = self.get_idx_unchecked(1); match f(&mut self.storage[self.read_at]) { @@ -146,7 +157,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { self.read_at = next_at; Ok(result) } - Err(error) => Err(error) + Err(error) => Err(error), } } @@ -169,7 +180,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// This function panics if the amount of elements returned by `f` is larger /// than the size of the slice passed into it. pub fn enqueue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R) - where F: FnOnce(&'b mut [T]) -> (usize, R) { + where + F: FnOnce(&'b mut [T]) -> (usize, R), + { if self.length == 0 { // Ring is currently empty. Reset `read_at` to optimize // for contiguous space. @@ -194,14 +207,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> { self.enqueue_many_with(|buf| { let size = cmp::min(size, buf.len()); (size, &mut buf[..size]) - }).1 + }) + .1 } /// Enqueue as many elements from the given slice into the buffer as possible, /// and return the amount of elements that could fit. // #[must_use] pub fn enqueue_slice(&mut self, data: &[T]) -> usize - where T: Copy { + where + T: Copy, + { let (size_1, data) = self.enqueue_many_with(|buf| { let size = cmp::min(buf.len(), data.len()); buf[..size].copy_from_slice(&data[..size]); @@ -222,7 +238,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// This function panics if the amount of elements returned by `f` is larger /// than the size of the slice passed into it. pub fn dequeue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R) - where F: FnOnce(&'b mut [T]) -> (usize, R) { + where + F: FnOnce(&'b mut [T]) -> (usize, R), + { let capacity = self.capacity(); let max_size = cmp::min(self.len(), capacity - self.read_at); let (size, result) = f(&mut self.storage[self.read_at..self.read_at + max_size]); @@ -246,14 +264,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> { self.dequeue_many_with(|buf| { let size = cmp::min(size, buf.len()); (size, &mut buf[..size]) - }).1 + }) + .1 } /// Dequeue as many elements from the buffer into the given slice as possible, /// and return the amount of elements that could fit. // #[must_use] pub fn dequeue_slice(&mut self, data: &mut [T]) -> usize - where T: Copy { + where + T: Copy, + { let (size_1, data) = self.dequeue_many_with(|buf| { let size = cmp::min(buf.len(), data.len()); data[..size].copy_from_slice(&buf[..size]); @@ -277,13 +298,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> { pub fn get_unallocated(&mut self, offset: usize, mut size: usize) -> &mut [T] { let start_at = self.get_idx(self.length + offset); // We can't access past the end of unallocated data. - if offset > self.window() { return &mut [] } + if offset > self.window() { + return &mut []; + } // We can't enqueue more than there is free space. let clamped_window = self.window() - offset; - if size > clamped_window { size = clamped_window } + if size > clamped_window { + size = clamped_window + } // We can't contiguously enqueue past the end of the storage. let until_end = self.capacity() - start_at; - if size > until_end { size = until_end } + if size > until_end { + size = until_end + } &mut self.storage[start_at..start_at + size] } @@ -293,7 +320,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// the amount written. // #[must_use] pub fn write_unallocated(&mut self, offset: usize, data: &[T]) -> usize - where T: Copy { + where + T: Copy, + { let (size_1, offset, data) = { let slice = self.get_unallocated(offset, data.len()); let slice_len = slice.len(); @@ -324,13 +353,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> { pub fn get_allocated(&self, offset: usize, mut size: usize) -> &[T] { let start_at = self.get_idx(offset); // We can't read past the end of the allocated data. - if offset > self.length { return &mut [] } + if offset > self.length { + return &mut []; + } // We can't read more than we have allocated. let clamped_length = self.length - offset; - if size > clamped_length { size = clamped_length } + if size > clamped_length { + size = clamped_length + } // We can't contiguously dequeue past the end of the storage. let until_end = self.capacity() - start_at; - if size > until_end { size = until_end } + if size > until_end { + size = until_end + } &self.storage[start_at..start_at + size] } @@ -340,7 +375,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// the amount read. // #[must_use] pub fn read_allocated(&mut self, offset: usize, data: &mut [T]) -> usize - where T: Copy { + where + T: Copy, + { let (size_1, offset, data) = { let slice = self.get_allocated(offset, data.len()); data[..slice.len()].copy_from_slice(slice); @@ -402,8 +439,10 @@ mod test { #[test] fn test_buffer_enqueue_dequeue_one_with() { let mut ring = RingBuffer::new(vec![0; 5]); - assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted)); + assert_eq!( + ring.dequeue_one_with(|_| unreachable!()) as Result<()>, + Err(Error::Exhausted) + ); ring.enqueue_one_with(Ok).unwrap(); assert!(!ring.is_empty()); @@ -414,15 +453,19 @@ mod test { assert!(!ring.is_empty()); } assert!(ring.is_full()); - assert_eq!(ring.enqueue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted)); + assert_eq!( + ring.enqueue_one_with(|_| unreachable!()) as Result<()>, + Err(Error::Exhausted) + ); for i in 0..5 { assert_eq!(ring.dequeue_one_with(|e| Ok(*e)).unwrap(), i); assert!(!ring.is_full()); } - assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted)); + assert_eq!( + ring.dequeue_one_with(|_| unreachable!()) as Result<()>, + Err(Error::Exhausted) + ); assert!(ring.is_empty()); } @@ -454,11 +497,14 @@ mod test { fn test_buffer_enqueue_many_with() { let mut ring = RingBuffer::new(vec![b'.'; 12]); - assert_eq!(ring.enqueue_many_with(|buf| { - assert_eq!(buf.len(), 12); - buf[0..2].copy_from_slice(b"ab"); + assert_eq!( + ring.enqueue_many_with(|buf| { + assert_eq!(buf.len(), 12); + buf[0..2].copy_from_slice(b"ab"); + (2, true) + }), (2, true) - }), (2, true)); + ); assert_eq!(ring.len(), 2); assert_eq!(&ring.storage[..], b"ab.........."); @@ -545,12 +591,15 @@ mod test { assert_eq!(ring.enqueue_slice(b"abcdefghijkl"), 12); - assert_eq!(ring.dequeue_many_with(|buf| { - assert_eq!(buf.len(), 12); - assert_eq!(buf, b"abcdefghijkl"); - buf[..4].copy_from_slice(b"...."); + assert_eq!( + ring.dequeue_many_with(|buf| { + assert_eq!(buf.len(), 12); + assert_eq!(buf, b"abcdefghijkl"); + buf[..4].copy_from_slice(b"...."); + (4, true) + }), (4, true) - }), (4, true)); + ); assert_eq!(ring.len(), 8); assert_eq!(&ring.storage[..], b"....efghijkl"); @@ -679,7 +728,7 @@ mod test { let mut ring = RingBuffer::new(vec![b'.'; 12]); assert_eq!(ring.get_allocated(16, 4), b""); - assert_eq!(ring.get_allocated(0, 4), b""); + assert_eq!(ring.get_allocated(0, 4), b""); ring.enqueue_slice(b"abcd"); assert_eq!(ring.get_allocated(0, 8), b"abcd"); @@ -711,7 +760,6 @@ mod test { let mut data = [0; 6]; assert_eq!(ring.read_allocated(6, &mut data[..]), 3); assert_eq!(&data[..], b"mno\x00\x00\x00"); - } #[test] diff --git a/src/time.rs b/src/time.rs index 724f80403..ebf13d3a6 100644 --- a/src/time.rs +++ b/src/time.rs @@ -10,7 +10,7 @@ absolute and relative time. [Duration]: struct.Duration.html */ -use core::{ops, fmt}; +use core::{fmt, ops}; /// A representation of an absolute time value. /// @@ -30,12 +30,16 @@ pub struct Instant { impl Instant { /// Create a new `Instant` from a number of milliseconds. pub fn from_millis>(millis: T) -> Instant { - Instant { millis: millis.into() } + Instant { + millis: millis.into(), + } } /// Create a new `Instant` from a number of seconds. pub fn from_secs>(secs: T) -> Instant { - Instant { millis: secs.into() * 1000 } + Instant { + millis: secs.into() * 1000, + } } /// Create a new `Instant` from the current [std::time::SystemTime]. @@ -79,7 +83,8 @@ impl From<::std::time::Instant> for Instant { #[cfg(feature = "std")] impl From<::std::time::SystemTime> for Instant { fn from(other: ::std::time::SystemTime) -> Instant { - let n = other.duration_since(::std::time::UNIX_EPOCH) + let n = other + .duration_since(::std::time::UNIX_EPOCH) .expect("start time must not be before the unix epoch"); Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64) } @@ -149,7 +154,9 @@ impl Duration { /// Create a new `Instant` from a number of seconds. pub const fn from_secs(secs: u64) -> Duration { - Duration { millis: secs * 1000 } + Duration { + millis: secs * 1000, + } } /// The fractional number of milliseconds in this `Duration`. @@ -193,14 +200,19 @@ impl ops::Sub for Duration { fn sub(self, rhs: Duration) -> Duration { Duration::from_millis( - self.millis.checked_sub(rhs.total_millis()).expect("overflow when subtracting durations")) + self.millis + .checked_sub(rhs.total_millis()) + .expect("overflow when subtracting durations"), + ) } } impl ops::SubAssign for Duration { fn sub_assign(&mut self, rhs: Duration) { - self.millis = self.millis.checked_sub( - rhs.total_millis()).expect("overflow when subtracting durations"); + self.millis = self + .millis + .checked_sub(rhs.total_millis()) + .expect("overflow when subtracting durations"); } } @@ -234,7 +246,7 @@ impl ops::DivAssign for Duration { impl ops::Shl for Duration { type Output = Duration; - + fn shl(self, rhs: u32) -> Duration { Duration::from_millis(self.millis << rhs) } @@ -248,7 +260,7 @@ impl ops::ShlAssign for Duration { impl ops::Shr for Duration { type Output = Duration; - + fn shr(self, rhs: u32) -> Duration { Duration::from_millis(self.millis >> rhs) } @@ -262,17 +274,13 @@ impl ops::ShrAssign for Duration { impl From<::core::time::Duration> for Duration { fn from(other: ::core::time::Duration) -> Duration { - Duration::from_millis( - other.as_secs() * 1000 + other.subsec_millis() as u64 - ) + Duration::from_millis(other.as_secs() * 1000 + other.subsec_millis() as u64) } } impl Into<::core::time::Duration> for Duration { fn into(self) -> ::core::time::Duration { - ::core::time::Duration::from_millis( - self.total_millis() - ) + ::core::time::Duration::from_millis(self.total_millis()) } } @@ -283,9 +291,15 @@ mod test { #[test] fn test_instant_ops() { // std::ops::Add - assert_eq!(Instant::from_millis(4) + Duration::from_millis(6), Instant::from_millis(10)); + assert_eq!( + Instant::from_millis(4) + Duration::from_millis(6), + Instant::from_millis(10) + ); // std::ops::Sub - assert_eq!(Instant::from_millis(7) - Duration::from_millis(5), Instant::from_millis(2)); + assert_eq!( + Instant::from_millis(7) - Duration::from_millis(5), + Instant::from_millis(2) + ); } #[test] @@ -306,19 +320,30 @@ mod test { #[cfg(feature = "std")] fn test_instant_conversions() { let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into(); - assert_eq!(Instant::from(::std::time::UNIX_EPOCH), - Instant::from_millis(0)); + assert_eq!( + Instant::from(::std::time::UNIX_EPOCH), + Instant::from_millis(0) + ); assert_eq!(epoc, ::std::time::UNIX_EPOCH); epoc = Instant::from_millis(2085955200i64 * 1000).into(); - assert_eq!(epoc, ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)); + assert_eq!( + epoc, + ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200) + ); } #[test] fn test_duration_ops() { // std::ops::Add - assert_eq!(Duration::from_millis(40) + Duration::from_millis(2), Duration::from_millis(42)); + assert_eq!( + Duration::from_millis(40) + Duration::from_millis(2), + Duration::from_millis(42) + ); // std::ops::Sub - assert_eq!(Duration::from_millis(555) - Duration::from_millis(42), Duration::from_millis(513)); + assert_eq!( + Duration::from_millis(555) - Duration::from_millis(42), + Duration::from_millis(513) + ); // std::ops::Mul assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286)); // std::ops::Div diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 3bf394749..48d64ca69 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -1,5 +1,5 @@ -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; use crate::{Error, Result}; @@ -24,7 +24,7 @@ enum_with_unknown! { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } mod field { @@ -34,9 +34,9 @@ mod field { pub const HTYPE: Field = 0..2; pub const PTYPE: Field = 2..4; - pub const HLEN: usize = 4; - pub const PLEN: usize = 5; - pub const OPER: Field = 6..8; + pub const HLEN: usize = 4; + pub const PLEN: usize = 5; + pub const OPER: Field = 6..8; #[inline] pub fn SHA(hardware_len: u8, _protocol_len: u8) -> Field { @@ -263,7 +263,7 @@ pub enum Repr { source_hardware_addr: EthernetAddress, source_protocol_addr: Ipv4Address, target_hardware_addr: EthernetAddress, - target_protocol_addr: Ipv4Address + target_protocol_addr: Ipv4Address, }, } @@ -271,22 +271,20 @@ impl Repr { /// Parse an Address Resolution Protocol packet and return a high-level representation, /// or return `Err(Error::Unrecognized)` if the packet is not recognized. pub fn parse>(packet: &Packet) -> Result { - match (packet.hardware_type(), packet.protocol_type(), - packet.hardware_len(), packet.protocol_len()) { - (Hardware::Ethernet, Protocol::Ipv4, 6, 4) => { - Ok(Repr::EthernetIpv4 { - operation: packet.operation(), - source_hardware_addr: - EthernetAddress::from_bytes(packet.source_hardware_addr()), - source_protocol_addr: - Ipv4Address::from_bytes(packet.source_protocol_addr()), - target_hardware_addr: - EthernetAddress::from_bytes(packet.target_hardware_addr()), - target_protocol_addr: - Ipv4Address::from_bytes(packet.target_protocol_addr()) - }) - }, - _ => Err(Error::Unrecognized) + match ( + packet.hardware_type(), + packet.protocol_type(), + packet.hardware_len(), + packet.protocol_len(), + ) { + (Hardware::Ethernet, Protocol::Ipv4, 6, 4) => Ok(Repr::EthernetIpv4 { + operation: packet.operation(), + source_hardware_addr: EthernetAddress::from_bytes(packet.source_hardware_addr()), + source_protocol_addr: Ipv4Address::from_bytes(packet.source_protocol_addr()), + target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()), + target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()), + }), + _ => Err(Error::Unrecognized), } } @@ -302,8 +300,10 @@ impl Repr { match *self { Repr::EthernetIpv4 { operation, - source_hardware_addr, source_protocol_addr, - target_hardware_addr, target_protocol_addr + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, } => { packet.set_hardware_type(Hardware::Ethernet); packet.set_protocol_type(Protocol::Ipv4); @@ -314,7 +314,7 @@ impl Repr { packet.set_source_protocol_addr(source_protocol_addr.as_bytes()); packet.set_target_hardware_addr(target_hardware_addr.as_bytes()); packet.set_target_protocol_addr(target_protocol_addr.as_bytes()); - }, + } } } } @@ -325,13 +325,23 @@ impl> fmt::Display for Packet { Ok(repr) => write!(f, "{}", repr), _ => { write!(f, "ARP (unrecognized)")?; - write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}", - self.hardware_type(), self.protocol_type(), - self.hardware_len(), self.protocol_len(), - self.operation())?; - write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}", - self.source_hardware_addr(), self.source_protocol_addr(), - self.target_hardware_addr(), self.target_protocol_addr())?; + write!( + f, + " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}", + self.hardware_type(), + self.protocol_type(), + self.hardware_len(), + self.protocol_len(), + self.operation() + )?; + write!( + f, + " sha={:?} spa={:?} tha={:?} tpa={:?}", + self.source_hardware_addr(), + self.source_protocol_addr(), + self.target_hardware_addr(), + self.target_protocol_addr() + )?; Ok(()) } } @@ -343,26 +353,36 @@ impl fmt::Display for Repr { match *self { Repr::EthernetIpv4 { operation, - source_hardware_addr, source_protocol_addr, - target_hardware_addr, target_protocol_addr + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, } => { - write!(f, "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", - source_hardware_addr, source_protocol_addr, - target_hardware_addr, target_protocol_addr, - operation) - }, + write!( + f, + "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, + operation + ) + } } } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { match Packet::new_checked(buffer) { Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet) + Ok(packet) => write!(f, "{}{}", indent, packet), } } } @@ -371,16 +391,10 @@ impl> PrettyPrint for Packet { mod test { use super::*; - static PACKET_BYTES: [u8; 28] = - [0x00, 0x01, - 0x08, 0x00, - 0x06, - 0x04, - 0x00, 0x01, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x21, 0x22, 0x23, 0x24, - 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - 0x41, 0x42, 0x43, 0x44]; + static PACKET_BYTES: [u8; 28] = [ + 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21, + 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44, + ]; #[test] fn test_deconstruct() { @@ -390,9 +404,15 @@ mod test { assert_eq!(packet.hardware_len(), 6); assert_eq!(packet.protocol_len(), 4); assert_eq!(packet.operation(), Operation::Request); - assert_eq!(packet.source_hardware_addr(), &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]); + assert_eq!( + packet.source_hardware_addr(), + &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16] + ); assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]); - assert_eq!(packet.target_hardware_addr(), &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]); + assert_eq!( + packet.target_hardware_addr(), + &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36] + ); assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]); } @@ -415,14 +435,14 @@ mod test { fn packet_repr() -> Repr { Repr::EthernetIpv4 { operation: Operation::Request, - source_hardware_addr: - EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]), - source_protocol_addr: - Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]), - target_hardware_addr: - EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]), - target_protocol_addr: - Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]) + source_hardware_addr: EthernetAddress::from_bytes(&[ + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + ]), + source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]), + target_hardware_addr: EthernetAddress::from_bytes(&[ + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + ]), + target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]), } } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 8cb1c11ae..cbb5f1e89 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -2,9 +2,9 @@ use byteorder::{ByteOrder, NetworkEndian}; -use crate::{Error, Result}; -use crate::wire::{EthernetAddress, Ipv4Address}; use crate::wire::arp::Hardware; +use crate::wire::{EthernetAddress, Ipv4Address}; +use crate::{Error, Result}; pub const SERVER_PORT: u16 = 67; pub const CLIENT_PORT: u16 = 68; @@ -37,8 +37,11 @@ enum_with_unknown! { impl MessageType { fn opcode(&self) -> OpCode { match *self { - MessageType::Discover | MessageType::Inform | MessageType::Request | - MessageType::Decline | MessageType::Release => OpCode::Request, + MessageType::Discover + | MessageType::Inform + | MessageType::Request + | MessageType::Decline + | MessageType::Release => OpCode::Request, MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply, MessageType::Unknown(_) => OpCode::Unknown(0), } @@ -59,7 +62,7 @@ pub enum DhcpOption<'a> { Router(Ipv4Address), SubnetMask(Ipv4Address), MaximumDhcpMessageSize(u16), - Other { kind: u8, data: &'a [u8] } + Other { kind: u8, data: &'a [u8] }, } impl<'a> DhcpOption<'a> { @@ -81,12 +84,10 @@ impl<'a> DhcpOption<'a> { skip_len = length + 2; let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?; match (kind, length) { - (field::OPT_END, _) | - (field::OPT_PAD, _) => - unreachable!(), + (field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(), (field::OPT_DHCP_MESSAGE_TYPE, 1) => { option = DhcpOption::MessageType(MessageType::from(data[0])); - }, + } (field::OPT_REQUESTED_IP, 4) => { option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data)); } @@ -95,7 +96,8 @@ impl<'a> DhcpOption<'a> { if hardware_type != Hardware::Ethernet { return Err(Error::Unrecognized); } - option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..])); + option = + DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..])); } (field::OPT_SERVER_IDENTIFIER, 4) => { option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data)); @@ -107,13 +109,20 @@ impl<'a> DhcpOption<'a> { option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data)); } (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { - option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]])); + option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([ + data[0], data[1], + ])); } (field::OPT_IP_LEASE_TIME, 4) => { - option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) + option = DhcpOption::IpLeaseTime(u32::from_be_bytes([ + data[0], data[1], data[2], data[3], + ])) } (_, _) => { - option = DhcpOption::Other { kind: kind, data: data }; + option = DhcpOption::Other { + kind: kind, + data: data, + }; } } } @@ -126,20 +135,14 @@ impl<'a> DhcpOption<'a> { &DhcpOption::EndOfList => 1, &DhcpOption::Pad => 1, &DhcpOption::MessageType(_) => 3, - &DhcpOption::ClientIdentifier(eth_addr) => { - 3 + eth_addr.as_bytes().len() - } - &DhcpOption::RequestedIp(ip) | - &DhcpOption::ServerIdentifier(ip) | - &DhcpOption::Router(ip) | - &DhcpOption::SubnetMask(ip) => { - 2 + ip.as_bytes().len() - }, - &DhcpOption::MaximumDhcpMessageSize(_) => { - 4 - } + &DhcpOption::ClientIdentifier(eth_addr) => 3 + eth_addr.as_bytes().len(), + &DhcpOption::RequestedIp(ip) + | &DhcpOption::ServerIdentifier(ip) + | &DhcpOption::Router(ip) + | &DhcpOption::SubnetMask(ip) => 2 + ip.as_bytes().len(), + &DhcpOption::MaximumDhcpMessageSize(_) => 4, &DhcpOption::IpLeaseTime(_) => 6, - &DhcpOption::Other { data, .. } => 2 + data.len() + &DhcpOption::Other { data, .. } => 2 + data.len(), } } @@ -168,19 +171,19 @@ impl<'a> DhcpOption<'a> { buffer[2] = u16::from(Hardware::Ethernet) as u8; buffer[3..9].copy_from_slice(eth_addr.as_bytes()); } - DhcpOption::RequestedIp(ip) => { + DhcpOption::RequestedIp(ip) => { buffer[0] = field::OPT_REQUESTED_IP; buffer[2..6].copy_from_slice(ip.as_bytes()); } - DhcpOption::ServerIdentifier(ip) => { + DhcpOption::ServerIdentifier(ip) => { buffer[0] = field::OPT_SERVER_IDENTIFIER; buffer[2..6].copy_from_slice(ip.as_bytes()); } - DhcpOption::Router(ip) => { + DhcpOption::Router(ip) => { buffer[0] = field::OPT_ROUTER; buffer[2..6].copy_from_slice(ip.as_bytes()); } - DhcpOption::SubnetMask(mask) => { + DhcpOption::SubnetMask(mask) => { buffer[0] = field::OPT_SUBNET_MASK; buffer[2..6].copy_from_slice(mask.as_bytes()); } @@ -192,7 +195,10 @@ impl<'a> DhcpOption<'a> { buffer[0] = field::OPT_IP_LEASE_TIME; buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]); } - DhcpOption::Other { kind, data: provided } => { + DhcpOption::Other { + kind, + data: provided, + } => { buffer[0] = kind; buffer[2..skip_length].copy_from_slice(provided); } @@ -207,7 +213,7 @@ impl<'a> DhcpOption<'a> { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } pub(crate) mod field { @@ -691,7 +697,7 @@ pub struct Repr<'a> { /// The maximum size dhcp packet the interface can receive pub max_size: Option, /// The DHCP IP lease duration, specified in seconds. - pub lease_duration: Option + pub lease_duration: Option, } impl<'a> Repr<'a> { @@ -700,22 +706,39 @@ impl<'a> Repr<'a> { let mut len = field::OPTIONS.start; // message type and end-of-options options len += 3 + 1; - if self.requested_ip.is_some() { len += 6; } - if self.client_identifier.is_some() { len += 9; } - if self.server_identifier.is_some() { len += 6; } - if self.max_size.is_some() { len += 4; } - if self.router.is_some() { len += 6; } - if self.subnet_mask.is_some() { len += 6; } - if self.lease_duration.is_some() { len += 6; } - if let Some(list) = self.parameter_request_list { len += list.len() + 2; } + if self.requested_ip.is_some() { + len += 6; + } + if self.client_identifier.is_some() { + len += 9; + } + if self.server_identifier.is_some() { + len += 6; + } + if self.max_size.is_some() { + len += 4; + } + if self.router.is_some() { + len += 6; + } + if self.subnet_mask.is_some() { + len += 6; + } + if self.lease_duration.is_some() { + len += 6; + } + if let Some(list) = self.parameter_request_list { + len += list.len() + 2; + } len } /// Parse a DHCP packet and return a high-level representation. pub fn parse(packet: &Packet<&'a T>) -> Result - where T: AsRef<[u8]> + ?Sized { - + where + T: AsRef<[u8]> + ?Sized, + { let transaction_id = packet.transaction_id(); let client_hardware_address = packet.client_hardware_address(); let client_ip = packet.client_ip(); @@ -753,12 +776,12 @@ impl<'a> Repr<'a> { let (next_options, option) = DhcpOption::parse(options)?; match option { DhcpOption::EndOfList => break, - DhcpOption::Pad => {}, + DhcpOption::Pad => {} DhcpOption::MessageType(value) => { if value.opcode() == packet.opcode() { message_type = Ok(value); } - }, + } DhcpOption::RequestedIp(ip) => { requested_ip = Some(ip); } @@ -773,24 +796,30 @@ impl<'a> Repr<'a> { } DhcpOption::SubnetMask(mask) => { subnet_mask = Some(mask); - }, + } DhcpOption::MaximumDhcpMessageSize(size) => { max_size = Some(size); } DhcpOption::IpLeaseTime(duration) => { lease_duration = Some(duration); } - DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => { + DhcpOption::Other { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data, + } => { parameter_request_list = Some(data); } - DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => { + DhcpOption::Other { + kind: field::OPT_DOMAIN_NAME_SERVER, + data, + } => { let mut servers = [None; MAX_DNS_SERVER_COUNT]; for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) { *server = Some(Ipv4Address::from_bytes(chunk)); } dns_servers = Some(servers); } - DhcpOption::Other {..} => {} + DhcpOption::Other { .. } => {} } options = next_options; } @@ -798,9 +827,21 @@ impl<'a> Repr<'a> { let broadcast = packet.broadcast_flag(); Ok(Repr { - transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip, - broadcast, requested_ip, server_identifier, router, - subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size, + transaction_id, + client_hardware_address, + client_ip, + your_ip, + server_ip, + relay_agent_ip, + broadcast, + requested_ip, + server_identifier, + router, + subnet_mask, + client_identifier, + parameter_request_list, + dns_servers, + max_size, lease_duration, message_type: message_type?, }) @@ -809,7 +850,9 @@ impl<'a> Repr<'a> { /// Emit a high-level representation into a Dynamic Host /// Configuration Protocol packet. pub fn emit(&self, packet: &mut Packet<&mut T>) -> Result<()> - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { packet.set_sname_and_boot_file_to_zero(); packet.set_opcode(self.message_type.opcode()); packet.set_hardware_type(Hardware::Ethernet); @@ -850,7 +893,11 @@ impl<'a> Repr<'a> { options = DhcpOption::IpLeaseTime(duration).emit(options); } if let Some(list) = self.parameter_request_list { - options = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }.emit(options); + options = DhcpOption::Other { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: list, + } + .emit(options); } DhcpOption::EndOfList.emit(options); } @@ -861,75 +908,80 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use crate::wire::Ipv4Address; use super::*; + use crate::wire::Ipv4Address; const MAGIC_COOKIE: u32 = 0x63825363; static DISCOVER_BYTES: &[u8] = &[ - 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x82, 0x01, - 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, - 0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, + 0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; static ACK_DNS_SERVER_BYTES: &[u8] = &[ - 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9, - 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, - 0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x2b, - 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, 0x79, 0x73, - 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, 0x03, 0x04, 0x0a, - 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0xa3, 0x01, 0x4a, - 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0xa3, - 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff + 0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, + 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, + 0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, + 0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, + 0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, + 0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, + 0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, + 0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, + 0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, + 0xff, ]; static ACK_LEASE_TIME_BYTES: &[u8] = &[ - 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2, - 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, - 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01, - 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, + 0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, + 0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, + 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]); @@ -971,9 +1023,13 @@ mod test { assert_eq!(options.len(), 6 + 1 + 7); let (options, client_id) = DhcpOption::parse(options).unwrap(); - assert_eq!(client_id, DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42] - }); + assert_eq!( + client_id, + DhcpOption::Other { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: &[1, 3, 6, 42] + } + ); assert_eq!(options.len(), 1 + 7); let (options, client_id) = DhcpOption::parse(options).unwrap(); @@ -1007,7 +1063,8 @@ mod test { options = DhcpOption::RequestedIp(IP_NULL).emit(options); options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options); let option = DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42], + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: &[1, 3, 6, 42], }; options = option.emit(options); DhcpOption::EndOfList.emit(options); @@ -1106,7 +1163,10 @@ mod test { let rest = dhcp_option.emit(&mut bytes); assert_eq!(rest.len(), 0); } - assert_eq!(&bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]); + assert_eq!( + &bytes[0..2], + &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8] + ); assert_eq!(&bytes[2..], DATA); } @@ -1118,10 +1178,14 @@ mod test { // The packet described by ACK_BYTES advertises 4 DNS servers // Here we ensure that we correctly parse the first 3 into our fixed // length-3 array (see issue #305) - assert_eq!(repr.dns_servers, Some([ - Some(Ipv4Address([163, 1, 74, 6])), - Some(Ipv4Address([163, 1, 74, 7])), - Some(Ipv4Address([163, 1, 74, 3]))])); + assert_eq!( + repr.dns_servers, + Some([ + Some(Ipv4Address([163, 1, 74, 6])), + Some(Ipv4Address([163, 1, 74, 7])), + Some(Ipv4Address([163, 1, 74, 3])) + ]) + ); } #[test] diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index f53094258..0e48d21c1 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -1,5 +1,5 @@ -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; use crate::{Error, Result}; @@ -17,8 +17,8 @@ impl fmt::Display for EtherType { match *self { EtherType::Ipv4 => write!(f, "IPv4"), EtherType::Ipv6 => write!(f, "IPv6"), - EtherType::Arp => write!(f, "ARP"), - EtherType::Unknown(id) => write!(f, "0x{:04x}", id) + EtherType::Arp => write!(f, "ARP"), + EtherType::Unknown(id) => write!(f, "0x{:04x}", id), } } } @@ -49,8 +49,7 @@ impl Address { /// Query whether the address is an unicast address. pub fn is_unicast(&self) -> bool { - !(self.is_broadcast() || - self.is_multicast()) + !(self.is_broadcast() || self.is_multicast()) } /// Query whether this address is the broadcast address. @@ -72,8 +71,11 @@ impl Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let bytes = self.0; - write!(f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]) + write!( + f, + "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5] + ) } } @@ -81,16 +83,16 @@ impl fmt::Display for Address { #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Frame> { - buffer: T + buffer: T, } mod field { use crate::wire::field::*; - pub const DESTINATION: Field = 0..6; - pub const SOURCE: Field = 6..12; - pub const ETHERTYPE: Field = 12..14; - pub const PAYLOAD: Rest = 14..; + pub const DESTINATION: Field = 0..6; + pub const SOURCE: Field = 6..12; + pub const ETHERTYPE: Field = 12..14; + pub const PAYLOAD: Rest = 14..; } /// The Ethernet header length @@ -209,19 +211,27 @@ impl> AsRef<[u8]> for Frame { impl> fmt::Display for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "EthernetII src={} dst={} type={}", - self.src_addr(), self.dst_addr(), self.ethertype()) + write!( + f, + "EthernetII src={} dst={} type={}", + self.src_addr(), + self.dst_addr(), + self.ethertype() + ) } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Frame { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { let frame = match Frame::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), - Ok(frame) => frame + Err(err) => return write!(f, "{}({})", indent, err), + Ok(frame) => frame, }; write!(f, "{}{}", indent, frame)?; @@ -241,7 +251,7 @@ impl> PrettyPrint for Frame { indent.increase(f)?; super::Ipv6Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent) } - _ => Ok(()) + _ => Ok(()), } } } @@ -250,9 +260,9 @@ impl> PrettyPrint for Frame { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { - pub src_addr: Address, - pub dst_addr: Address, - pub ethertype: EtherType, + pub src_addr: Address, + pub dst_addr: Address, + pub ethertype: EtherType, } impl Repr { @@ -300,32 +310,32 @@ mod test_ipv4 { // Tests that are valid only with "proto-ipv4" use super::*; - static FRAME_BYTES: [u8; 64] = - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x08, 0x00, - 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff]; - - static PAYLOAD_BYTES: [u8; 50] = - [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff]; + static FRAME_BYTES: [u8; 64] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, 0xaa, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, + ]; + + static PAYLOAD_BYTES: [u8; 50] = [ + 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, + ]; #[test] fn test_deconstruct() { let frame = Frame::new_unchecked(&FRAME_BYTES[..]); - assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])); - assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])); + assert_eq!( + frame.dst_addr(), + Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) + ); + assert_eq!( + frame.src_addr(), + Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]) + ); assert_eq!(frame.ethertype(), EtherType::Ipv4); assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]); } @@ -348,28 +358,30 @@ mod test_ipv6 { // Tests that are valid only with "proto-ipv6" use super::*; - static FRAME_BYTES: [u8; 54] = - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x86, 0xdd, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - - static PAYLOAD_BYTES: [u8; 40] = - [0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; + static FRAME_BYTES: [u8; 54] = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x86, 0xdd, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ]; + + static PAYLOAD_BYTES: [u8; 40] = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + ]; #[test] fn test_deconstruct() { let frame = Frame::new_unchecked(&FRAME_BYTES[..]); - assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])); - assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])); + assert_eq!( + frame.dst_addr(), + Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) + ); + assert_eq!( + frame.src_addr(), + Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]) + ); assert_eq!(frame.ethertype(), EtherType::Ipv6); assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]); } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index ddabb3f77..7964ee339 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -1,10 +1,10 @@ -use core::{cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; +use core::{cmp, fmt}; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::{Ipv4Packet, Ipv4Repr}; +use crate::{Error, Result}; enum_with_unknown! { /// Internet protocol control message type. @@ -35,17 +35,17 @@ enum_with_unknown! { impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Message::EchoReply => write!(f, "echo reply"), + Message::EchoReply => write!(f, "echo reply"), Message::DstUnreachable => write!(f, "destination unreachable"), - Message::Redirect => write!(f, "message redirect"), - Message::EchoRequest => write!(f, "echo request"), - Message::RouterAdvert => write!(f, "router advertisement"), - Message::RouterSolicit => write!(f, "router solicitation"), - Message::TimeExceeded => write!(f, "time exceeded"), - Message::ParamProblem => write!(f, "parameter problem"), - Message::Timestamp => write!(f, "timestamp"), + Message::Redirect => write!(f, "message redirect"), + Message::EchoRequest => write!(f, "echo request"), + Message::RouterAdvert => write!(f, "router advertisement"), + Message::RouterSolicit => write!(f, "router solicitation"), + Message::TimeExceeded => write!(f, "time exceeded"), + Message::ParamProblem => write!(f, "parameter problem"), + Message::Timestamp => write!(f, "timestamp"), Message::TimestampReply => write!(f, "timestamp reply"), - Message::Unknown(id) => write!(f, "{}", id) + Message::Unknown(id) => write!(f, "{}", id), } } } @@ -91,40 +91,25 @@ enum_with_unknown! { impl fmt::Display for DstUnreachable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - DstUnreachable::NetUnreachable => - write!(f, "destination network unreachable"), - DstUnreachable::HostUnreachable => - write!(f, "destination host unreachable"), - DstUnreachable::ProtoUnreachable => - write!(f, "destination protocol unreachable"), - DstUnreachable::PortUnreachable => - write!(f, "destination port unreachable"), - DstUnreachable::FragRequired => - write!(f, "fragmentation required, and DF flag set"), - DstUnreachable::SrcRouteFailed => - write!(f, "source route failed"), - DstUnreachable::DstNetUnknown => - write!(f, "destination network unknown"), - DstUnreachable::DstHostUnknown => - write!(f, "destination host unknown"), - DstUnreachable::SrcHostIsolated => - write!(f, "source host isolated"), - DstUnreachable::NetProhibited => - write!(f, "network administratively prohibited"), - DstUnreachable::HostProhibited => - write!(f, "host administratively prohibited"), - DstUnreachable::NetUnreachToS => - write!(f, "network unreachable for ToS"), - DstUnreachable::HostUnreachToS => - write!(f, "host unreachable for ToS"), - DstUnreachable::CommProhibited => - write!(f, "communication administratively prohibited"), - DstUnreachable::HostPrecedViol => - write!(f, "host precedence violation"), - DstUnreachable::PrecedCutoff => - write!(f, "precedence cutoff in effect"), - DstUnreachable::Unknown(id) => - write!(f, "{}", id) + DstUnreachable::NetUnreachable => write!(f, "destination network unreachable"), + DstUnreachable::HostUnreachable => write!(f, "destination host unreachable"), + DstUnreachable::ProtoUnreachable => write!(f, "destination protocol unreachable"), + DstUnreachable::PortUnreachable => write!(f, "destination port unreachable"), + DstUnreachable::FragRequired => write!(f, "fragmentation required, and DF flag set"), + DstUnreachable::SrcRouteFailed => write!(f, "source route failed"), + DstUnreachable::DstNetUnknown => write!(f, "destination network unknown"), + DstUnreachable::DstHostUnknown => write!(f, "destination host unknown"), + DstUnreachable::SrcHostIsolated => write!(f, "source host isolated"), + DstUnreachable::NetProhibited => write!(f, "network administratively prohibited"), + DstUnreachable::HostProhibited => write!(f, "host administratively prohibited"), + DstUnreachable::NetUnreachToS => write!(f, "network unreachable for ToS"), + DstUnreachable::HostUnreachToS => write!(f, "host unreachable for ToS"), + DstUnreachable::CommProhibited => { + write!(f, "communication administratively prohibited") + } + DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"), + DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"), + DstUnreachable::Unknown(id) => write!(f, "{}", id), } } } @@ -169,17 +154,17 @@ enum_with_unknown! { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } mod field { use crate::wire::field::*; - pub const TYPE: usize = 0; - pub const CODE: usize = 1; - pub const CHECKSUM: Field = 2..4; + pub const TYPE: usize = 0; + pub const CODE: usize = 1; + pub const CHECKSUM: Field = 2..4; - pub const UNUSED: Field = 4..8; + pub const UNUSED: Field = 4..8; pub const ECHO_IDENT: Field = 4..6; pub const ECHO_SEQNO: Field = 6..8; @@ -268,10 +253,10 @@ impl> Packet { /// The result depends on the value of the message type field. pub fn header_len(&self) -> usize { match self.msg_type() { - Message::EchoRequest => field::ECHO_SEQNO.end, - Message::EchoReply => field::ECHO_SEQNO.end, + Message::EchoRequest => field::ECHO_SEQNO.end, + Message::EchoReply => field::ECHO_SEQNO.end, Message::DstUnreachable => field::UNUSED.end, - _ => field::UNUSED.end // make a conservative assumption + _ => field::UNUSED.end, // make a conservative assumption } } @@ -280,7 +265,9 @@ impl> Packet { /// # Fuzzing /// This function always returns `true` when fuzzing. pub fn verify_checksum(&self) -> bool { - if cfg!(fuzzing) { return true } + if cfg!(fuzzing) { + return true; + } let data = self.buffer.as_ref(); checksum::data(data) == !0 @@ -371,47 +358,49 @@ impl> AsRef<[u8]> for Packet { #[non_exhaustive] pub enum Repr<'a> { EchoRequest { - ident: u16, + ident: u16, seq_no: u16, - data: &'a [u8] + data: &'a [u8], }, EchoReply { - ident: u16, + ident: u16, seq_no: u16, - data: &'a [u8] + data: &'a [u8], }, DstUnreachable { reason: DstUnreachable, header: Ipv4Repr, - data: &'a [u8] + data: &'a [u8], }, } impl<'a> Repr<'a> { /// Parse an Internet Control Message Protocol version 4 packet and return /// a high-level representation. - pub fn parse(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities) - -> Result> - where T: AsRef<[u8]> + ?Sized { + pub fn parse( + packet: &Packet<&'a T>, + checksum_caps: &ChecksumCapabilities, + ) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { // Valid checksum is expected. - if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) } + if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { + return Err(Error::Checksum); + } match (packet.msg_type(), packet.msg_code()) { - (Message::EchoRequest, 0) => { - Ok(Repr::EchoRequest { - ident: packet.echo_ident(), - seq_no: packet.echo_seq_no(), - data: packet.data() - }) - }, - - (Message::EchoReply, 0) => { - Ok(Repr::EchoReply { - ident: packet.echo_ident(), - seq_no: packet.echo_seq_no(), - data: packet.data() - }) - }, + (Message::EchoRequest, 0) => Ok(Repr::EchoRequest { + ident: packet.echo_ident(), + seq_no: packet.echo_seq_no(), + data: packet.data(), + }), + + (Message::EchoReply, 0) => Ok(Repr::EchoReply { + ident: packet.echo_ident(), + seq_no: packet.echo_seq_no(), + data: packet.data(), + }), (Message::DstUnreachable, code) => { let ip_packet = Ipv4Packet::new_checked(packet.data())?; @@ -419,7 +408,9 @@ impl<'a> Repr<'a> { let payload = &packet.data()[ip_packet.header_len() as usize..]; // RFC 792 requires exactly eight bytes to be returned. // We allow more, since there isn't a reason not to, but require at least eight. - if payload.len() < 8 { return Err(Error::Truncated) } + if payload.len() < 8 { + return Err(Error::Truncated); + } Ok(Repr::DstUnreachable { reason: DstUnreachable::from(code), @@ -428,22 +419,21 @@ impl<'a> Repr<'a> { dst_addr: ip_packet.dst_addr(), protocol: ip_packet.protocol(), payload_len: payload.len(), - hop_limit: ip_packet.hop_limit() + hop_limit: ip_packet.hop_limit(), }, - data: payload + data: payload, }) } - _ => Err(Error::Unrecognized) + _ => Err(Error::Unrecognized), } } /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { match self { - &Repr::EchoRequest { data, .. } | - &Repr::EchoReply { data, .. } => { + &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() - }, + } &Repr::DstUnreachable { header, data, .. } => { field::UNUSED.end + header.buffer_len() + data.len() } @@ -453,28 +443,42 @@ impl<'a> Repr<'a> { /// Emit a high-level representation into an Internet Control Message Protocol version 4 /// packet. pub fn emit(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { packet.set_msg_code(0); match *self { - Repr::EchoRequest { ident, seq_no, data } => { + Repr::EchoRequest { + ident, + seq_no, + data, + } => { packet.set_msg_type(Message::EchoRequest); packet.set_msg_code(0); packet.set_echo_ident(ident); packet.set_echo_seq_no(seq_no); let data_len = cmp::min(packet.data_mut().len(), data.len()); packet.data_mut()[..data_len].copy_from_slice(&data[..data_len]) - }, + } - Repr::EchoReply { ident, seq_no, data } => { + Repr::EchoReply { + ident, + seq_no, + data, + } => { packet.set_msg_type(Message::EchoReply); packet.set_msg_code(0); packet.set_echo_ident(ident); packet.set_echo_seq_no(seq_no); let data_len = cmp::min(packet.data_mut().len(), data.len()); packet.data_mut()[..data_len].copy_from_slice(&data[..data_len]) - }, + } - Repr::DstUnreachable { reason, header, data } => { + Repr::DstUnreachable { + reason, + header, + data, + } => { packet.set_msg_type(Message::DstUnreachable); packet.set_msg_code(reason.into()); @@ -503,9 +507,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { write!(f, "ICMPv4 ({})", err)?; write!(f, " type={:?}", self.msg_type())?; match self.msg_type() { - Message::DstUnreachable => - write!(f, " code={:?}", DstUnreachable::from(self.msg_code())), - _ => write!(f, " code={}", self.msg_code()) + Message::DstUnreachable => { + write!(f, " code={:?}", DstUnreachable::from(self.msg_code())) + } + _ => write!(f, " code={}", self.msg_code()), } } } @@ -515,27 +520,46 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Repr::EchoRequest { ident, seq_no, data } => - write!(f, "ICMPv4 echo request id={} seq={} len={}", - ident, seq_no, data.len()), - Repr::EchoReply { ident, seq_no, data } => - write!(f, "ICMPv4 echo reply id={} seq={} len={}", - ident, seq_no, data.len()), - Repr::DstUnreachable { reason, .. } => - write!(f, "ICMPv4 destination unreachable ({})", - reason), + Repr::EchoRequest { + ident, + seq_no, + data, + } => write!( + f, + "ICMPv4 echo request id={} seq={} len={}", + ident, + seq_no, + data.len() + ), + Repr::EchoReply { + ident, + seq_no, + data, + } => write!( + f, + "ICMPv4 echo reply id={} seq={} len={}", + ident, + seq_no, + data.len() + ), + Repr::DstUnreachable { reason, .. } => { + write!(f, "ICMPv4 destination unreachable ({})", reason) + } } } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { let packet = match Packet::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), - Ok(packet) => packet + Err(err) => return write!(f, "{}({})", indent, err), + Ok(packet) => packet, }; write!(f, "{}{}", indent, packet)?; @@ -544,7 +568,7 @@ impl> PrettyPrint for Packet { indent.increase(f)?; super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent) } - _ => Ok(()) + _ => Ok(()), } } } @@ -553,13 +577,11 @@ impl> PrettyPrint for Packet { mod test { use super::*; - static ECHO_PACKET_BYTES: [u8; 12] = - [0x08, 0x00, 0x8e, 0xfe, - 0x12, 0x34, 0xab, 0xcd, - 0xaa, 0x00, 0x00, 0xff]; + static ECHO_PACKET_BYTES: [u8; 12] = [ + 0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff, + ]; - static ECHO_DATA_BYTES: [u8; 4] = - [0xaa, 0x00, 0x00, 0xff]; + static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; #[test] fn test_echo_deconstruct() { @@ -590,7 +612,7 @@ mod test { Repr::EchoRequest { ident: 0x1234, seq_no: 0xabcd, - data: &ECHO_DATA_BYTES + data: &ECHO_DATA_BYTES, } } @@ -612,8 +634,7 @@ mod test { #[test] fn test_check_len() { - let bytes = [0x0b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00]; + let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated)); assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated)); assert!(Packet::new_checked(&bytes[..]).is_ok()); diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 5ab01db86..395509b80 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -1,13 +1,13 @@ -use core::{cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; +use core::{cmp, fmt}; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; -use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; use crate::wire::MldRepr; #[cfg(feature = "medium-ethernet")] use crate::wire::NdiscRepr; +use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; +use crate::{Error, Result}; enum_with_unknown! { /// Internet protocol control message type. @@ -57,8 +57,11 @@ impl Message { /// [NDISC]: https://tools.ietf.org/html/rfc4861 pub fn is_ndisc(&self) -> bool { match *self { - Message::RouterSolicit | Message::RouterAdvert | Message::NeighborSolicit | - Message::NeighborAdvert | Message::Redirect => true, + Message::RouterSolicit + | Message::RouterAdvert + | Message::NeighborSolicit + | Message::NeighborAdvert + | Message::Redirect => true, _ => false, } } @@ -78,20 +81,20 @@ impl Message { impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Message::DstUnreachable => write!(f, "destination unreachable"), - Message::PktTooBig => write!(f, "packet too big"), - Message::TimeExceeded => write!(f, "time exceeded"), - Message::ParamProblem => write!(f, "parameter problem"), - Message::EchoReply => write!(f, "echo reply"), - Message::EchoRequest => write!(f, "echo request"), - Message::RouterSolicit => write!(f, "router solicitation"), - Message::RouterAdvert => write!(f, "router advertisement"), + Message::DstUnreachable => write!(f, "destination unreachable"), + Message::PktTooBig => write!(f, "packet too big"), + Message::TimeExceeded => write!(f, "time exceeded"), + Message::ParamProblem => write!(f, "parameter problem"), + Message::EchoReply => write!(f, "echo reply"), + Message::EchoRequest => write!(f, "echo request"), + Message::RouterSolicit => write!(f, "router solicitation"), + Message::RouterAdvert => write!(f, "router advertisement"), Message::NeighborSolicit => write!(f, "neighbor solicitation"), - Message::NeighborAdvert => write!(f, "neighbor advert"), - Message::Redirect => write!(f, "redirect"), - Message::MldQuery => write!(f, "multicast listener query"), - Message::MldReport => write!(f, "multicast listener report"), - Message::Unknown(id) => write!(f, "{}", id) + Message::NeighborAdvert => write!(f, "neighbor advert"), + Message::Redirect => write!(f, "redirect"), + Message::MldQuery => write!(f, "multicast listener query"), + Message::MldReport => write!(f, "multicast listener report"), + Message::Unknown(id) => write!(f, "{}", id), } } } @@ -119,22 +122,19 @@ enum_with_unknown! { impl fmt::Display for DstUnreachable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - DstUnreachable::NoRoute => - write!(f, "no route to destination"), - DstUnreachable::AdminProhibit => - write!(f, "communication with destination administratively prohibited"), - DstUnreachable::BeyondScope => - write!(f, "beyond scope of source address"), - DstUnreachable::AddrUnreachable => - write!(f, "address unreachable"), - DstUnreachable::PortUnreachable => - write!(f, "port unreachable"), - DstUnreachable::FailedPolicy => - write!(f, "source address failed ingress/egress policy"), - DstUnreachable::RejectRoute => - write!(f, "reject route to destination"), - DstUnreachable::Unknown(id) => - write!(f, "{}", id) + DstUnreachable::NoRoute => write!(f, "no route to destination"), + DstUnreachable::AdminProhibit => write!( + f, + "communication with destination administratively prohibited" + ), + DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"), + DstUnreachable::AddrUnreachable => write!(f, "address unreachable"), + DstUnreachable::PortUnreachable => write!(f, "port unreachable"), + DstUnreachable::FailedPolicy => { + write!(f, "source address failed ingress/egress policy") + } + DstUnreachable::RejectRoute => write!(f, "reject route to destination"), + DstUnreachable::Unknown(id) => write!(f, "{}", id), } } } @@ -154,14 +154,10 @@ enum_with_unknown! { impl fmt::Display for ParamProblem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ParamProblem::ErroneousHdrField => - write!(f, "erroneous header field."), - ParamProblem::UnrecognizedNxtHdr => - write!(f, "unrecognized next header type."), - ParamProblem::UnrecognizedOption => - write!(f, "unrecognized IPv6 option."), - ParamProblem::Unknown(id) => - write!(f, "{}", id) + ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."), + ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."), + ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."), + ParamProblem::Unknown(id) => write!(f, "{}", id), } } } @@ -179,12 +175,9 @@ enum_with_unknown! { impl fmt::Display for TimeExceeded { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - TimeExceeded::HopLimitExceeded => - write!(f, "hop limit exceeded in transit"), - TimeExceeded::FragReassemExceeded => - write!(f, "fragment reassembly time exceeded"), - TimeExceeded::Unknown(id) => - write!(f, "{}", id) + TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"), + TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"), + TimeExceeded::Unknown(id) => write!(f, "{}", id), } } } @@ -193,7 +186,7 @@ impl fmt::Display for TimeExceeded { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - pub(super) buffer: T + pub(super) buffer: T, } // Ranges and constants describing key boundaries in the ICMPv6 header. @@ -201,54 +194,54 @@ pub(super) mod field { use crate::wire::field::*; // ICMPv6: See https://tools.ietf.org/html/rfc4443 - pub const TYPE: usize = 0; - pub const CODE: usize = 1; - pub const CHECKSUM: Field = 2..4; + pub const TYPE: usize = 0; + pub const CODE: usize = 1; + pub const CHECKSUM: Field = 2..4; - pub const UNUSED: Field = 4..8; - pub const MTU: Field = 4..8; - pub const POINTER: Field = 4..8; - pub const ECHO_IDENT: Field = 4..6; - pub const ECHO_SEQNO: Field = 6..8; + pub const UNUSED: Field = 4..8; + pub const MTU: Field = 4..8; + pub const POINTER: Field = 4..8; + pub const ECHO_IDENT: Field = 4..6; + pub const ECHO_SEQNO: Field = 6..8; - pub const HEADER_END: usize = 8; + pub const HEADER_END: usize = 8; // NDISC: See https://tools.ietf.org/html/rfc4861 // Router Advertisement message offsets - pub const CUR_HOP_LIMIT: usize = 4; - pub const ROUTER_FLAGS: usize = 5; - pub const ROUTER_LT: Field = 6..8; - pub const REACHABLE_TM: Field = 8..12; - pub const RETRANS_TM: Field = 12..16; + pub const CUR_HOP_LIMIT: usize = 4; + pub const ROUTER_FLAGS: usize = 5; + pub const ROUTER_LT: Field = 6..8; + pub const REACHABLE_TM: Field = 8..12; + pub const RETRANS_TM: Field = 12..16; // Neighbor Solicitation message offsets - pub const TARGET_ADDR: Field = 8..24; + pub const TARGET_ADDR: Field = 8..24; // Neighbor Advertisement message offsets - pub const NEIGH_FLAGS: usize = 4; + pub const NEIGH_FLAGS: usize = 4; // Redirected Header message offsets - pub const DEST_ADDR: Field = 24..40; + pub const DEST_ADDR: Field = 24..40; // MLD: // - https://tools.ietf.org/html/rfc3810 // - https://tools.ietf.org/html/rfc3810 // Multicast Listener Query message - pub const MAX_RESP_CODE: Field = 4..6; - pub const QUERY_RESV: Field = 6..8; - pub const QUERY_MCAST_ADDR: Field = 8..24; - pub const SQRV: usize = 24; - pub const QQIC: usize = 25; - pub const QUERY_NUM_SRCS: Field = 26..28; + pub const MAX_RESP_CODE: Field = 4..6; + pub const QUERY_RESV: Field = 6..8; + pub const QUERY_MCAST_ADDR: Field = 8..24; + pub const SQRV: usize = 24; + pub const QQIC: usize = 25; + pub const QUERY_NUM_SRCS: Field = 26..28; // Multicast Listener Report Message - pub const RECORD_RESV: Field = 4..6; - pub const NR_MCAST_RCRDS: Field = 6..8; + pub const RECORD_RESV: Field = 4..6; + pub const NR_MCAST_RCRDS: Field = 6..8; // Multicast Address Record Offsets - pub const RECORD_TYPE: usize = 0; - pub const AUX_DATA_LEN: usize = 1; - pub const RECORD_NUM_SRCS: Field = 2..4; + pub const RECORD_TYPE: usize = 0; + pub const AUX_DATA_LEN: usize = 1; + pub const RECORD_NUM_SRCS: Field = 2..4; pub const RECORD_MCAST_ADDR: Field = 4..20; } @@ -333,29 +326,28 @@ impl> Packet { NetworkEndian::read_u32(&data[field::POINTER]) } - /// Return the header length. The result depends on the value of /// the message type field. pub fn header_len(&self) -> usize { match self.msg_type() { - Message::DstUnreachable => field::UNUSED.end, - Message::PktTooBig => field::MTU.end, - Message::TimeExceeded => field::UNUSED.end, - Message::ParamProblem => field::POINTER.end, - Message::EchoRequest => field::ECHO_SEQNO.end, - Message::EchoReply => field::ECHO_SEQNO.end, - Message::RouterSolicit => field::UNUSED.end, - Message::RouterAdvert => field::RETRANS_TM.end, + Message::DstUnreachable => field::UNUSED.end, + Message::PktTooBig => field::MTU.end, + Message::TimeExceeded => field::UNUSED.end, + Message::ParamProblem => field::POINTER.end, + Message::EchoRequest => field::ECHO_SEQNO.end, + Message::EchoReply => field::ECHO_SEQNO.end, + Message::RouterSolicit => field::UNUSED.end, + Message::RouterAdvert => field::RETRANS_TM.end, Message::NeighborSolicit => field::TARGET_ADDR.end, - Message::NeighborAdvert => field::TARGET_ADDR.end, - Message::Redirect => field::DEST_ADDR.end, - Message::MldQuery => field::QUERY_NUM_SRCS.end, - Message::MldReport => field::NR_MCAST_RCRDS.end, + Message::NeighborAdvert => field::TARGET_ADDR.end, + Message::Redirect => field::DEST_ADDR.end, + Message::MldQuery => field::QUERY_NUM_SRCS.end, + Message::MldReport => field::NR_MCAST_RCRDS.end, // For packets that are not included in RFC 4443, do not // include the last 32 bits of the ICMPv6 header in // `header_bytes`. This must be done so that these bytes // can be accessed in the `payload`. - _ => field::CHECKSUM.end + _ => field::CHECKSUM.end, } } @@ -364,13 +356,14 @@ impl> Packet { /// # Fuzzing /// This function always returns `true` when fuzzing. pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { - if cfg!(fuzzing) { return true } + if cfg!(fuzzing) { + return true; + } let data = self.buffer.as_ref(); checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, - data.len() as u32), - checksum::data(data) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32), + checksum::data(data), ]) == !0 } } @@ -409,16 +402,18 @@ impl + AsMut<[u8]>> Packet { #[inline] pub fn clear_reserved(&mut self) { match self.msg_type() { - Message::RouterSolicit | Message::NeighborSolicit | - Message::NeighborAdvert | Message::Redirect => { + Message::RouterSolicit + | Message::NeighborSolicit + | Message::NeighborAdvert + | Message::Redirect => { let data = self.buffer.as_mut(); NetworkEndian::write_u32(&mut data[field::UNUSED], 0); - }, + } Message::MldQuery => { let data = self.buffer.as_mut(); NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0); data[field::SQRV] &= 0xf; - }, + } Message::MldReport => { let data = self.buffer.as_mut(); NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0); @@ -479,9 +474,8 @@ impl + AsMut<[u8]>> Packet { let checksum = { let data = self.buffer.as_ref(); !checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, - data.len() as u32), - checksum::data(data) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32), + checksum::data(data), ]) }; self.set_checksum(checksum) @@ -510,33 +504,33 @@ pub enum Repr<'a> { DstUnreachable { reason: DstUnreachable, header: Ipv6Repr, - data: &'a [u8] + data: &'a [u8], }, PktTooBig { mtu: u32, header: Ipv6Repr, - data: &'a [u8] + data: &'a [u8], }, TimeExceeded { reason: TimeExceeded, header: Ipv6Repr, - data: &'a [u8] + data: &'a [u8], }, ParamProblem { - reason: ParamProblem, + reason: ParamProblem, pointer: u32, - header: Ipv6Repr, - data: &'a [u8] + header: Ipv6Repr, + data: &'a [u8], }, EchoRequest { - ident: u16, + ident: u16, seq_no: u16, - data: &'a [u8] + data: &'a [u8], }, EchoReply { - ident: u16, + ident: u16, seq_no: u16, - data: &'a [u8] + data: &'a [u8], }, #[cfg(feature = "medium-ethernet")] Ndisc(NdiscRepr<'a>), @@ -546,29 +540,37 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an Internet Control Message Protocol version 6 packet and return /// a high-level representation. - pub fn parse(src_addr: &IpAddress, dst_addr: &IpAddress, - packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities) - -> Result> - where T: AsRef<[u8]> + ?Sized { - fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) - -> Result<(&'a [u8], Ipv6Repr)> - where T: AsRef<[u8]> + ?Sized { + pub fn parse( + src_addr: &IpAddress, + dst_addr: &IpAddress, + packet: &Packet<&'a T>, + checksum_caps: &ChecksumCapabilities, + ) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { + fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)> + where + T: AsRef<[u8]> + ?Sized, + { let ip_packet = Ipv6Packet::new_checked(packet.payload())?; let payload = &packet.payload()[ip_packet.header_len() as usize..]; - if payload.len() < 8 { return Err(Error::Truncated) } + if payload.len() < 8 { + return Err(Error::Truncated); + } let repr = Ipv6Repr { src_addr: ip_packet.src_addr(), dst_addr: ip_packet.dst_addr(), next_header: ip_packet.next_header(), payload_len: payload.len(), - hop_limit: ip_packet.hop_limit() + hop_limit: ip_packet.hop_limit(), }; Ok((payload, repr)) } // Valid checksum is expected. if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) { - return Err(Error::Checksum) + return Err(Error::Checksum); } match (packet.msg_type(), packet.msg_code()) { @@ -577,85 +579,80 @@ impl<'a> Repr<'a> { Ok(Repr::DstUnreachable { reason: DstUnreachable::from(code), header: repr, - data: payload + data: payload, }) - }, + } (Message::PktTooBig, 0) => { let (payload, repr) = create_packet_from_payload(packet)?; Ok(Repr::PktTooBig { mtu: packet.pkt_too_big_mtu(), header: repr, - data: payload + data: payload, }) - }, + } (Message::TimeExceeded, code) => { let (payload, repr) = create_packet_from_payload(packet)?; Ok(Repr::TimeExceeded { reason: TimeExceeded::from(code), header: repr, - data: payload + data: payload, }) - }, + } (Message::ParamProblem, code) => { let (payload, repr) = create_packet_from_payload(packet)?; Ok(Repr::ParamProblem { reason: ParamProblem::from(code), pointer: packet.param_problem_ptr(), header: repr, - data: payload + data: payload, }) - }, - (Message::EchoRequest, 0) => { - Ok(Repr::EchoRequest { - ident: packet.echo_ident(), - seq_no: packet.echo_seq_no(), - data: packet.payload() - }) - }, - (Message::EchoReply, 0) => { - Ok(Repr::EchoReply { - ident: packet.echo_ident(), - seq_no: packet.echo_seq_no(), - data: packet.payload() - }) - }, + } + (Message::EchoRequest, 0) => Ok(Repr::EchoRequest { + ident: packet.echo_ident(), + seq_no: packet.echo_seq_no(), + data: packet.payload(), + }), + (Message::EchoReply, 0) => Ok(Repr::EchoReply { + ident: packet.echo_ident(), + seq_no: packet.echo_seq_no(), + data: packet.payload(), + }), #[cfg(feature = "medium-ethernet")] - (msg_type, 0) if msg_type.is_ndisc() => { - NdiscRepr::parse(packet).map(Repr::Ndisc) - }, - (msg_type, 0) if msg_type.is_mld() => { - MldRepr::parse(packet).map(Repr::Mld) - }, - _ => Err(Error::Unrecognized) + (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), + (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), + _ => Err(Error::Unrecognized), } } /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { match self { - &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } | - &Repr::TimeExceeded { header, data, .. } | &Repr::ParamProblem { header, data, .. } => { + &Repr::DstUnreachable { header, data, .. } + | &Repr::PktTooBig { header, data, .. } + | &Repr::TimeExceeded { header, data, .. } + | &Repr::ParamProblem { header, data, .. } => { field::UNUSED.end + header.buffer_len() + data.len() } - &Repr::EchoRequest { data, .. } | - &Repr::EchoReply { data, .. } => { + &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() - }, + } #[cfg(feature = "medium-ethernet")] - &Repr::Ndisc(ndisc) => { - ndisc.buffer_len() - }, - &Repr::Mld(mld) => { - mld.buffer_len() - }, + &Repr::Ndisc(ndisc) => ndisc.buffer_len(), + &Repr::Mld(mld) => mld.buffer_len(), } } /// Emit a high-level representation into an Internet Control Message Protocol version 6 /// packet. - pub fn emit(&self, src_addr: &IpAddress, dst_addr: &IpAddress, - packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + pub fn emit( + &self, + src_addr: &IpAddress, + dst_addr: &IpAddress, + packet: &mut Packet<&mut T>, + checksum_caps: &ChecksumCapabilities, + ) where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) { let mut ip_packet = Ipv6Packet::new_unchecked(buffer); header.emit(&mut ip_packet); @@ -664,12 +661,16 @@ impl<'a> Repr<'a> { } match *self { - Repr::DstUnreachable { reason, header, data } => { + Repr::DstUnreachable { + reason, + header, + data, + } => { packet.set_msg_type(Message::DstUnreachable); packet.set_msg_code(reason.into()); emit_contained_packet(packet.payload_mut(), header, &data); - }, + } Repr::PktTooBig { mtu, header, data } => { packet.set_msg_type(Message::PktTooBig); @@ -677,49 +678,62 @@ impl<'a> Repr<'a> { packet.set_pkt_too_big_mtu(mtu); emit_contained_packet(packet.payload_mut(), header, &data); - }, + } - Repr::TimeExceeded { reason, header, data } => { + Repr::TimeExceeded { + reason, + header, + data, + } => { packet.set_msg_type(Message::TimeExceeded); packet.set_msg_code(reason.into()); emit_contained_packet(packet.payload_mut(), header, &data); - }, + } - Repr::ParamProblem { reason, pointer, header, data } => { + Repr::ParamProblem { + reason, + pointer, + header, + data, + } => { packet.set_msg_type(Message::ParamProblem); packet.set_msg_code(reason.into()); packet.set_param_problem_ptr(pointer); emit_contained_packet(packet.payload_mut(), header, &data); - }, + } - Repr::EchoRequest { ident, seq_no, data } => { + Repr::EchoRequest { + ident, + seq_no, + data, + } => { packet.set_msg_type(Message::EchoRequest); packet.set_msg_code(0); packet.set_echo_ident(ident); packet.set_echo_seq_no(seq_no); let data_len = cmp::min(packet.payload_mut().len(), data.len()); packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) - }, + } - Repr::EchoReply { ident, seq_no, data } => { + Repr::EchoReply { + ident, + seq_no, + data, + } => { packet.set_msg_type(Message::EchoReply); packet.set_msg_code(0); packet.set_echo_ident(ident); packet.set_echo_seq_no(seq_no); let data_len = cmp::min(packet.payload_mut().len(), data.len()); packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) - }, + } #[cfg(feature = "medium-ethernet")] - Repr::Ndisc(ndisc) => { - ndisc.emit(packet) - }, + Repr::Ndisc(ndisc) => ndisc.emit(packet), - Repr::Mld(mld) => { - mld.emit(packet) - }, + Repr::Mld(mld) => mld.emit(packet), } if checksum_caps.icmpv6.tx() { @@ -733,60 +747,39 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { - use crate::wire::{Ipv6Address, Ipv6Repr, IpProtocol}; - use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; use super::*; + use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; + use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr}; + + static ECHO_PACKET_BYTES: [u8; 12] = [ + 0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff, + ]; + + static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; + + static PKT_TOO_BIG_BYTES: [u8; 60] = [ + 0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, + 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, + ]; + + static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, + 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, + ]; - static ECHO_PACKET_BYTES: [u8; 12] = - [0x80, 0x00, 0x19, 0xb3, - 0x12, 0x34, 0xab, 0xcd, - 0xaa, 0x00, 0x00, 0xff]; - - static ECHO_PACKET_PAYLOAD: [u8; 4] = - [0xaa, 0x00, 0x00, 0xff]; - - static PKT_TOO_BIG_BYTES: [u8; 60] = - [0x02, 0x00, 0x0f, 0xc9, - 0x00, 0x00, 0x05, 0xdc, - 0x60, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x11, 0x40, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0xbf, 0x00, 0x00, 0x35, - 0x00, 0x0c, 0x12, 0x4d, - 0xaa, 0x00, 0x00, 0xff]; - - static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = - [0x60, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x11, 0x40, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - 0xbf, 0x00, 0x00, 0x35, - 0x00, 0x0c, 0x12, 0x4d, - 0xaa, 0x00, 0x00, 0xff]; - - static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = - [0xbf, 0x00, 0x00, 0x35, - 0x00, 0x0c, 0x12, 0x4d, - 0xaa, 0x00, 0x00, 0xff]; + static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [ + 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, + ]; fn echo_packet_repr() -> Repr<'static> { Repr::EchoRequest { ident: 0x1234, seq_no: 0xabcd, - data: &ECHO_PACKET_PAYLOAD + data: &ECHO_PACKET_PAYLOAD, } } @@ -794,17 +787,17 @@ mod test { Repr::PktTooBig { mtu: 1500, header: Ipv6Repr { - src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01]), - dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02]), + src_addr: Ipv6Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, + ]), + dst_addr: Ipv6Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, + ]), next_header: IpProtocol::Udp, payload_len: 12, - hop_limit: 0x40 + hop_limit: 0x40, }, data: &PKT_TOO_BIG_UDP_PAYLOAD, } @@ -819,7 +812,10 @@ mod test { assert_eq!(packet.echo_ident(), 0x1234); assert_eq!(packet.echo_seq_no(), 0xabcd); assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]); - assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true); + assert_eq!( + packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), + true + ); assert!(!packet.msg_type().is_error()); } @@ -831,7 +827,9 @@ mod test { packet.set_msg_code(0); packet.set_echo_ident(0x1234); packet.set_echo_seq_no(0xabcd); - packet.payload_mut().copy_from_slice(&ECHO_PACKET_PAYLOAD[..]); + packet + .payload_mut() + .copy_from_slice(&ECHO_PACKET_PAYLOAD[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } @@ -839,8 +837,13 @@ mod test { #[test] fn test_echo_repr_parse() { let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]); - let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &packet, &ChecksumCapabilities::default()).unwrap(); + let repr = Repr::parse( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &packet, + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(repr, echo_packet_repr()); } @@ -849,8 +852,12 @@ mod test { let repr = echo_packet_repr(); let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); - repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &mut packet, &ChecksumCapabilities::default()); + repr.emit( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &mut packet, + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } @@ -862,7 +869,10 @@ mod test { assert_eq!(packet.checksum(), 0x0fc9); assert_eq!(packet.pkt_too_big_mtu(), 1500); assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]); - assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true); + assert_eq!( + packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), + true + ); assert!(packet.msg_type().is_error()); } @@ -873,7 +883,9 @@ mod test { packet.set_msg_type(Message::PktTooBig); packet.set_msg_code(0); packet.set_pkt_too_big_mtu(1500); - packet.payload_mut().copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]); + packet + .payload_mut() + .copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); } @@ -881,8 +893,13 @@ mod test { #[test] fn test_too_big_repr_parse() { let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]); - let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &packet, &ChecksumCapabilities::default()).unwrap(); + let repr = Repr::parse( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &packet, + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(repr, too_big_packet_repr()); } @@ -891,8 +908,12 @@ mod test { let repr = too_big_packet_repr(); let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); - repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &mut packet, &ChecksumCapabilities::default()); + repr.emit( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &mut packet, + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); } } diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 3c6da88a9..2a11772f4 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -1,9 +1,9 @@ -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; -use crate::{Error, Result}; -use crate::wire::ip::checksum; use crate::time::Duration; +use crate::wire::ip::checksum; +use crate::{Error, Result}; use crate::wire::Ipv4Address; @@ -202,7 +202,8 @@ impl Repr { /// Parse an Internet Group Management Protocol v1/v2 packet and return /// a high-level representation. pub fn parse(packet: &Packet<&T>) -> Result - where T: AsRef<[u8]> + ?Sized + where + T: AsRef<[u8]> + ?Sized, { // Check if the address is 0.0.0.0 or multicast let addr = packet.group_addr(); @@ -226,13 +227,13 @@ impl Repr { version, }) } - Message::MembershipReportV2 => { - Ok(Repr::MembershipReport { - group_addr: packet.group_addr(), - version: IgmpVersion::Version2, - }) - } - Message::LeaveGroup => Ok(Repr::LeaveGroup { group_addr: packet.group_addr() }), + Message::MembershipReportV2 => Ok(Repr::MembershipReport { + group_addr: packet.group_addr(), + version: IgmpVersion::Version2, + }), + Message::LeaveGroup => Ok(Repr::LeaveGroup { + group_addr: packet.group_addr(), + }), Message::MembershipReportV1 => { // for backwards compatibility with IGMPv1 Ok(Repr::MembershipReport { @@ -252,20 +253,21 @@ impl Repr { /// Emit a high-level representation into an Internet Group Management Protocol v2 packet. pub fn emit(&self, packet: &mut Packet<&mut T>) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { match *self { Repr::MembershipQuery { max_resp_time, group_addr, - version + version, } => { packet.set_msg_type(Message::MembershipQuery); match version { - IgmpVersion::Version1 => - packet.set_max_resp_code(0), - IgmpVersion::Version2 => - packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)), + IgmpVersion::Version1 => packet.set_max_resp_code(0), + IgmpVersion::Version2 => { + packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)) + } } packet.set_group_address(group_addr); } @@ -336,20 +338,21 @@ impl<'a> fmt::Display for Repr { group_addr, version, } => { - write!(f, - "IGMP membership query max_resp_time={} group_addr={} version={:?}", - max_resp_time, - group_addr, - version) + write!( + f, + "IGMP membership query max_resp_time={} group_addr={} version={:?}", + max_resp_time, group_addr, version + ) } Repr::MembershipReport { group_addr, version, } => { - write!(f, - "IGMP membership report group_addr={} version={:?}", - group_addr, - version) + write!( + f, + "IGMP membership report group_addr={} version={:?}", + group_addr, version + ) } Repr::LeaveGroup { group_addr } => { write!(f, "IGMP leave group group_addr={})", group_addr) @@ -361,10 +364,11 @@ impl<'a> fmt::Display for Repr { use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, - f: &mut fmt::Formatter, - indent: &mut PrettyIndent) - -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { match Packet::new_checked(buffer) { Err(err) => writeln!(f, "{}({})", indent, err), Ok(packet) => writeln!(f, "{}{}", indent, packet), @@ -376,7 +380,6 @@ impl> PrettyPrint for Packet { mod test { use super::*; - static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96]; static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25]; @@ -386,8 +389,10 @@ mod test { assert_eq!(packet.msg_type(), Message::LeaveGroup); assert_eq!(packet.max_resp_code(), 0); assert_eq!(packet.checksum(), 0x269); - assert_eq!(packet.group_addr(), - Ipv4Address::from_bytes(&[224, 0, 6, 150])); + assert_eq!( + packet.group_addr(), + Ipv4Address::from_bytes(&[224, 0, 6, 150]) + ); assert_eq!(packet.verify_checksum(), true); } @@ -397,8 +402,10 @@ mod test { assert_eq!(packet.msg_type(), Message::MembershipReportV2); assert_eq!(packet.max_resp_code(), 0); assert_eq!(packet.checksum(), 0x08da); - assert_eq!(packet.group_addr(), - Ipv4Address::from_bytes(&[225, 0, 0, 37])); + assert_eq!( + packet.group_addr(), + Ipv4Address::from_bytes(&[225, 0, 0, 37]) + ); assert_eq!(packet.verify_checksum(), true); } @@ -437,9 +444,7 @@ mod test { #[test] fn duration_to_max_resp_time_max() { for duration in 31744..65536 { - let time = duration_to_max_resp_code( - Duration::from_millis(duration * 100) - ); + let time = duration_to_max_resp_code(Duration::from_millis(duration * 100)); assert_eq!(time, 0xFF); } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 67a25e320..346284106 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -1,12 +1,12 @@ -use core::fmt; use core::convert::From; +use core::fmt; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; #[cfg(feature = "proto-ipv4")] -use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr}; +use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; +use crate::{Error, Result}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] @@ -31,7 +31,7 @@ impl Version { 4 => Ok(Version::Ipv4), #[cfg(feature = "proto-ipv6")] 6 => Ok(Version::Ipv6), - _ => Err(Error::Unrecognized) + _ => Err(Error::Unrecognized), } } } @@ -67,17 +67,17 @@ enum_with_unknown! { impl fmt::Display for Protocol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Protocol::HopByHop => write!(f, "Hop-by-Hop"), - Protocol::Icmp => write!(f, "ICMP"), - Protocol::Igmp => write!(f, "IGMP"), - Protocol::Tcp => write!(f, "TCP"), - Protocol::Udp => write!(f, "UDP"), - Protocol::Ipv6Route => write!(f, "IPv6-Route"), - Protocol::Ipv6Frag => write!(f, "IPv6-Frag"), - Protocol::Icmpv6 => write!(f, "ICMPv6"), - Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"), - Protocol::Ipv6Opts => write!(f, "IPv6-Opts"), - Protocol::Unknown(id) => write!(f, "0x{:02x}", id) + Protocol::HopByHop => write!(f, "Hop-by-Hop"), + Protocol::Icmp => write!(f, "ICMP"), + Protocol::Igmp => write!(f, "IGMP"), + Protocol::Tcp => write!(f, "TCP"), + Protocol::Udp => write!(f, "UDP"), + Protocol::Ipv6Route => write!(f, "IPv6-Route"), + Protocol::Ipv6Frag => write!(f, "IPv6-Frag"), + Protocol::Icmpv6 => write!(f, "ICMPv6"), + Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"), + Protocol::Ipv6Opts => write!(f, "IPv6-Opts"), + Protocol::Unknown(id) => write!(f, "0x{:02x}", id), } } } @@ -107,74 +107,73 @@ impl Address { /// Create an address wrapping an IPv6 address with the given octets. #[cfg(feature = "proto-ipv6")] #[allow(clippy::too_many_arguments)] - pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, - a4: u16, a5: u16, a6: u16, a7: u16) -> Address { + pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7)) } /// Return an address as a sequence of octets, in big-endian. pub fn as_bytes(&self) -> &[u8] { match *self { - Address::Unspecified => &[], + Address::Unspecified => &[], #[cfg(feature = "proto-ipv4")] - Address::Ipv4(ref addr) => addr.as_bytes(), + Address::Ipv4(ref addr) => addr.as_bytes(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(ref addr) => addr.as_bytes(), + Address::Ipv6(ref addr) => addr.as_bytes(), } } /// Query whether the address is a valid unicast address. pub fn is_unicast(&self) -> bool { match *self { - Address::Unspecified => false, + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => addr.is_unicast(), + Address::Ipv4(addr) => addr.is_unicast(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(addr) => addr.is_unicast(), + Address::Ipv6(addr) => addr.is_unicast(), } } /// Query whether the address is a valid multicast address. pub fn is_multicast(&self) -> bool { match *self { - Address::Unspecified => false, + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => addr.is_multicast(), + Address::Ipv4(addr) => addr.is_multicast(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(addr) => addr.is_multicast(), + Address::Ipv6(addr) => addr.is_multicast(), } } /// Query whether the address is the broadcast address. pub fn is_broadcast(&self) -> bool { match *self { - Address::Unspecified => false, + Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => addr.is_broadcast(), + Address::Ipv4(addr) => addr.is_broadcast(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(_) => false, + Address::Ipv6(_) => false, } } /// Query whether the address falls into the "unspecified" range. pub fn is_unspecified(&self) -> bool { match *self { - Address::Unspecified => true, + Address::Unspecified => true, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => addr.is_unspecified(), + Address::Ipv4(addr) => addr.is_unspecified(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(addr) => addr.is_unspecified(), + Address::Ipv6(addr) => addr.is_unspecified(), } } /// Return an unspecified address that has the same IP version as `self`. pub fn to_unspecified(&self) -> Address { match *self { - Address::Unspecified => Address::Unspecified, + Address::Unspecified => Address::Unspecified, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), + Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), + Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), } } @@ -196,7 +195,7 @@ impl Address { } } else if one { // 1 where 0 was expected - return None + return None; } mask >>= 1; } @@ -252,11 +251,11 @@ impl From for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Address::Unspecified => write!(f, "*"), + Address::Unspecified => write!(f, "*"), #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => write!(f, "{}", addr), + Address::Ipv4(addr) => write!(f, "{}", addr), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(addr) => write!(f, "{}", addr), + Address::Ipv6(addr) => write!(f, "{}", addr), } } } @@ -265,11 +264,11 @@ impl fmt::Display for Address { impl defmt::Format for Address { fn format(&self, f: defmt::Formatter) { match self { - &Address::Unspecified => defmt::write!(f, "{:?}", "*"), + &Address::Unspecified => defmt::write!(f, "{:?}", "*"), #[cfg(feature = "proto-ipv4")] - &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr), + &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr), #[cfg(feature = "proto-ipv6")] - &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr), + &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr), } } } @@ -297,8 +296,9 @@ impl Cidr { Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)), - Address::Unspecified => - panic!("a CIDR block cannot be based on an unspecified address"), + Address::Unspecified => { + panic!("a CIDR block cannot be based on an unspecified address") + } } } @@ -306,9 +306,9 @@ impl Cidr { pub fn address(&self) -> Address { match *self { #[cfg(feature = "proto-ipv4")] - Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), + Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), #[cfg(feature = "proto-ipv6")] - Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()), + Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()), } } @@ -316,9 +316,9 @@ impl Cidr { pub fn prefix_len(&self) -> u8 { match *self { #[cfg(feature = "proto-ipv4")] - Cidr::Ipv4(cidr) => cidr.prefix_len(), + Cidr::Ipv4(cidr) => cidr.prefix_len(), #[cfg(feature = "proto-ipv6")] - Cidr::Ipv6(cidr) => cidr.prefix_len(), + Cidr::Ipv6(cidr) => cidr.prefix_len(), } } @@ -327,18 +327,17 @@ impl Cidr { pub fn contains_addr(&self, addr: &Address) -> bool { match (self, addr) { #[cfg(feature = "proto-ipv4")] - (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => - cidr.contains_addr(addr), + (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr), #[cfg(feature = "proto-ipv6")] - (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => - cidr.contains_addr(addr), + (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr), #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))] - (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => - false, + (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => false, (_, &Address::Unspecified) => - // a fully unspecified address covers both IPv4 and IPv6, - // and no CIDR block can do that. - false, + // a fully unspecified address covers both IPv4 and IPv6, + // and no CIDR block can do that. + { + false + } } } @@ -347,14 +346,11 @@ impl Cidr { pub fn contains_subnet(&self, subnet: &Cidr) -> bool { match (self, subnet) { #[cfg(feature = "proto-ipv4")] - (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => - cidr.contains_subnet(other), + (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other), #[cfg(feature = "proto-ipv6")] - (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => - cidr.contains_subnet(other), + (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other), #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))] - (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => - false, + (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false, } } } @@ -377,9 +373,9 @@ impl fmt::Display for Cidr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { #[cfg(feature = "proto-ipv4")] - Cidr::Ipv4(cidr) => write!(f, "{}", cidr), + Cidr::Ipv4(cidr) => write!(f, "{}", cidr), #[cfg(feature = "proto-ipv6")] - Cidr::Ipv6(cidr) => write!(f, "{}", cidr), + Cidr::Ipv6(cidr) => write!(f, "{}", cidr), } } } @@ -389,9 +385,9 @@ impl defmt::Format for Cidr { fn format(&self, f: defmt::Formatter) { match self { #[cfg(feature = "proto-ipv4")] - &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr), + &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr), #[cfg(feature = "proto-ipv6")] - &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr), + &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr), } } } @@ -402,12 +398,15 @@ impl defmt::Format for Cidr { #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct Endpoint { pub addr: Address, - pub port: u16 + pub port: u16, } impl Endpoint { /// An endpoint with unspecified address and port. - pub const UNSPECIFIED: Endpoint = Endpoint { addr: Address::Unspecified, port: 0 }; + pub const UNSPECIFIED: Endpoint = Endpoint { + addr: Address::Unspecified, + port: 0, + }; /// Create an endpoint address from given address and port. pub fn new(addr: Address, port: u16) -> Endpoint { @@ -465,13 +464,19 @@ impl defmt::Format for Endpoint { impl From for Endpoint { fn from(port: u16) -> Endpoint { - Endpoint { addr: Address::Unspecified, port } + Endpoint { + addr: Address::Unspecified, + port, + } } } impl> From<(T, u16)> for Endpoint { fn from((addr, port): (T, u16)) -> Endpoint { - Endpoint { addr: addr.into(), port } + Endpoint { + addr: addr.into(), + port, + } } } @@ -485,11 +490,11 @@ impl> From<(T, u16)> for Endpoint { #[non_exhaustive] pub enum Repr { Unspecified { - src_addr: Address, - dst_addr: Address, - protocol: Protocol, + src_addr: Address, + dst_addr: Address, + protocol: Protocol, payload_len: usize, - hop_limit: u8 + hop_limit: u8, }, #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Repr), @@ -570,25 +575,31 @@ impl Repr { /// Set the payload length. pub fn set_payload_len(&mut self, length: usize) { match *self { - Repr::Unspecified { ref mut payload_len, .. } => - *payload_len = length, + Repr::Unspecified { + ref mut payload_len, + .. + } => *payload_len = length, #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) => - *payload_len = length, + Repr::Ipv4(Ipv4Repr { + ref mut payload_len, + .. + }) => *payload_len = length, #[cfg(feature = "proto-ipv6")] - Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) => - *payload_len = length, + Repr::Ipv6(Ipv6Repr { + ref mut payload_len, + .. + }) => *payload_len = length, } } /// Return the TTL value. pub fn hop_limit(&self) -> u8 { match *self { - Repr::Unspecified { hop_limit, .. } => hop_limit, + Repr::Unspecified { hop_limit, .. } => hop_limit, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] - Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit, + Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit, } } @@ -607,15 +618,15 @@ impl Repr { $ipty(addr) => { $iprepr.src_addr = addr; return Ok($reprty($iprepr)); - }, - _ => () + } + _ => (), } } Err(Error::Unaddressable) } else { Ok($reprty($iprepr)) } - } + }; } match self { @@ -623,12 +634,16 @@ impl Repr { &Repr::Unspecified { src_addr: src_addr @ Address::Unspecified, dst_addr: Address::Ipv4(dst_addr), - protocol, payload_len, hop_limit - } | - &Repr::Unspecified { + protocol, + payload_len, + hop_limit, + } + | &Repr::Unspecified { src_addr: src_addr @ Address::Ipv4(_), dst_addr: Address::Ipv4(dst_addr), - protocol, payload_len, hop_limit + protocol, + payload_len, + hop_limit, } if src_addr.is_unspecified() => { let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr { Some(src_ipv4_addr) @@ -642,8 +657,11 @@ impl Repr { } } Ok(Repr::Ipv4(Ipv4Repr { - src_addr: src_addr.ok_or(Error::Unaddressable)?, - dst_addr, protocol, payload_len, hop_limit + src_addr: src_addr.ok_or(Error::Unaddressable)?, + dst_addr, + protocol, + payload_len, + hop_limit, })) } @@ -651,12 +669,16 @@ impl Repr { &Repr::Unspecified { src_addr: src_addr @ Address::Unspecified, dst_addr: Address::Ipv6(dst_addr), - protocol, payload_len, hop_limit - } | - &Repr::Unspecified { + protocol, + payload_len, + hop_limit, + } + | &Repr::Unspecified { src_addr: src_addr @ Address::Ipv6(_), dst_addr: Address::Ipv6(dst_addr), - protocol, payload_len, hop_limit + protocol, + payload_len, + hop_limit, } if src_addr.is_unspecified() => { let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr { Some(src_ipv6_addr) @@ -670,9 +692,11 @@ impl Repr { } } Ok(Repr::Ipv6(Ipv6Repr { - src_addr: src_addr.ok_or(Error::Unaddressable)?, + src_addr: src_addr.ok_or(Error::Unaddressable)?, next_header: protocol, - dst_addr, payload_len, hop_limit + dst_addr, + payload_len, + hop_limit, })) } @@ -680,41 +704,45 @@ impl Repr { &Repr::Unspecified { src_addr: Address::Ipv4(src_addr), dst_addr: Address::Ipv4(dst_addr), - protocol, payload_len, hop_limit - } => { - Ok(Repr::Ipv4(Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, - protocol: protocol, - payload_len: payload_len, hop_limit - })) - } + protocol, + payload_len, + hop_limit, + } => Ok(Repr::Ipv4(Ipv4Repr { + src_addr: src_addr, + dst_addr: dst_addr, + protocol: protocol, + payload_len: payload_len, + hop_limit, + })), #[cfg(feature = "proto-ipv6")] &Repr::Unspecified { src_addr: Address::Ipv6(src_addr), dst_addr: Address::Ipv6(dst_addr), - protocol, payload_len, hop_limit - } => { - Ok(Repr::Ipv6(Ipv6Repr { - src_addr: src_addr, - dst_addr: dst_addr, - next_header: protocol, - payload_len: payload_len, - hop_limit: hop_limit - })) - } + protocol, + payload_len, + hop_limit, + } => Ok(Repr::Ipv6(Ipv6Repr { + src_addr: src_addr, + dst_addr: dst_addr, + next_header: protocol, + payload_len: payload_len, + hop_limit: hop_limit, + })), #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(mut repr) => - resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs), + &Repr::Ipv4(mut repr) => { + resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs) + } #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(mut repr) => - resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs), + &Repr::Ipv6(mut repr) => { + resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs) + } - &Repr::Unspecified { .. } => - panic!("source and destination IP address families do not match"), + &Repr::Unspecified { .. } => { + panic!("source and destination IP address families do not match") + } } } @@ -724,14 +752,11 @@ impl Repr { /// This function panics if invoked on an unspecified representation. pub fn buffer_len(&self) -> usize { match *self { - Repr::Unspecified { .. } => - panic!("unspecified IP representation"), + Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(repr) => - repr.buffer_len(), + Repr::Ipv4(repr) => repr.buffer_len(), #[cfg(feature = "proto-ipv6")] - Repr::Ipv6(repr) => - repr.buffer_len(), + Repr::Ipv6(repr) => repr.buffer_len(), } } @@ -739,16 +764,17 @@ impl Repr { /// /// # Panics /// This function panics if invoked on an unspecified representation. - pub fn emit + AsMut<[u8]>>(&self, buffer: T, _checksum_caps: &ChecksumCapabilities) { + pub fn emit + AsMut<[u8]>>( + &self, + buffer: T, + _checksum_caps: &ChecksumCapabilities, + ) { match *self { - Repr::Unspecified { .. } => - panic!("unspecified IP representation"), + Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(repr) => - repr.emit(&mut Ipv4Packet::new_unchecked(buffer), &_checksum_caps), + Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), &_checksum_caps), #[cfg(feature = "proto-ipv6")] - Repr::Ipv6(repr) => - repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), + Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), } } @@ -816,8 +842,12 @@ pub mod checksum { } /// Compute an IP pseudo header checksum. - pub fn pseudo_header(src_addr: &Address, dst_addr: &Address, - protocol: Protocol, length: u32) -> u16 { + pub fn pseudo_header( + src_addr: &Address, + dst_addr: &Address, + protocol: Protocol, + length: u32, + ) -> u16 { match (src_addr, dst_addr) { #[cfg(feature = "proto-ipv4")] (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => { @@ -828,9 +858,9 @@ pub mod checksum { combine(&[ data(src_addr.as_bytes()), data(dst_addr.as_bytes()), - data(&proto_len[..]) + data(&proto_len[..]), ]) - }, + } #[cfg(feature = "proto-ipv6")] (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => { @@ -840,12 +870,14 @@ pub mod checksum { combine(&[ data(src_addr.as_bytes()), data(dst_addr.as_bytes()), - data(&proto_len[..]) + data(&proto_len[..]), ]) } - _ => panic!("Unexpected pseudo header addresses: {}, {}", - src_addr, dst_addr) + _ => panic!( + "Unexpected pseudo header addresses: {}, {}", + src_addr, dst_addr + ), } } @@ -861,14 +893,18 @@ pub mod checksum { use crate::wire::pretty_print::PrettyIndent; -pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &mut PrettyIndent, - ip_repr: T, payload: &[u8]) -> fmt::Result { - #[cfg(feature = "proto-ipv4")] - use crate::wire::Icmpv4Packet; +pub fn pretty_print_ip_payload>( + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ip_repr: T, + payload: &[u8], +) -> fmt::Result { #[cfg(feature = "proto-ipv4")] use super::pretty_print::PrettyPrint; - use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr}; use crate::wire::ip::checksum::format_checksum; + #[cfg(feature = "proto-ipv4")] + use crate::wire::Icmpv4Packet; + use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr}; let checksum_caps = ChecksumCapabilities::ignored(); let repr = ip_repr.into(); @@ -883,13 +919,23 @@ pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &m match UdpPacket::<&[u8]>::new_checked(payload) { Err(err) => write!(f, "{}({})", indent, err), Ok(udp_packet) => { - match UdpRepr::parse(&udp_packet, &repr.src_addr(), - &repr.dst_addr(), &checksum_caps) { + match UdpRepr::parse( + &udp_packet, + &repr.src_addr(), + &repr.dst_addr(), + &checksum_caps, + ) { Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err), Ok(udp_repr) => { - write!(f, "{}{} len={}", indent, udp_repr, udp_packet.payload().len())?; - let valid = udp_packet.verify_checksum(&repr.src_addr(), - &repr.dst_addr()); + write!( + f, + "{}{} len={}", + indent, + udp_repr, + udp_packet.payload().len() + )?; + let valid = + udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); format_checksum(f, valid) } } @@ -901,20 +947,24 @@ pub fn pretty_print_ip_payload>(f: &mut fmt::Formatter, indent: &m match TcpPacket::<&[u8]>::new_checked(payload) { Err(err) => write!(f, "{}({})", indent, err), Ok(tcp_packet) => { - match TcpRepr::parse(&tcp_packet, &repr.src_addr(), - &repr.dst_addr(), &checksum_caps) { + match TcpRepr::parse( + &tcp_packet, + &repr.src_addr(), + &repr.dst_addr(), + &checksum_caps, + ) { Err(err) => write!(f, "{}{} ({})", indent, tcp_packet, err), Ok(tcp_repr) => { write!(f, "{}{}", indent, tcp_repr)?; - let valid = tcp_packet.verify_checksum(&repr.src_addr(), - &repr.dst_addr()); + let valid = + tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); format_checksum(f, valid) } } } } } - _ => Ok(()) + _ => Ok(()), } } @@ -923,17 +973,21 @@ pub(crate) mod test { #![allow(unused)] #[cfg(feature = "proto-ipv6")] - pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1])); + pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ])); #[cfg(feature = "proto-ipv6")] - pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2])); + pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + ])); #[cfg(feature = "proto-ipv6")] - pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3])); + pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + ])); #[cfg(feature = "proto-ipv6")] - pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4])); + pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + ])); #[cfg(feature = "proto-ipv6")] pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED); @@ -948,9 +1002,8 @@ pub(crate) mod test { #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); - use super::*; - use crate::wire::{IpAddress, IpProtocol,IpCidr}; + use crate::wire::{IpAddress, IpCidr, IpProtocol}; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Repr}; @@ -969,124 +1022,132 @@ pub(crate) mod test { let payload_len = 10; assert_eq!( - Repr::Unspecified{ - src_addr: $ip_addr(ip_addr_a), - dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + Repr::Unspecified { + src_addr: $ip_addr(ip_addr_a), + dst_addr: $ip_addr(ip_addr_b), + protocol: proto, hop_limit: 0x2a, payload_len, - }.lower(&[]), - Ok($ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + } + .lower(&[]), + Ok($ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 0x2a, payload_len })) ); assert_eq!( - Repr::Unspecified{ - src_addr: IpAddress::Unspecified, - dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + Repr::Unspecified { + src_addr: IpAddress::Unspecified, + dst_addr: $ip_addr(ip_addr_b), + protocol: proto, hop_limit: 64, payload_len - }.lower(&[]), + } + .lower(&[]), Err(Error::Unaddressable) ); assert_eq!( - Repr::Unspecified{ - src_addr: IpAddress::Unspecified, - dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + Repr::Unspecified { + src_addr: IpAddress::Unspecified, + dst_addr: $ip_addr(ip_addr_b), + protocol: proto, hop_limit: 64, payload_len - }.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + } + .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), + Ok($ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 64, payload_len })) ); assert_eq!( - Repr::Unspecified{ - src_addr: $ip_addr($unspecified), - dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + Repr::Unspecified { + src_addr: $ip_addr($unspecified), + dst_addr: $ip_addr(ip_addr_b), + protocol: proto, hop_limit: 64, payload_len - }.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + } + .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), + Ok($ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 64, payload_len })) ); assert_eq!( - Repr::Unspecified{ - src_addr: $ip_addr($unspecified), - dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + Repr::Unspecified { + src_addr: $ip_addr($unspecified), + dst_addr: $ip_addr(ip_addr_b), + protocol: proto, hop_limit: 64, payload_len - }.lower(&[]), - Ok($ip_repr($repr{ - src_addr: $unspecified, - dst_addr: ip_addr_b, - $nxthdr: proto, + } + .lower(&[]), + Ok($ip_repr($repr { + src_addr: $unspecified, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 64, payload_len })) ); assert_eq!( - $ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + $ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 255, payload_len - }).lower(&[]), - Ok($ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + }) + .lower(&[]), + Ok($ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 255, payload_len })) ); assert_eq!( - $ip_repr($repr{ - src_addr: $unspecified, - dst_addr: ip_addr_b, - $nxthdr: proto, + $ip_repr($repr { + src_addr: $unspecified, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 255, payload_len - }).lower(&[]), + }) + .lower(&[]), Err(Error::Unaddressable) ); assert_eq!( - $ip_repr($repr{ - src_addr: $unspecified, - dst_addr: ip_addr_b, - $nxthdr: proto, + $ip_repr($repr { + src_addr: $unspecified, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 64, payload_len - }).lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr{ - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - $nxthdr: proto, + }) + .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), + Ok($ip_repr($repr { + src_addr: ip_addr_a, + dst_addr: ip_addr_b, + $nxthdr: proto, hop_limit: 64, payload_len })) @@ -1095,15 +1156,31 @@ pub(crate) mod test { } }; (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => { - generate_common_tests!(ipv4, Ipv4Repr, Repr::Ipv4, IpAddress::Ipv4, - Ipv4Address::from_bytes, protocol, $addr_bytes_a, - $addr_bytes_b, Ipv4Address::UNSPECIFIED); + generate_common_tests!( + ipv4, + Ipv4Repr, + Repr::Ipv4, + IpAddress::Ipv4, + Ipv4Address::from_bytes, + protocol, + $addr_bytes_a, + $addr_bytes_b, + Ipv4Address::UNSPECIFIED + ); }; (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => { - generate_common_tests!(ipv6, Ipv6Repr, Repr::Ipv6, IpAddress::Ipv6, - Ipv6Address::from_bytes, next_header, $addr_bytes_a, - $addr_bytes_b, Ipv6Address::UNSPECIFIED); - } + generate_common_tests!( + ipv6, + Ipv6Repr, + Repr::Ipv6, + IpAddress::Ipv6, + Ipv6Address::from_bytes, + next_header, + $addr_bytes_a, + $addr_bytes_b, + Ipv6Address::UNSPECIFIED + ); + }; } #[cfg(feature = "proto-ipv4")] @@ -1121,12 +1198,13 @@ pub(crate) mod test { #[should_panic(expected = "source and destination IP address families do not match")] fn test_lower_between_families() { Repr::Unspecified { - src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED), - dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED), - protocol: IpProtocol::Icmpv6, + src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED), + dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED), + protocol: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: 0 - }.lower(&[]); + payload_len: 0, + } + .lower(&[]); } #[test] @@ -1138,10 +1216,7 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv4")] fn to_prefix_len_ipv4() { fn test_eq>(prefix_len: u8, mask: A) { - assert_eq!( - Some(prefix_len), - mask.into().to_prefix_len() - ); + assert_eq!(Some(prefix_len), mask.into().to_prefix_len()); } test_eq(0, Ipv4Address::new(0, 0, 0, 0)); @@ -1181,25 +1256,36 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv4")] fn to_prefix_len_ipv4_error() { - assert_eq!(None, IpAddress::from(Ipv4Address::new(255,255,255,1)).to_prefix_len()); + assert_eq!( + None, + IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).to_prefix_len() + ); } #[test] #[cfg(feature = "proto-ipv6")] fn to_prefix_len_ipv6() { fn test_eq>(prefix_len: u8, mask: A) { - assert_eq!( - Some(prefix_len), - mask.into().to_prefix_len() - ); + assert_eq!(Some(prefix_len), mask.into().to_prefix_len()); } test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0)); - test_eq(128, Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)); + test_eq( + 128, + Ipv6Address::new( + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + ), + ); } #[cfg(feature = "proto-ipv6")] fn to_prefix_len_ipv6_error() { - assert_eq!(None, IpAddress::from(Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1)).to_prefix_len()); + assert_eq!( + None, + IpAddress::from(Ipv6Address::new( + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1 + )) + .to_prefix_len() + ); } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 221d656b1..bdadc99b8 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -1,9 +1,9 @@ -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::{checksum, pretty_print_ip_payload}; +use crate::{Error, Result}; pub use super::IpProtocol as Protocol; @@ -27,10 +27,10 @@ pub struct Address(pub [u8; 4]); impl Address { /// An unspecified address. - pub const UNSPECIFIED: Address = Address([0x00; 4]); + pub const UNSPECIFIED: Address = Address([0x00; 4]); /// The broadcast address. - pub const BROADCAST: Address = Address([0xff; 4]); + pub const BROADCAST: Address = Address([0xff; 4]); /// All multicast-capable nodes pub const MULTICAST_ALL_SYSTEMS: Address = Address([224, 0, 0, 1]); @@ -60,9 +60,7 @@ impl Address { /// Query whether the address is an unicast address. pub fn is_unicast(&self) -> bool { - !(self.is_broadcast() || - self.is_multicast() || - self.is_unspecified()) + !(self.is_broadcast() || self.is_multicast() || self.is_unspecified()) } /// Query whether the address is the broadcast address. @@ -115,7 +113,14 @@ impl fmt::Display for Address { #[cfg(feature = "defmt")] impl defmt::Format for Address { fn format(&self, f: defmt::Formatter) { - defmt::write!(f, "{=u8}.{=u8}.{=u8}.{=u8}", self.0[0], self.0[1], self.0[2], self.0[3]) + defmt::write!( + f, + "{=u8}.{=u8}.{=u8}.{=u8}", + self.0[0], + self.0[1], + self.0[2], + self.0[3] + ) } } @@ -123,7 +128,7 @@ impl defmt::Format for Address { /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct Cidr { - address: Address, + address: Address, prefix_len: u8, } @@ -137,14 +142,20 @@ impl Cidr { // Replace with const panic (or assert) when stabilized // see: https://github.com/rust-lang/rust/issues/51999 ["Prefix length should be <= 32"][(prefix_len > 32) as usize]; - Cidr { address, prefix_len } + Cidr { + address, + prefix_len, + } } /// Create an IPv4 CIDR block from the given address and network mask. pub fn from_netmask(addr: Address, netmask: Address) -> Result { let netmask = NetworkEndian::read_u32(&netmask.0[..]); if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() { - Ok(Cidr { address: addr, prefix_len: netmask.count_ones() as u8 }) + Ok(Cidr { + address: addr, + prefix_len: netmask.count_ones() as u8, + }) } else { Err(Error::Illegal) } @@ -170,8 +181,8 @@ impl Cidr { let data = [ ((number >> 24) & 0xff) as u8, ((number >> 16) & 0xff) as u8, - ((number >> 8) & 0xff) as u8, - ((number >> 0) & 0xff) as u8, + ((number >> 8) & 0xff) as u8, + ((number >> 0) & 0xff) as u8, ]; Address(data) @@ -190,8 +201,8 @@ impl Cidr { let data = [ ((number >> 24) & 0xff) as u8, ((number >> 16) & 0xff) as u8, - ((number >> 8) & 0xff) as u8, - ((number >> 0) & 0xff) as u8, + ((number >> 8) & 0xff) as u8, + ((number >> 0) & 0xff) as u8, ]; Some(Address(data)) @@ -206,14 +217,19 @@ impl Cidr { self.address.0[2] & mask[2], self.address.0[3] & mask[3], ]; - Cidr { address: Address(network), prefix_len: self.prefix_len } + Cidr { + address: Address(network), + prefix_len: self.prefix_len, + } } /// Query whether the subnetwork described by this IPv4 CIDR block contains /// the given address. pub fn contains_addr(&self, addr: &Address) -> bool { // right shift by 32 is not legal - if self.prefix_len == 0 { return true } + if self.prefix_len == 0 { + return true; + } let shift = 32 - self.prefix_len; let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift; @@ -245,18 +261,18 @@ impl defmt::Format for Cidr { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } mod field { use crate::wire::field::*; - pub const VER_IHL: usize = 0; + pub const VER_IHL: usize = 0; pub const DSCP_ECN: usize = 1; - pub const LENGTH: Field = 2..4; - pub const IDENT: Field = 4..6; - pub const FLG_OFF: Field = 6..8; - pub const TTL: usize = 8; + pub const LENGTH: Field = 2..4; + pub const IDENT: Field = 4..6; + pub const FLG_OFF: Field = 6..8; + pub const TTL: usize = 8; pub const PROTOCOL: usize = 9; pub const CHECKSUM: Field = 10..12; pub const SRC_ADDR: Field = 12..16; @@ -265,7 +281,6 @@ mod field { pub const HEADER_LEN: usize = field::DST_ADDR.end; - impl> Packet { /// Imbue a raw octet buffer with IPv4 packet structure. pub fn new_unchecked(buffer: T) -> Packet { @@ -414,7 +429,9 @@ impl> Packet { /// # Fuzzing /// This function always returns `true` when fuzzing. pub fn verify_checksum(&self) -> bool { - if cfg!(fuzzing) { return true } + if cfg!(fuzzing) { + return true; + } let data = self.buffer.as_ref(); checksum::data(&data[..self.header_len() as usize]) == !0 @@ -572,36 +589,46 @@ impl> AsRef<[u8]> for Packet { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { - pub src_addr: Address, - pub dst_addr: Address, - pub protocol: Protocol, + pub src_addr: Address, + pub dst_addr: Address, + pub protocol: Protocol, pub payload_len: usize, - pub hop_limit: u8 + pub hop_limit: u8, } impl Repr { /// Parse an Internet Protocol version 4 packet and return a high-level representation. - pub fn parse + ?Sized>(packet: &Packet<&T>, - checksum_caps: &ChecksumCapabilities) -> Result { + pub fn parse + ?Sized>( + packet: &Packet<&T>, + checksum_caps: &ChecksumCapabilities, + ) -> Result { // Version 4 is expected. - if packet.version() != 4 { return Err(Error::Malformed) } + if packet.version() != 4 { + return Err(Error::Malformed); + } // Valid checksum is expected. - if checksum_caps.ipv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) } + if checksum_caps.ipv4.rx() && !packet.verify_checksum() { + return Err(Error::Checksum); + } // We do not support fragmentation. - if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error::Fragmented) } + if packet.more_frags() || packet.frag_offset() != 0 { + return Err(Error::Fragmented); + } // Since the packet is not fragmented, it must include the entire payload. let payload_len = packet.total_len() as usize - packet.header_len() as usize; - if packet.payload().len() < payload_len { return Err(Error::Truncated) } + if packet.payload().len() < payload_len { + return Err(Error::Truncated); + } // All DSCP values are acceptable, since they are of no concern to receiving endpoint. // All ECN values are acceptable, since ECN requires opt-in from both endpoints. // All TTL values are acceptable, since we do not perform routing. Ok(Repr { - src_addr: packet.src_addr(), - dst_addr: packet.dst_addr(), - protocol: packet.protocol(), + src_addr: packet.src_addr(), + dst_addr: packet.dst_addr(), + protocol: packet.protocol(), payload_len: payload_len, - hop_limit: packet.hop_limit() + hop_limit: packet.hop_limit(), }) } @@ -612,7 +639,11 @@ impl Repr { } /// Emit a high-level representation into an Internet Protocol version 4 packet. - pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet, checksum_caps: &ChecksumCapabilities) { + pub fn emit + AsMut<[u8]>>( + &self, + packet: &mut Packet, + checksum_caps: &ChecksumCapabilities, + ) { packet.set_version(4); packet.set_header_len(field::DST_ADDR.end as u8); packet.set_dscp(0); @@ -645,8 +676,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { Ok(repr) => write!(f, "{}", repr), Err(err) => { write!(f, "IPv4 ({})", err)?; - write!(f, " src={} dst={} proto={} hop_limit={}", - self.src_addr(), self.dst_addr(), self.protocol(), self.hop_limit())?; + write!( + f, + " src={} dst={} proto={} hop_limit={}", + self.src_addr(), + self.dst_addr(), + self.protocol(), + self.hop_limit() + )?; if self.version() != 4 { write!(f, " ver={}", self.version())?; } @@ -680,32 +717,36 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IPv4 src={} dst={} proto={}", - self.src_addr, self.dst_addr, self.protocol) + write!( + f, + "IPv4 src={} dst={} proto={}", + self.src_addr, self.dst_addr, self.protocol + ) } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { use crate::wire::ip::checksum::format_checksum; let checksum_caps = ChecksumCapabilities::ignored(); let (ip_repr, payload) = match Packet::new_checked(buffer) { Err(err) => return write!(f, "{}({})", indent, err), - Ok(ip_packet) => { - match Repr::parse(&ip_packet, &checksum_caps) { - Err(_) => return Ok(()), - Ok(ip_repr) => { - write!(f, "{}{}", indent, ip_repr)?; - format_checksum(f, ip_packet.verify_checksum())?; - (ip_repr, ip_packet.payload()) - } + Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) { + Err(_) => return Ok(()), + Ok(ip_repr) => { + write!(f, "{}{}", indent, ip_repr)?; + format_checksum(f, ip_packet.verify_checksum())?; + (ip_repr, ip_packet.payload()) } - } + }, }; pretty_print_ip_payload(f, indent, ip_repr, payload) @@ -716,20 +757,12 @@ impl> PrettyPrint for Packet { mod test { use super::*; - static PACKET_BYTES: [u8; 30] = - [0x45, 0x00, 0x00, 0x1e, - 0x01, 0x02, 0x62, 0x03, - 0x1a, 0x01, 0xd5, 0x6e, - 0x11, 0x12, 0x13, 0x14, - 0x21, 0x22, 0x23, 0x24, - 0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff]; - - static PAYLOAD_BYTES: [u8; 10] = - [0xaa, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff]; + static PACKET_BYTES: [u8; 30] = [ + 0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12, 0x13, + 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + ]; + + static PAYLOAD_BYTES: [u8; 10] = [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]; #[test] fn test_deconstruct() { @@ -781,10 +814,14 @@ mod test { bytes.extend(&PACKET_BYTES[..]); bytes.push(0); - assert_eq!(Packet::new_unchecked(&bytes).payload().len(), - PAYLOAD_BYTES.len()); - assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(), - PAYLOAD_BYTES.len()); + assert_eq!( + Packet::new_unchecked(&bytes).payload().len(), + PAYLOAD_BYTES.len() + ); + assert_eq!( + Packet::new_unchecked(&mut bytes).payload_mut().len(), + PAYLOAD_BYTES.len() + ); } #[test] @@ -793,28 +830,23 @@ mod test { bytes.extend(&PACKET_BYTES[..]); Packet::new_unchecked(&mut bytes).set_total_len(128); - assert_eq!(Packet::new_checked(&bytes).unwrap_err(), - Error::Truncated); + assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated); } - static REPR_PACKET_BYTES: [u8; 24] = - [0x45, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x40, 0x00, - 0x40, 0x01, 0xd2, 0x79, - 0x11, 0x12, 0x13, 0x14, - 0x21, 0x22, 0x23, 0x24, - 0xaa, 0x00, 0x00, 0xff]; + static REPR_PACKET_BYTES: [u8; 24] = [ + 0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13, + 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff, + ]; - static REPR_PAYLOAD_BYTES: [u8; 4] = - [0xaa, 0x00, 0x00, 0xff]; + static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; fn packet_repr() -> Repr { Repr { - src_addr: Address([0x11, 0x12, 0x13, 0x14]), - dst_addr: Address([0x21, 0x22, 0x23, 0x24]), - protocol: Protocol::Icmp, + src_addr: Address([0x11, 0x12, 0x13, 0x14]), + dst_addr: Address([0x21, 0x22, 0x23, 0x24]), + protocol: Protocol::Icmp, payload_len: 4, - hop_limit: 64 + hop_limit: 64, } } @@ -833,7 +865,10 @@ mod test { packet.set_version(6); packet.fill_checksum(); let packet = Packet::new_unchecked(&*packet.into_inner()); - assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed)); + assert_eq!( + Repr::parse(&packet, &ChecksumCapabilities::default()), + Err(Error::Malformed) + ); } #[test] @@ -876,28 +911,34 @@ mod test { let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24); let inside_subnet = [ - [192, 168, 1, 0], [192, 168, 1, 1], - [192, 168, 1, 2], [192, 168, 1, 10], - [192, 168, 1, 127], [192, 168, 1, 255], + [192, 168, 1, 0], + [192, 168, 1, 1], + [192, 168, 1, 2], + [192, 168, 1, 10], + [192, 168, 1, 127], + [192, 168, 1, 255], ]; let outside_subnet = [ - [192, 168, 0, 0], [127, 0, 0, 1], - [192, 168, 2, 0], [192, 168, 0, 255], - [ 0, 0, 0, 0], [255, 255, 255, 255], + [192, 168, 0, 0], + [127, 0, 0, 1], + [192, 168, 2, 0], + [192, 168, 0, 255], + [0, 0, 0, 0], + [255, 255, 255, 255], ]; let subnets = [ - ([192, 168, 1, 0], 32), - ([192, 168, 1, 255], 24), - ([192, 168, 1, 10], 30), + ([192, 168, 1, 0], 32), + ([192, 168, 1, 255], 24), + ([192, 168, 1, 10], 30), ]; let not_subnets = [ - ([192, 168, 1, 10], 23), - ([127, 0, 0, 1], 8), - ([192, 168, 1, 0], 0), - ([192, 168, 0, 255], 32), + ([192, 168, 1, 10], 23), + ([127, 0, 0, 1], 8), + ([192, 168, 1, 0], 0), + ([192, 168, 0, 255], 32), ]; for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) { @@ -908,13 +949,17 @@ mod test { assert!(!cidr.contains_addr(&addr)); } - for subnet in subnets.iter().map( - |&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) { + for subnet in subnets + .iter() + .map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) + { assert!(cidr.contains_subnet(&subnet)); } - for subnet in not_subnets.iter().map( - |&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) { + for subnet in not_subnets + .iter() + .map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) + { assert!(!cidr.contains_subnet(&subnet)); } @@ -924,94 +969,175 @@ mod test { #[test] fn test_cidr_from_netmask() { - assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(), - true); - assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(), - true); - assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(), - Cidr::new(Address([0, 0, 0, 1]), 24)); - assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(), - Cidr::new(Address([192, 168, 0, 1]), 16)); - assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(), - Cidr::new(Address([172, 16, 0, 1]), 12)); - assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(), - Cidr::new(Address([255, 255, 255, 1]), 24)); - assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(), - Cidr::new(Address([255, 255, 255, 255]), 32)); + assert_eq!( + Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(), + true + ); + assert_eq!( + Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(), + true + ); + assert_eq!( + Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(), + Cidr::new(Address([0, 0, 0, 1]), 24) + ); + assert_eq!( + Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(), + Cidr::new(Address([192, 168, 0, 1]), 16) + ); + assert_eq!( + Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(), + Cidr::new(Address([172, 16, 0, 1]), 12) + ); + assert_eq!( + Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(), + Cidr::new(Address([255, 255, 255, 1]), 24) + ); + assert_eq!( + Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])) + .unwrap(), + Cidr::new(Address([255, 255, 255, 255]), 32) + ); } #[test] fn test_cidr_netmask() { - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(), - Address([0, 0, 0, 0])); - assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(), - Address([255, 255, 255, 0])); - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(), - Address([255, 255, 255, 255])); - assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(), - Address([255, 0, 0, 0])); - assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(), - Address([255, 255, 0, 0])); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(), - Address([255, 255, 0, 0])); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(), - Address([255, 255, 128, 0])); - assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(), - Address([255, 240, 0, 0])); - assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(), - Address([255, 255, 255, 0])); - assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(), - Address([255, 255, 255, 255])); + assert_eq!( + Cidr::new(Address([0, 0, 0, 0]), 0).netmask(), + Address([0, 0, 0, 0]) + ); + assert_eq!( + Cidr::new(Address([0, 0, 0, 1]), 24).netmask(), + Address([255, 255, 255, 0]) + ); + assert_eq!( + Cidr::new(Address([0, 0, 0, 0]), 32).netmask(), + Address([255, 255, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([127, 0, 0, 0]), 8).netmask(), + Address([255, 0, 0, 0]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 0, 0]), 16).netmask(), + Address([255, 255, 0, 0]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 16).netmask(), + Address([255, 255, 0, 0]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 17).netmask(), + Address([255, 255, 128, 0]) + ); + assert_eq!( + Cidr::new(Address([172, 16, 0, 0]), 12).netmask(), + Address([255, 240, 0, 0]) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 1]), 24).netmask(), + Address([255, 255, 255, 0]) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 255]), 32).netmask(), + Address([255, 255, 255, 255]) + ); } #[test] fn test_cidr_broadcast() { - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(), - Address([255, 255, 255, 255])); - assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(), - Address([0, 0, 0, 255])); - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), - None); - assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(), - Address([127, 255, 255, 255])); - assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(), - Address([192, 168, 255, 255])); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(), - Address([192, 168, 255, 255])); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(), - Address([192, 168, 127, 255])); - assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(), - Address([172, 31, 255, 255])); - assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(), - Address([255, 255, 255, 255])); - assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(), - None); - assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(), - None); - + assert_eq!( + Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(), + Address([255, 255, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(), + Address([0, 0, 0, 255]) + ); + assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), None); + assert_eq!( + Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(), + Address([127, 255, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 0, 0]), 16) + .broadcast() + .unwrap(), + Address([192, 168, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 16) + .broadcast() + .unwrap(), + Address([192, 168, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 17) + .broadcast() + .unwrap(), + Address([192, 168, 127, 255]) + ); + assert_eq!( + Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(), + Address([172, 31, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 1]), 24) + .broadcast() + .unwrap(), + Address([255, 255, 255, 255]) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(), + None + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(), + None + ); } #[test] fn test_cidr_network() { - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(), - Cidr::new(Address([0, 0, 0, 0]), 0)); - assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(), - Cidr::new(Address([0, 0, 0, 0]), 24)); - assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(), - Cidr::new(Address([0, 0, 0, 0]), 32)); - assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(), - Cidr::new(Address([127, 0, 0, 0]), 8)); - assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(), - Cidr::new(Address([192, 168, 0, 0]), 16)); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(), - Cidr::new(Address([192, 168, 0, 0]), 16)); - assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(), - Cidr::new(Address([192, 168, 0, 0]), 17)); - assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(), - Cidr::new(Address([172, 16, 0, 0]), 12)); - assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(), - Cidr::new(Address([255, 255, 255, 0]), 24)); - assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(), - Cidr::new(Address([255, 255, 255, 255]), 32)); + assert_eq!( + Cidr::new(Address([0, 0, 0, 0]), 0).network(), + Cidr::new(Address([0, 0, 0, 0]), 0) + ); + assert_eq!( + Cidr::new(Address([0, 0, 0, 1]), 24).network(), + Cidr::new(Address([0, 0, 0, 0]), 24) + ); + assert_eq!( + Cidr::new(Address([0, 0, 0, 0]), 32).network(), + Cidr::new(Address([0, 0, 0, 0]), 32) + ); + assert_eq!( + Cidr::new(Address([127, 0, 0, 0]), 8).network(), + Cidr::new(Address([127, 0, 0, 0]), 8) + ); + assert_eq!( + Cidr::new(Address([192, 168, 0, 0]), 16).network(), + Cidr::new(Address([192, 168, 0, 0]), 16) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 16).network(), + Cidr::new(Address([192, 168, 0, 0]), 16) + ); + assert_eq!( + Cidr::new(Address([192, 168, 1, 1]), 17).network(), + Cidr::new(Address([192, 168, 0, 0]), 17) + ); + assert_eq!( + Cidr::new(Address([172, 16, 0, 1]), 12).network(), + Cidr::new(Address([172, 16, 0, 0]), 12) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 1]), 24).network(), + Cidr::new(Address([255, 255, 255, 0]), 24) + ); + assert_eq!( + Cidr::new(Address([255, 255, 255, 255]), 32).network(), + Cidr::new(Address([255, 255, 255, 255]), 32) + ); } } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 575219c54..cb00586d3 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -1,12 +1,12 @@ #![deny(missing_docs)] -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; -use crate::{Error, Result}; use crate::wire::ip::pretty_print_ip_payload; #[cfg(feature = "proto-ipv4")] use crate::wire::ipv4; +use crate::{Error, Result}; pub use super::IpProtocol as Protocol; @@ -29,28 +29,30 @@ impl Address { /// The link-local [all routers multicast address]. /// /// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 - pub const LINK_LOCAL_ALL_NODES: Address = - Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + pub const LINK_LOCAL_ALL_NODES: Address = Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, + ]); /// The link-local [all nodes multicast address]. /// /// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 - pub const LINK_LOCAL_ALL_ROUTERS: Address = - Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]); + pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, + ]); /// The [loopback address]. /// /// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - pub const LOOPBACK: Address = - Address([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); + pub const LOOPBACK: Address = Address([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, + ]); /// Construct an IPv6 address from parts. #[allow(clippy::too_many_arguments)] - pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, - a4: u16, a5: u16, a6: u16, a7: u16) -> Address { + pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { let mut addr = [0u8; 16]; NetworkEndian::write_u16(&mut addr[0..2], a0); NetworkEndian::write_u16(&mut addr[2..4], a1); @@ -127,8 +129,7 @@ impl Address { /// /// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6 pub fn is_link_local(&self) -> bool { - self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00] + self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] } /// Query whether the IPv6 address is the [loopback address]. @@ -149,7 +150,9 @@ impl Address { /// Convert an IPv4 mapped IPv6 address to an IPv4 address. pub fn as_ipv4(&self) -> Option { if self.is_ipv4_mapped() { - Some(ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15])) + Some(ipv4::Address::new( + self.0[12], self.0[13], self.0[14], self.0[15], + )) } else { None } @@ -180,8 +183,10 @@ impl Address { /// unicast. pub fn solicited_node(&self) -> Address { assert!(self.is_unicast()); - let mut bytes = [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let mut bytes = [ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; bytes[14..].copy_from_slice(&self.0[14..]); Address(bytes) } @@ -204,7 +209,11 @@ impl From

for ::std::net::Ipv6Addr { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.is_ipv4_mapped() { - return write!(f, "::ffff:{}.{}.{}.{}", self.0[12], self.0[13], self.0[14], self.0[15]) + return write!( + f, + "::ffff:{}.{}.{}.{}", + self.0[12], self.0[13], self.0[14], self.0[15] + ); } // The string representation of an IPv6 address should @@ -217,7 +226,7 @@ impl fmt::Display for Address { Head, HeadBody, Tail, - TailBody + TailBody, } let mut words = [0u16; 8]; self.write_parts(&mut words); @@ -229,7 +238,7 @@ impl fmt::Display for Address { (0, &State::Head) | (0, &State::HeadBody) => { write!(f, "::")?; State::Tail - }, + } // Continue iterating without writing any characters until // we hit anothing non-zero value. (0, &State::Tail) => State::Tail, @@ -238,11 +247,11 @@ impl fmt::Display for Address { (_, &State::Head) => { write!(f, "{:x}", word)?; State::HeadBody - }, + } (_, &State::Tail) => { write!(f, "{:x}", word)?; State::TailBody - }, + } // Write the u16 with a leading colon when parsing a value // that isn't the first in a section (_, &State::HeadBody) | (_, &State::TailBody) => { @@ -260,8 +269,9 @@ impl fmt::Display for Address { impl From for Address { fn from(address: ipv4::Address) -> Self { let octets = address.0; - Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, - octets[0], octets[1], octets[2], octets[3]]) + Address([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3], + ]) } } @@ -270,7 +280,7 @@ impl From for Address { #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Cidr { - address: Address, + address: Address, prefix_len: u8, } @@ -278,12 +288,13 @@ impl Cidr { /// The [solicited node prefix]. /// /// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1 - pub const SOLICITED_NODE_PREFIX: Cidr = - Cidr { - address: Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00]), - prefix_len: 104 - }; + pub const SOLICITED_NODE_PREFIX: Cidr = Cidr { + address: Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, + 0x00, 0x00, + ]), + prefix_len: 104, + }; /// Create an IPv6 CIDR block from the given address and prefix length. /// @@ -291,7 +302,10 @@ impl Cidr { /// This function panics if the prefix length is larger than 128. pub fn new(address: Address, prefix_len: u8) -> Cidr { assert!(prefix_len <= 128); - Cidr { address, prefix_len } + Cidr { + address, + prefix_len, + } } /// Return the address of this IPv6 CIDR block. @@ -308,7 +322,9 @@ impl Cidr { /// the given address. pub fn contains_addr(&self, addr: &Address) -> bool { // right shift by 128 is not legal - if self.prefix_len == 0 { return true } + if self.prefix_len == 0 { + return true; + } let shift = 128 - self.prefix_len; self.address.mask(shift) == addr.mask(shift) @@ -332,7 +348,7 @@ impl fmt::Display for Cidr { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } // Ranges and constants describing the IPv6 header @@ -367,17 +383,17 @@ mod field { pub const VER_TC_FLOW: Field = 0..4; // 16-bit value representing the length of the payload. // Note: Options are included in this length. - pub const LENGTH: Field = 4..6; + pub const LENGTH: Field = 4..6; // 8-bit value identifying the type of header following this // one. Note: The same numbers are used in IPv4. - pub const NXT_HDR: usize = 6; + pub const NXT_HDR: usize = 6; // 8-bit value decremented by each node that forwards this // packet. The packet is discarded when the value is 0. - pub const HOP_LIMIT: usize = 7; + pub const HOP_LIMIT: usize = 7; // IPv6 address of the source node. - pub const SRC_ADDR: Field = 8..24; + pub const SRC_ADDR: Field = 8..24; // IPv6 address of the destination node. - pub const DST_ADDR: Field = 24..40; + pub const DST_ADDR: Field = 24..40; } /// Length of an IPv6 header. @@ -602,15 +618,15 @@ impl> AsRef<[u8]> for Packet { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { /// IPv6 address of the source node. - pub src_addr: Address, + pub src_addr: Address, /// IPv6 address of the destination node. - pub dst_addr: Address, + pub dst_addr: Address, /// Protocol contained in the next header. pub next_header: Protocol, /// Length of the payload including the extension headers. pub payload_len: usize, /// The 8-bit hop limit field. - pub hop_limit: u8 + pub hop_limit: u8, } impl Repr { @@ -618,13 +634,15 @@ impl Repr { pub fn parse + ?Sized>(packet: &Packet<&T>) -> Result { // Ensure basic accessors will work packet.check_len()?; - if packet.version() != 6 { return Err(Error::Malformed); } + if packet.version() != 6 { + return Err(Error::Malformed); + } Ok(Repr { - src_addr: packet.src_addr(), - dst_addr: packet.dst_addr(), + src_addr: packet.src_addr(), + dst_addr: packet.dst_addr(), next_header: packet.next_header(), payload_len: packet.payload_len() as usize, - hop_limit: packet.hop_limit() + hop_limit: packet.hop_limit(), }) } @@ -651,29 +669,33 @@ impl Repr { impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IPv6 src={} dst={} nxt_hdr={} hop_limit={}", - self.src_addr, self.dst_addr, self.next_header, self.hop_limit) + write!( + f, + "IPv6 src={} dst={} nxt_hdr={} hop_limit={}", + self.src_addr, self.dst_addr, self.next_header, self.hop_limit + ) } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; // TODO: This is very similar to the implementation for IPv4. Make // a way to have less copy and pasted code here. impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { let (ip_repr, payload) = match Packet::new_checked(buffer) { Err(err) => return write!(f, "{}({})", indent, err), - Ok(ip_packet) => { - match Repr::parse(&ip_packet) { - Err(_) => return Ok(()), - Ok(ip_repr) => { - write!(f, "{}{}", indent, ip_repr)?; - (ip_repr, ip_packet.payload()) - } + Ok(ip_packet) => match Repr::parse(&ip_packet) { + Err(_) => return Ok(()), + Ok(ip_repr) => { + write!(f, "{}{}", indent, ip_repr)?; + (ip_repr, ip_packet.payload()) } - } + }, }; pretty_print_ip_payload(f, indent, ip_repr, payload) @@ -682,18 +704,18 @@ impl> PrettyPrint for Packet { #[cfg(test)] mod test { - use crate::Error; use super::{Address, Cidr}; use super::{Packet, Protocol, Repr}; - use crate::wire::pretty_print::{PrettyPrinter}; + use crate::wire::pretty_print::PrettyPrinter; + use crate::Error; #[cfg(feature = "proto-ipv4")] use crate::wire::ipv4::Address as Ipv4Address; - static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01]); + static LINK_LOCAL_ADDR: Address = Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, + ]); #[test] fn test_basic_multicast() { assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified()); @@ -724,48 +746,62 @@ mod test { #[test] fn test_address_format() { - assert_eq!("ff02::1", - format!("{}", Address::LINK_LOCAL_ALL_NODES)); - assert_eq!("fe80::1", - format!("{}", LINK_LOCAL_ADDR)); - assert_eq!("fe80::7f00:0:1", - format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001))); - assert_eq!("::", - format!("{}", Address::UNSPECIFIED)); - assert_eq!("::1", - format!("{}", Address::LOOPBACK)); + assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES)); + assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR)); + assert_eq!( + "fe80::7f00:0:1", + format!( + "{}", + Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001) + ) + ); + assert_eq!("::", format!("{}", Address::UNSPECIFIED)); + assert_eq!("::1", format!("{}", Address::LOOPBACK)); #[cfg(feature = "proto-ipv4")] - assert_eq!("::ffff:192.168.1.1", - format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))); + assert_eq!( + "::ffff:192.168.1.1", + format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1))) + ); } #[test] fn test_new() { - assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1), - Address::LINK_LOCAL_ALL_NODES); - assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2), - Address::LINK_LOCAL_ALL_ROUTERS); - assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), - Address::LOOPBACK); - assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), - Address::UNSPECIFIED); - assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), - LINK_LOCAL_ADDR); + assert_eq!( + Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1), + Address::LINK_LOCAL_ALL_NODES + ); + assert_eq!( + Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2), + Address::LINK_LOCAL_ALL_ROUTERS + ); + assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), Address::LOOPBACK); + assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), Address::UNSPECIFIED); + assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), LINK_LOCAL_ADDR); } #[test] fn test_from_parts() { - assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]), - Address::LINK_LOCAL_ALL_NODES); - assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]), - Address::LINK_LOCAL_ALL_ROUTERS); - assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]), - Address::LOOPBACK); - assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]), - Address::UNSPECIFIED); - assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]), - LINK_LOCAL_ADDR); + assert_eq!( + Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]), + Address::LINK_LOCAL_ALL_NODES + ); + assert_eq!( + Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]), + Address::LINK_LOCAL_ALL_ROUTERS + ); + assert_eq!( + Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]), + Address::LOOPBACK + ); + assert_eq!( + Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]), + Address::UNSPECIFIED + ); + assert_eq!( + Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]), + LINK_LOCAL_ADDR + ); } #[test] @@ -788,18 +824,36 @@ mod test { #[test] fn test_mask() { let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1); - assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!( + addr.mask(11), + [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ); + assert_eq!( + addr.mask(15), + [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ); + assert_eq!( + addr.mask(26), + [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ); + assert_eq!( + addr.mask(128), + [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] + ); + assert_eq!( + addr.mask(127), + [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ); } #[cfg(feature = "proto-ipv4")] #[test] fn test_is_ipv4_mapped() { assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped()); - assert_eq!(true, Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped()); + assert_eq!( + true, + Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped() + ); } #[cfg(feature = "proto-ipv4")] @@ -814,10 +868,14 @@ mod test { #[cfg(feature = "proto-ipv4")] #[test] fn test_from_ipv4_address() { - assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]), - Address::from(Ipv4Address::new(192, 168, 1, 1))); - assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]), - Address::from(Ipv4Address::new(222, 1, 41, 90))); + assert_eq!( + Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]), + Address::from(Ipv4Address::new(192, 168, 1, 1)) + ); + assert_eq!( + Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]), + Address::from(Ipv4Address::new(222, 1, 41, 90)) + ); } #[test] @@ -825,52 +883,96 @@ mod test { let cidr = Cidr::new(LINK_LOCAL_ADDR, 64); let inside_subnet = [ - [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02], - [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], - [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff] + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, + ], + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, + 0x77, 0x88, + ], + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ], + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, + ], ]; let outside_subnet = [ - [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], - [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], - [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], - [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02] + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ], + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ], + [ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ], + [ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, + ], ]; let subnets = [ - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - 65), - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], - 128), - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78], - 96) + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ], + 65, + ), + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, + ], + 128, + ), + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x34, 0x56, 0x78, + ], + 96, + ), ]; let not_subnets = [ - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - 63), - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - 64), - ([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - 65), - ([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], - 128) + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ], + 63, + ), + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ], + 64, + ), + ( + [ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ], + 65, + ), + ( + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, + ], + 128, + ), ]; for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) { @@ -881,13 +983,11 @@ mod test { assert!(!cidr.contains_addr(&addr)); } - for subnet in subnets.iter().map( - |&(a, p)| Cidr::new(Address(a), p)) { + for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) { assert!(cidr.contains_subnet(&subnet)); } - for subnet in not_subnets.iter().map( - |&(a, p)| Cidr::new(Address(a), p)) { + for subnet in not_subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) { assert!(!cidr.contains_subnet(&subnet)); } @@ -907,33 +1007,26 @@ mod test { let _ = Address::from_parts(&[0u16; 7]); } - static REPR_PACKET_BYTES: [u8; 52] = [0x60, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x11, 0x40, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x02, - 0x00, 0x0c, 0x02, 0x4e, - 0xff, 0xff, 0xff, 0xff]; - static REPR_PAYLOAD_BYTES: [u8; 12] = [0x00, 0x01, 0x00, 0x02, - 0x00, 0x0c, 0x02, 0x4e, - 0xff, 0xff, 0xff, 0xff]; + static REPR_PACKET_BYTES: [u8; 52] = [ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff, + ]; + static REPR_PAYLOAD_BYTES: [u8; 12] = [ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff, + ]; fn packet_repr() -> Repr { Repr { - src_addr: Address([0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01]), - dst_addr: Address::LINK_LOCAL_ALL_NODES, + src_addr: Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ]), + dst_addr: Address::LINK_LOCAL_ALL_NODES, next_header: Protocol::Udp, payload_len: 12, - hop_limit: 64 + hop_limit: 64, } } @@ -948,10 +1041,13 @@ mod test { assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len()); assert_eq!(packet.next_header(), Protocol::Udp); assert_eq!(packet.hop_limit(), 0x40); - assert_eq!(packet.src_addr(), Address([0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01])); + assert_eq!( + packet.src_addr(), + Address([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01 + ]) + ); assert_eq!(packet.dst_addr(), Address::LINK_LOCAL_ALL_NODES); assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]); } @@ -976,15 +1072,14 @@ mod test { packet.set_hop_limit(0xfe); packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS); packet.set_dst_addr(Address::LINK_LOCAL_ALL_NODES); - packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES[..]); + packet + .payload_mut() + .copy_from_slice(&REPR_PAYLOAD_BYTES[..]); let mut expected_bytes = [ - 0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len(); expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]); @@ -998,10 +1093,14 @@ mod test { bytes.extend(&REPR_PACKET_BYTES[..]); bytes.push(0); - assert_eq!(Packet::new_unchecked(&bytes).payload().len(), - REPR_PAYLOAD_BYTES.len()); - assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(), - REPR_PAYLOAD_BYTES.len()); + assert_eq!( + Packet::new_unchecked(&bytes).payload().len(), + REPR_PAYLOAD_BYTES.len() + ); + assert_eq!( + Packet::new_unchecked(&mut bytes).payload_mut().len(), + REPR_PAYLOAD_BYTES.len() + ); } #[test] @@ -1010,8 +1109,7 @@ mod test { bytes.extend(&REPR_PACKET_BYTES[..]); Packet::new_unchecked(&mut bytes).set_payload_len(0x80); - assert_eq!(Packet::new_checked(&bytes).unwrap_err(), - Error::Truncated); + assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated); } #[test] @@ -1063,7 +1161,12 @@ mod test { #[test] fn test_pretty_print() { - assert_eq!(format!("{}", PrettyPrinter::>::new("\n", &&REPR_PACKET_BYTES[..])), - "\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"); + assert_eq!( + format!( + "{}", + PrettyPrinter::>::new("\n", &&REPR_PACKET_BYTES[..]) + ), + "\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4" + ); } } diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index ffa393617..36ccdfe77 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -1,5 +1,5 @@ -use core::fmt; use crate::{Error, Result}; +use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; @@ -9,7 +9,7 @@ pub use super::IpProtocol as Protocol; #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { - buffer: T + buffer: T, } // Format of the Fragment Header @@ -25,13 +25,13 @@ mod field { use crate::wire::field::*; // 8-bit identifier of the header immediately following this header. - pub const NXT_HDR: usize = 0; + pub const NXT_HDR: usize = 0; // 8-bit reserved field. - pub const RESERVED: usize = 1; + pub const RESERVED: usize = 1; // 16-bit field containing the fragment offset, reserved and more fragments values. - pub const FR_OF_M: Field = 2..4; + pub const FR_OF_M: Field = 2..4; // 32-bit field identifying the fragmented packet - pub const IDENT: Field = 4..8; + pub const IDENT: Field = 4..8; } impl> Header { @@ -170,17 +170,19 @@ pub struct Repr { pub more_frags: bool, /// The identification for every packet that is fragmented. pub ident: u32, - } impl Repr { /// Parse an IPv6 Fragment Header and return a high-level representation. - pub fn parse(header: &Header<&T>) -> Result where T: AsRef<[u8]> + ?Sized { + pub fn parse(header: &Header<&T>) -> Result + where + T: AsRef<[u8]> + ?Sized, + { Ok(Repr { next_header: header.next_header(), frag_offset: header.frag_offset(), more_frags: header.more_frags(), - ident: header.ident() + ident: header.ident(), }) } @@ -202,8 +204,11 @@ impl Repr { impl<'a> fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}", - self.next_header, self.frag_offset, self.more_frags, self.ident) + write!( + f, + "IPv6 Fragment next_hdr={} offset={} more={} ident={}", + self.next_header, self.frag_offset, self.more_frags, self.ident + ) } } @@ -212,21 +217,23 @@ mod test { use super::*; // A Fragment Header with more fragments remaining - static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, - 0x0, 0x0, 0x30, 0x39]; + static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39]; // A Fragment Header with no more fragments remaining - static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, - 0x0, 0x1, 0x9, 0x32]; + static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32]; #[test] fn test_check_len() { // less than 8 bytes - assert_eq!(Err(Error::Truncated), - Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len() + ); // valid - assert_eq!(Ok(()), - Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()); + assert_eq!( + Ok(()), + Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len() + ); } #[test] @@ -248,24 +255,48 @@ mod test { fn test_repr_parse_valid() { let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG); let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr, - Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 }); + assert_eq!( + repr, + Repr { + next_header: Protocol::Tcp, + frag_offset: 0, + more_frags: true, + ident: 12345 + } + ); let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG); let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr, - Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 }); + assert_eq!( + repr, + Repr { + next_header: Protocol::Tcp, + frag_offset: 320, + more_frags: false, + ident: 67890 + } + ); } #[test] fn test_repr_emit() { - let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 }; + let repr = Repr { + next_header: Protocol::Tcp, + frag_offset: 0, + more_frags: true, + ident: 12345, + }; let mut bytes = [0u8; 8]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]); - let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 }; + let repr = Repr { + next_header: Protocol::Tcp, + frag_offset: 320, + more_frags: false, + ident: 67890, + }; let mut bytes = [0u8; 8]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index dde76aef4..97b868553 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -1,14 +1,14 @@ -use core::fmt; use crate::{Error, Result}; +use core::fmt; -use crate::wire::ipv6option::Ipv6OptionsIterator; pub use super::IpProtocol as Protocol; +use crate::wire::ipv6option::Ipv6OptionsIterator; /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { - buffer: T + buffer: T, } // Format of the Hop-by-Hop Options Header @@ -31,13 +31,13 @@ mod field { use crate::wire::field::*; // Minimum size of the header. - pub const MIN_HEADER_SIZE: usize = 8; + pub const MIN_HEADER_SIZE: usize = 8; // 8-bit identifier of the header immediately following this header. - pub const NXT_HDR: usize = 0; + pub const NXT_HDR: usize = 0; // 8-bit unsigned integer. Length of the OPTIONS field in 8-octet units, // not including the first 8 octets. - pub const LENGTH: usize = 1; + pub const LENGTH: usize = 1; // Variable-length field. Option-Type-specific data. // // Length of the header is in 8-octet units, not including the first 8 octets. The first two @@ -111,7 +111,7 @@ impl> Header { impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> { /// Return the option data. #[inline] - pub fn options(&self) -> &'a[u8] { + pub fn options(&self) -> &'a [u8] { let data = self.buffer.as_ref(); &data[field::OPTIONS(data[field::LENGTH])] } @@ -163,18 +163,21 @@ pub struct Repr<'a> { /// The type of header immediately following the Hop-by-Hop Options header. pub next_header: Protocol, /// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets. - pub length: u8, + pub length: u8, /// The options contained in the Hop-by-Hop Options header. - pub options: &'a [u8] + pub options: &'a [u8], } impl<'a> Repr<'a> { /// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation. - pub fn parse(header: &Header<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized { + pub fn parse(header: &Header<&'a T>) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { Ok(Repr { next_header: header.next_header(), length: header.header_len(), - options: header.options() + options: header.options(), }) } @@ -199,7 +202,11 @@ impl<'a> Repr<'a> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IPv6 Hop-by-Hop Options next_hdr={} length={} ", self.next_header, self.length) + write!( + f, + "IPv6 Hop-by-Hop Options next_hdr={} length={} ", + self.next_header, self.length + ) } } @@ -208,36 +215,43 @@ mod test { use super::*; // A Hop-by-Hop Option header with a PadN option of option data length 4. - static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, - 0x0, 0x0, 0x0, 0x0]; + static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0x0]; // A Hop-by-Hop Option header with a PadN option of option data length 12. - static REPR_PACKET_PAD12: [u8; 16] = [0x06, 0x1, 0x1, 0x12, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0]; + static REPR_PACKET_PAD12: [u8; 16] = [ + 0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ]; #[test] fn test_check_len() { // zero byte buffer - assert_eq!(Err(Error::Truncated), - Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len() + ); // no length field - assert_eq!(Err(Error::Truncated), - Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len() + ); // less than 8 bytes - assert_eq!(Err(Error::Truncated), - Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len() + ); // valid - assert_eq!(Ok(()), - Header::new_unchecked(&REPR_PACKET_PAD4).check_len()); + assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len()); // valid - assert_eq!(Ok(()), - Header::new_unchecked(&REPR_PACKET_PAD12).check_len()); + assert_eq!( + Ok(()), + Header::new_unchecked(&REPR_PACKET_PAD12).check_len() + ); // length field value greater than number of bytes let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; - assert_eq!(Err(Error::Truncated), - Header::new_unchecked(&header).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new_unchecked(&header).check_len() + ); } #[test] @@ -259,19 +273,27 @@ mod test { bytes.extend(&REPR_PACKET_PAD4[..]); bytes.push(0); - assert_eq!(Header::new_unchecked(&bytes).options().len(), - REPR_PACKET_PAD4[2..].len()); - assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(), - REPR_PACKET_PAD4[2..].len()); + assert_eq!( + Header::new_unchecked(&bytes).options().len(), + REPR_PACKET_PAD4[2..].len() + ); + assert_eq!( + Header::new_unchecked(&mut bytes).options_mut().len(), + REPR_PACKET_PAD4[2..].len() + ); let mut bytes = vec![]; bytes.extend(&REPR_PACKET_PAD12[..]); bytes.push(0); - assert_eq!(Header::new_unchecked(&bytes).options().len(), - REPR_PACKET_PAD12[2..].len()); - assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(), - REPR_PACKET_PAD12[2..].len()); + assert_eq!( + Header::new_unchecked(&bytes).options().len(), + REPR_PACKET_PAD12[2..].len() + ); + assert_eq!( + Header::new_unchecked(&mut bytes).options_mut().len(), + REPR_PACKET_PAD12[2..].len() + ); } #[test] @@ -295,26 +317,44 @@ mod test { fn test_repr_parse_valid() { let header = Header::new_unchecked(&REPR_PACKET_PAD4); let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr, Repr { - next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] - }); + assert_eq!( + repr, + Repr { + next_header: Protocol::Tcp, + length: 0, + options: &REPR_PACKET_PAD4[2..] + } + ); let header = Header::new_unchecked(&REPR_PACKET_PAD12); let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr, Repr { - next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] - }); + assert_eq!( + repr, + Repr { + next_header: Protocol::Tcp, + length: 1, + options: &REPR_PACKET_PAD12[2..] + } + ); } #[test] fn test_repr_emit() { - let repr = Repr{ next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] }; + let repr = Repr { + next_header: Protocol::Tcp, + length: 0, + options: &REPR_PACKET_PAD4[2..], + }; let mut bytes = [0u8; 8]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]); - let repr = Repr{ next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] }; + let repr = Repr { + next_header: Protocol::Tcp, + length: 1, + options: &REPR_PACKET_PAD12[2..], + }; let mut bytes = [0u8; 16]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index adab8f42b..e22a8aad7 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -1,5 +1,5 @@ -use core::fmt; use crate::{Error, Result}; +use core::fmt; enum_with_unknown! { /// IPv6 Extension Header Option Type @@ -14,9 +14,9 @@ enum_with_unknown! { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Type::Pad1 => write!(f, "Pad1"), - Type::PadN => write!(f, "PadN"), - Type::Unknown(id) => write!(f, "{}", id) + Type::Pad1 => write!(f, "Pad1"), + Type::PadN => write!(f, "PadN"), + Type::Unknown(id) => write!(f, "{}", id), } } } @@ -40,11 +40,11 @@ enum_with_unknown! { impl fmt::Display for FailureType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - FailureType::Skip => write!(f, "skip"), - FailureType::Discard => write!(f, "discard"), - FailureType::DiscardSendAll => write!(f, "discard and send error"), + FailureType::Skip => write!(f, "skip"), + FailureType::Discard => write!(f, "discard"), + FailureType::DiscardSendAll => write!(f, "discard and send error"), FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"), - FailureType::Unknown(id) => write!(f, "Unknown({})", id), + FailureType::Unknown(id) => write!(f, "Unknown({})", id), } } } @@ -60,7 +60,7 @@ impl From for FailureType { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv6Option> { - buffer: T + buffer: T, } // Format of Option @@ -77,9 +77,9 @@ mod field { use crate::wire::field::*; // 8-bit identifier of the type of option. - pub const TYPE: usize = 0; + pub const TYPE: usize = 0; // 8-bit unsigned integer. Length of the DATA field of this option, in octets. - pub const LENGTH: usize = 1; + pub const LENGTH: usize = 1; // Variable-length field. Option-Type-specific data. pub fn DATA(length: u8) -> Field { 2..length as usize + 2 @@ -221,27 +221,26 @@ pub enum Repr<'a> { Pad1, PadN(u8), Unknown { - type_: Type, + type_: Type, length: u8, - data: &'a [u8] + data: &'a [u8], }, } impl<'a> Repr<'a> { /// Parse an IPv6 Extension Header Option and return a high-level representation. - pub fn parse(opt: &Ipv6Option<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized { + pub fn parse(opt: &Ipv6Option<&'a T>) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { match opt.option_type() { - Type::Pad1 => - Ok(Repr::Pad1), - Type::PadN => - Ok(Repr::PadN(opt.data_len())), - unknown_type @ Type::Unknown(_) => { - Ok(Repr::Unknown { - type_: unknown_type, - length: opt.data_len(), - data: opt.data(), - }) - } + Type::Pad1 => Ok(Repr::Pad1), + Type::PadN => Ok(Repr::PadN(opt.data_len())), + unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown { + type_: unknown_type, + length: opt.data_len(), + data: opt.data(), + }), } } @@ -249,18 +248,15 @@ impl<'a> Repr<'a> { pub fn buffer_len(&self) -> usize { match *self { Repr::Pad1 => 1, - Repr::PadN(length) => - field::DATA(length).end, - Repr::Unknown{ length, .. } => - field::DATA(length).end, + Repr::PadN(length) => field::DATA(length).end, + Repr::Unknown { length, .. } => field::DATA(length).end, } } /// Emit a high-level representation into an IPv6 Extension Header Option. pub fn emit + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) { match *self { - Repr::Pad1 => - opt.set_option_type(Type::Pad1), + Repr::Pad1 => opt.set_option_type(Type::Pad1), Repr::PadN(len) => { opt.set_option_type(Type::PadN); opt.set_data_len(len); @@ -269,7 +265,11 @@ impl<'a> Repr<'a> { *x = 0 } } - Repr::Unknown{ type_, length, data } => { + Repr::Unknown { + type_, + length, + data, + } => { opt.set_option_type(type_); opt.set_data_len(length); opt.data_mut().copy_from_slice(&data[..length as usize]); @@ -285,7 +285,7 @@ pub struct Ipv6OptionsIterator<'a> { pos: usize, length: usize, data: &'a [u8], - hit_error: bool + hit_error: bool, } impl<'a> Ipv6OptionsIterator<'a> { @@ -301,7 +301,8 @@ impl<'a> Ipv6OptionsIterator<'a> { Ipv6OptionsIterator { pos: 0, hit_error: false, - length, data + length, + data, } } } @@ -314,18 +315,16 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> { // If we still have data to parse and we have not previously // hit an error, attempt to parse the next option. match Ipv6Option::new_checked(&self.data[self.pos..]) { - Ok(hdr) => { - match Repr::parse(&hdr) { - Ok(repr) => { - self.pos += repr.buffer_len(); - Some(Ok(repr)) - } - Err(e) => { - self.hit_error = true; - Some(Err(e)) - } + Ok(hdr) => match Repr::parse(&hdr) { + Ok(repr) => { + self.pos += repr.buffer_len(); + Some(Ok(repr)) } - } + Err(e) => { + self.hit_error = true; + Some(Err(e)) + } + }, Err(e) => { self.hit_error = true; Some(Err(e)) @@ -343,12 +342,9 @@ impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IPv6 Option ")?; match *self { - Repr::Pad1 => - write!(f, "{} ", Type::Pad1), - Repr::PadN(len) => - write!(f, "{} length={} ", Type::PadN, len), - Repr::Unknown{ type_, length, .. } => - write!(f, "{} length={} ", type_, length), + Repr::Pad1 => write!(f, "{} ", Type::Pad1), + Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len), + Repr::Unknown { type_, length, .. } => write!(f, "{} length={} ", type_, length), } } } @@ -357,35 +353,49 @@ impl<'a> fmt::Display for Repr<'a> { mod test { use super::*; - static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0]; - static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0]; + static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0]; + static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0]; static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0]; #[test] fn test_check_len() { let bytes = [0u8]; // zero byte buffer - assert_eq!(Err(Error::Truncated), - Ipv6Option::new_unchecked(&bytes[..0]).check_len()); + assert_eq!( + Err(Error::Truncated), + Ipv6Option::new_unchecked(&bytes[..0]).check_len() + ); // pad1 - assert_eq!(Ok(()), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()); + assert_eq!( + Ok(()), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len() + ); // padn with truncated data - assert_eq!(Err(Error::Truncated), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()); + assert_eq!( + Err(Error::Truncated), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len() + ); // padn - assert_eq!(Ok(()), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()); + assert_eq!( + Ok(()), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len() + ); // unknown option type with truncated data - assert_eq!(Err(Error::Truncated), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()); - assert_eq!(Err(Error::Truncated), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()); + assert_eq!( + Err(Error::Truncated), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len() + ); + assert_eq!( + Err(Error::Truncated), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len() + ); // unknown type - assert_eq!(Ok(()), - Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()); + assert_eq!( + Ok(()), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len() + ); } #[test] @@ -402,7 +412,7 @@ mod test { assert_eq!(opt.option_type(), Type::Pad1); // two octets of padding - let bytes: [u8; 2] = [0x1, 0x0]; + let bytes: [u8; 2] = [0x1, 0x0]; let opt = Ipv6Option::new_unchecked(&bytes); assert_eq!(opt.option_type(), Type::PadN); assert_eq!(opt.data_len(), 0); @@ -414,14 +424,14 @@ mod test { assert_eq!(opt.data(), &[0]); // extra bytes in buffer - let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff]; + let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff]; let opt = Ipv6Option::new_unchecked(&bytes); assert_eq!(opt.option_type(), Type::PadN); assert_eq!(opt.data_len(), 7); assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]); // unrecognized option - let bytes: [u8; 1] = [0xff]; + let bytes: [u8; 1] = [0xff]; let opt = Ipv6Option::new_unchecked(&bytes); assert_eq!(opt.option_type(), Type::Unknown(255)); @@ -447,7 +457,14 @@ mod test { let data = [0u8; 3]; let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN); let unknown = Repr::parse(&opt).unwrap(); - assert_eq!(unknown, Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data }); + assert_eq!( + unknown, + Repr::Unknown { + type_: Type::Unknown(255), + length: 3, + data: &data + } + ); } #[test] @@ -465,7 +482,11 @@ mod test { assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN); let data = [0u8; 3]; - let repr = Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data }; + let repr = Repr::Unknown { + type_: Type::Unknown(255), + length: 3, + data: &data, + }; let mut bytes = [254u8; 5]; // don't assume bytes are initialized to zero let mut opt = Ipv6Option::new_unchecked(&mut bytes); repr.emit(&mut opt); @@ -488,10 +509,10 @@ mod test { #[test] fn test_options_iter() { - let options = [0x00, 0x01, 0x01, 0x00, - 0x01, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x11, - 0x00, 0x01, 0x08, 0x00]; + let options = [ + 0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01, + 0x08, 0x00, + ]; let mut iterator = Ipv6OptionsIterator::new(&options, 0); assert_eq!(iterator.next(), None); @@ -504,8 +525,14 @@ mod test { (2, Ok(Repr::PadN(2))) => continue, (3, Ok(Repr::PadN(0))) => continue, (4, Ok(Repr::Pad1)) => continue, - (5, Ok(Repr::Unknown { type_: Type::Unknown(0x11), length: 0, .. })) => - continue, + ( + 5, + Ok(Repr::Unknown { + type_: Type::Unknown(0x11), + length: 0, + .. + }), + ) => continue, (6, Err(Error::Truncated)) => continue, (i, res) => panic!("Unexpected option `{:?}` at index {}", res, i), } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index a00e44bc7..fdb155ada 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -1,5 +1,5 @@ -use core::fmt; use crate::{Error, Result}; +use core::fmt; use crate::wire::IpProtocol as Protocol; use crate::wire::Ipv6Address as Address; @@ -37,14 +37,14 @@ enum_with_unknown! { impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Type::Type0 => write!(f, "Type0"), - Type::Nimrod => write!(f, "Nimrod"), - Type::Type2 => write!(f, "Type2"), - Type::Rpl => write!(f, "Rpl"), - Type::Experiment1 => write!(f, "Experiment1"), - Type::Experiment2 => write!(f, "Experiment2"), - Type::Reserved => write!(f, "Reserved"), - Type::Unknown(id) => write!(f, "{}", id) + Type::Type0 => write!(f, "Type0"), + Type::Nimrod => write!(f, "Nimrod"), + Type::Type2 => write!(f, "Type2"), + Type::Rpl => write!(f, "Rpl"), + Type::Experiment1 => write!(f, "Experiment1"), + Type::Experiment2 => write!(f, "Experiment2"), + Type::Reserved => write!(f, "Reserved"), + Type::Unknown(id) => write!(f, "{}", id), } } } @@ -53,7 +53,7 @@ impl fmt::Display for Type { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { - buffer: T + buffer: T, } // Format of the Routing Header @@ -76,15 +76,15 @@ mod field { use crate::wire::field::*; // Minimum size of the header. - pub const MIN_HEADER_SIZE: usize = 4; + pub const MIN_HEADER_SIZE: usize = 4; // 8-bit identifier of the header immediately following this header. - pub const NXT_HDR: usize = 0; + pub const NXT_HDR: usize = 0; // 8-bit unsigned integer. Length of the DATA field in 8-octet units, // not including the first 8 octets. - pub const LENGTH: usize = 1; + pub const LENGTH: usize = 1; // 8-bit identifier of a particular Routing header variant. - pub const TYPE: usize = 2; + pub const TYPE: usize = 2; // 8-bit unsigned integer. The number of route segments remaining. pub const SEG_LEFT: usize = 3; // Variable-length field. Routing-Type-specific data. @@ -130,9 +130,9 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 8-bit field containing the CmprI and CmprE values. - pub const CMPR: usize = 4; + pub const CMPR: usize = 4; // 8-bit field containing the Pad value. - pub const PAD: usize = 5; + pub const PAD: usize = 5; // Variable length field containing addresses pub fn ADDRESSES(length_field: u8) -> Field { let data = DATA(length_field); @@ -234,7 +234,6 @@ impl> Header { data[field::CMPR] >> 4 } - /// Return the number of prefix octects elided from the last address (`addresses[n]`). /// /// # Panics @@ -316,7 +315,7 @@ impl + AsMut<[u8]>> Header { data[7] = 0; } - _ => panic!("Unrecognized routing type when clearing reserved fields.") + _ => panic!("Unrecognized routing type when clearing reserved fields."), } } } @@ -395,59 +394,57 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { pub enum Repr<'a> { Type2 { /// The type of header immediately following the Routing header. - next_header: Protocol, + next_header: Protocol, /// Length of the Routing header in 8-octet units, not including the first 8 octets. - length: u8, + length: u8, /// Number of route segments remaining. - segments_left: u8, + segments_left: u8, /// The home address of the destination mobile node. home_address: Address, }, Rpl { /// The type of header immediately following the Routing header. - next_header: Protocol, + next_header: Protocol, /// Length of the Routing header in 8-octet units, not including the first 8 octets. - length: u8, + length: u8, /// Number of route segments remaining. - segments_left: u8, + segments_left: u8, /// Number of prefix octets from each segment, except the last segment, that are elided. - cmpr_i: u8, + cmpr_i: u8, /// Number of prefix octets from the last segment that are elided. - cmpr_e: u8, + cmpr_e: u8, /// Number of octets that are used for padding after `address[n]` at the end of the /// RPL Source Route Header. - pad: u8, + pad: u8, /// Vector of addresses, numbered 1 to `n`. - addresses: &'a[u8], + addresses: &'a [u8], }, } - impl<'a> Repr<'a> { /// Parse an IPv6 Routing Header and return a high-level representation. - pub fn parse(header: &'a Header<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized { + pub fn parse(header: &'a Header<&'a T>) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { match header.routing_type() { - Type::Type2 => { - Ok(Repr::Type2 { - next_header: header.next_header(), - length: header.header_len(), - segments_left: header.segments_left(), - home_address: header.home_address(), - }) - } - Type::Rpl => { - Ok(Repr::Rpl { - next_header: header.next_header(), - length: header.header_len(), - segments_left: header.segments_left(), - cmpr_i: header.cmpr_i(), - cmpr_e: header.cmpr_e(), - pad: header.pad(), - addresses: header.addresses(), - }) - } - - _ => Err(Error::Unrecognized) + Type::Type2 => Ok(Repr::Type2 { + next_header: header.next_header(), + length: header.header_len(), + segments_left: header.segments_left(), + home_address: header.home_address(), + }), + Type::Rpl => Ok(Repr::Rpl { + next_header: header.next_header(), + length: header.header_len(), + segments_left: header.segments_left(), + cmpr_i: header.cmpr_i(), + cmpr_e: header.cmpr_e(), + pad: header.pad(), + addresses: header.addresses(), + }), + + _ => Err(Error::Unrecognized), } } @@ -455,16 +452,19 @@ impl<'a> Repr<'a> { /// representation. pub fn buffer_len(&self) -> usize { match self { - &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => { - field::DATA(length).end - } + &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end, } } /// Emit a high-level representation into an IPv6 Routing Header. pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { match *self { - Repr::Type2 { next_header, length, segments_left, home_address } => { + Repr::Type2 { + next_header, + length, + segments_left, + home_address, + } => { header.set_next_header(next_header); header.set_header_len(length); header.set_routing_type(Type::Type2); @@ -472,7 +472,15 @@ impl<'a> Repr<'a> { header.clear_reserved(); header.set_home_address(home_address); } - Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => { + Repr::Rpl { + next_header, + length, + segments_left, + cmpr_i, + cmpr_e, + pad, + addresses, + } => { header.set_next_header(next_header); header.set_header_len(length); header.set_routing_type(Type::Rpl); @@ -490,11 +498,31 @@ impl<'a> Repr<'a> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Repr::Type2 { next_header, length, segments_left, home_address } => { - write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", - next_header, length, Type::Type2, segments_left, home_address) + Repr::Type2 { + next_header, + length, + segments_left, + home_address, + } => { + write!( + f, + "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", + next_header, + length, + Type::Type2, + segments_left, + home_address + ) } - Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => { + Repr::Rpl { + next_header, + length, + segments_left, + cmpr_i, + cmpr_e, + pad, + .. + } => { write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) } @@ -507,12 +535,10 @@ mod test { use super::*; // A Type 2 Routing Header - static BYTES_TYPE2: [u8; 24] = [0x6, 0x2, 0x2, 0x1, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x1]; + static BYTES_TYPE2: [u8; 24] = [ + 0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + ]; // A representation of a Type 2 Routing header static REPR_TYPE2: Repr = Repr::Type2 { @@ -523,16 +549,11 @@ mod test { }; // A Source Routing Header with full IPv6 addresses in bytes - static BYTES_SRH_FULL: [u8; 40] = [0x6, 0x4, 0x3, 0x2, - 0x0, 0x0, 0x0, 0x0, - 0xfd, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, - 0xfd, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x3, 0x1]; + static BYTES_SRH_FULL: [u8; 40] = [ + 0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x1, + ]; // A representation of a Source Routing Header with full IPv6 addresses static REPR_SRH_FULL: Repr = Repr::Rpl { @@ -542,21 +563,16 @@ mod test { cmpr_i: 0, cmpr_e: 0, pad: 0, - addresses: &[0xfd, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x2, - 0xfd, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x3, 0x1] + addresses: &[ + 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, + ], }; // A Source Routing Header with elided IPv6 addresses in bytes - static BYTES_SRH_ELIDED: [u8; 16] = [0x6, 0x1, 0x3, 0x2, - 0xfe, 0x50, 0x0, 0x0, - 0x2, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0]; + static BYTES_SRH_ELIDED: [u8; 16] = [ + 0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, + ]; // A representation of a Source Routing Header with elided IPv6 addresses static REPR_SRH_ELIDED: Repr = Repr::Rpl { @@ -566,20 +582,37 @@ mod test { cmpr_i: 15, cmpr_e: 14, pad: 5, - addresses: &[0x2, 0x3, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0] + addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0], }; #[test] fn test_check_len() { // less than min header size - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..3]).check_len()); - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..3]).check_len()); - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..3]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_TYPE2[..3]).check_len() + ); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_SRH_FULL[..3]).check_len() + ); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_SRH_ELIDED[..3]).check_len() + ); // less than specfied length field - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..23]).check_len()); - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..39]).check_len()); - assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..11]).check_len()); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_TYPE2[..23]).check_len() + ); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_SRH_FULL[..39]).check_len() + ); + assert_eq!( + Err(Error::Truncated), + Header::new(&BYTES_SRH_ELIDED[..11]).check_len() + ); // valid assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len()); assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len()); diff --git a/src/wire/mld.rs b/src/wire/mld.rs index eb2a32937..a8590924d 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -6,9 +6,9 @@ use byteorder::{ByteOrder, NetworkEndian}; -use crate::{Error, Result}; use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::Ipv6Address; +use crate::{Error, Result}; enum_with_unknown! { /// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for @@ -168,7 +168,7 @@ impl + AsMut<[u8]>> Packet { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AddressRecord> { - buffer: T + buffer: T, } impl> AddressRecord { @@ -305,58 +305,61 @@ pub enum Repr<'a> { qrv: u8, qqic: u8, num_srcs: u16, - data: &'a [u8] + data: &'a [u8], }, Report { nr_mcast_addr_rcrds: u16, - data: &'a [u8] - } + data: &'a [u8], + }, } impl<'a> Repr<'a> { /// Parse an MLDv2 packet and return a high-level representation. pub fn parse(packet: &Packet<&'a T>) -> Result> - where T: AsRef<[u8]> + ?Sized { + where + T: AsRef<[u8]> + ?Sized, + { match packet.msg_type() { - Message::MldQuery => { - Ok(Repr::Query { - max_resp_code: packet.max_resp_code(), - mcast_addr: packet.mcast_addr(), - s_flag: packet.s_flag(), - qrv: packet.qrv(), - qqic: packet.qqic(), - num_srcs: packet.num_srcs(), - data: packet.payload() - }) - }, - Message::MldReport => { - Ok(Repr::Report { - nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(), - data: packet.payload() - }) - }, - _ => Err(Error::Unrecognized) + Message::MldQuery => Ok(Repr::Query { + max_resp_code: packet.max_resp_code(), + mcast_addr: packet.mcast_addr(), + s_flag: packet.s_flag(), + qrv: packet.qrv(), + qqic: packet.qqic(), + num_srcs: packet.num_srcs(), + data: packet.payload(), + }), + Message::MldReport => Ok(Repr::Report { + nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(), + data: packet.payload(), + }), + _ => Err(Error::Unrecognized), } } /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { match self { - Repr::Query { .. } => { - field::QUERY_NUM_SRCS.end - } - Repr::Report { .. } => { - field::NR_MCAST_RCRDS.end - } + Repr::Query { .. } => field::QUERY_NUM_SRCS.end, + Repr::Report { .. } => field::NR_MCAST_RCRDS.end, } } /// Emit a high-level representation into an MLDv2 packet. pub fn emit(&self, packet: &mut Packet<&mut T>) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { match self { - Repr::Query { max_resp_code, mcast_addr, s_flag, - qrv, qqic, num_srcs, data } => { + Repr::Query { + max_resp_code, + mcast_addr, + s_flag, + qrv, + qqic, + num_srcs, + data, + } => { packet.set_msg_type(Message::MldQuery); packet.set_msg_code(0); packet.clear_reserved(); @@ -371,8 +374,11 @@ impl<'a> Repr<'a> { packet.set_qqic(*qqic); packet.set_num_srcs(*num_srcs); packet.payload_mut().copy_from_slice(&data[..]); - }, - Repr::Report { nr_mcast_addr_rcrds, data } => { + } + Repr::Report { + nr_mcast_addr_rcrds, + data, + } => { packet.set_msg_type(Message::MldReport); packet.set_msg_code(0); packet.clear_reserved(); @@ -385,74 +391,49 @@ impl<'a> Repr<'a> { #[cfg(test)] mod test { + use super::*; use crate::phy::ChecksumCapabilities; - use crate::wire::Icmpv6Repr; use crate::wire::icmpv6::Message; - use super::*; - - static QUERY_PACKET_BYTES: [u8; 44] = - [0x82, 0x00, 0x73, 0x74, - 0x04, 0x00, 0x00, 0x00, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0x0a, 0x12, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02]; - - static QUERY_PACKET_PAYLOAD: [u8; 16] = - [0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02]; - - static REPORT_PACKET_BYTES: [u8; 44] = - [0x8f, 0x00, 0x73, 0x85, - 0x00, 0x00, 0x00, 0x01, - 0x01, 0x00, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02]; - - static REPORT_PACKET_PAYLOAD: [u8; 36] = - [0x01, 0x00, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - 0xff, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02]; + use crate::wire::Icmpv6Repr; + static QUERY_PACKET_BYTES: [u8; 44] = [ + 0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + + static QUERY_PACKET_PAYLOAD: [u8; 16] = [ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, + ]; + + static REPORT_PACKET_BYTES: [u8; 44] = [ + 0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; + + static REPORT_PACKET_PAYLOAD: [u8; 36] = [ + 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + ]; fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> { match ty { - Message::MldQuery => { - Icmpv6Repr::Mld(Repr::Query { - max_resp_code: 0x400, - mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, - s_flag: true, - qrv: 0x02, - qqic: 0x12, - num_srcs: 0x01, - data: &QUERY_PACKET_PAYLOAD - }) - }, - Message::MldReport => { - Icmpv6Repr::Mld(Repr::Report { - nr_mcast_addr_rcrds: 1, - data: &REPORT_PACKET_PAYLOAD - }) - }, + Message::MldQuery => Icmpv6Repr::Mld(Repr::Query { + max_resp_code: 0x400, + mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + s_flag: true, + qrv: 0x02, + qqic: 0x12, + num_srcs: 0x01, + data: &QUERY_PACKET_PAYLOAD, + }), + Message::MldReport => Icmpv6Repr::Mld(Repr::Report { + nr_mcast_addr_rcrds: 1, + data: &REPORT_PACKET_PAYLOAD, + }), _ => { panic!("Message type must be a MLDv2 message type"); } @@ -471,8 +452,10 @@ mod test { assert_eq!(packet.qrv(), 0x02); assert_eq!(packet.qqic(), 0x12); assert_eq!(packet.num_srcs(), 0x01); - assert_eq!(Ipv6Address::from_bytes(packet.payload()), - Ipv6Address::LINK_LOCAL_ALL_ROUTERS); + assert_eq!( + Ipv6Address::from_bytes(packet.payload()), + Ipv6Address::LINK_LOCAL_ALL_ROUTERS + ); } #[test] @@ -487,10 +470,14 @@ mod test { packet.set_qrv(0x02); packet.set_qqic(0x12); packet.set_num_srcs(0x01); - packet.payload_mut().copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes()); + packet + .payload_mut() + .copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes()); packet.clear_reserved(); - packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into()); + packet.fill_checksum( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + ); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); } @@ -506,8 +493,10 @@ mod test { assert_eq!(addr_rcrd.aux_data_len(), 0x00); assert_eq!(addr_rcrd.num_srcs(), 0x01); assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES); - assert_eq!(Ipv6Address::from_bytes(addr_rcrd.payload()), - Ipv6Address::LINK_LOCAL_ALL_ROUTERS); + assert_eq!( + Ipv6Address::from_bytes(addr_rcrd.payload()), + Ipv6Address::LINK_LOCAL_ALL_ROUTERS + ); } #[test] @@ -524,31 +513,38 @@ mod test { addr_rcrd.set_aux_data_len(0); addr_rcrd.set_num_srcs(1); addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES); - addr_rcrd.payload_mut() + addr_rcrd + .payload_mut() .copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes()); } - packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into()); + packet.fill_checksum( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + ); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); } #[test] fn test_query_repr_parse() { let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]); - let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), - &packet, - &ChecksumCapabilities::default()); + let repr = Icmpv6Repr::parse( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + &packet, + &ChecksumCapabilities::default(), + ); assert_eq!(repr, Ok(create_repr(Message::MldQuery))); } #[test] fn test_report_repr_parse() { let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]); - let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), - &packet, - &ChecksumCapabilities::default()); + let repr = Icmpv6Repr::parse( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + &packet, + &ChecksumCapabilities::default(), + ); assert_eq!(repr, Ok(create_repr(Message::MldReport))); } @@ -557,10 +553,12 @@ mod test { let mut bytes = [0x2a; 44]; let mut packet = Packet::new_unchecked(&mut bytes[..]); let repr = create_repr(Message::MldQuery); - repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), - &mut packet, - &ChecksumCapabilities::default()); + repr.emit( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + &mut packet, + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); } @@ -569,10 +567,12 @@ mod test { let mut bytes = [0x2a; 44]; let mut packet = Packet::new_unchecked(&mut bytes[..]); let repr = create_repr(Message::MldReport); - repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), - &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), - &mut packet, - &ChecksumCapabilities::default()); + repr.emit( + &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), + &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), + &mut packet, + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 7a7f39c75..e1dc18d32 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -72,161 +72,136 @@ let mut buffer = vec![0; repr.buffer_len() + repr.payload_len]; mod field { pub type Field = ::core::ops::Range; - pub type Rest = ::core::ops::RangeFrom; + pub type Rest = ::core::ops::RangeFrom; } pub mod pretty_print; -#[cfg(feature = "medium-ethernet")] -mod ethernet; #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] mod arp; +#[cfg(feature = "proto-dhcpv4")] +pub(crate) mod dhcpv4; +#[cfg(feature = "medium-ethernet")] +mod ethernet; +#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] +mod icmp; +#[cfg(feature = "proto-ipv4")] +mod icmpv4; +#[cfg(feature = "proto-ipv6")] +mod icmpv6; +#[cfg(feature = "proto-igmp")] +mod igmp; pub(crate) mod ip; #[cfg(feature = "proto-ipv4")] mod ipv4; #[cfg(feature = "proto-ipv6")] mod ipv6; #[cfg(feature = "proto-ipv6")] -mod ipv6option; +mod ipv6fragment; #[cfg(feature = "proto-ipv6")] mod ipv6hopbyhop; #[cfg(feature = "proto-ipv6")] -mod ipv6fragment; +mod ipv6option; #[cfg(feature = "proto-ipv6")] mod ipv6routing; -#[cfg(feature = "proto-ipv4")] -mod icmpv4; #[cfg(feature = "proto-ipv6")] -mod icmpv6; -#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] -mod icmp; -#[cfg(feature = "proto-igmp")] -mod igmp; +mod mld; #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] mod ndisc; #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] mod ndiscoption; -#[cfg(feature = "proto-ipv6")] -mod mld; -mod udp; mod tcp; -#[cfg(feature = "proto-dhcpv4")] -pub(crate) mod dhcpv4; +mod udp; pub use self::pretty_print::PrettyPrinter; #[cfg(feature = "medium-ethernet")] -pub use self::ethernet::{EtherType as EthernetProtocol, - Address as EthernetAddress, - Frame as EthernetFrame, - HEADER_LEN as ETHERNET_HEADER_LEN, - Repr as EthernetRepr}; +pub use self::ethernet::{ + Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame, + Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN, +}; #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] -pub use self::arp::{Hardware as ArpHardware, - Operation as ArpOperation, - Packet as ArpPacket, - Repr as ArpRepr}; - -pub use self::ip::{Version as IpVersion, - Protocol as IpProtocol, - Address as IpAddress, - Endpoint as IpEndpoint, - Repr as IpRepr, - Cidr as IpCidr}; +pub use self::arp::{ + Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr, +}; + +pub use self::ip::{ + Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol, + Repr as IpRepr, Version as IpVersion, +}; #[cfg(feature = "proto-ipv4")] -pub use self::ipv4::{Address as Ipv4Address, - Packet as Ipv4Packet, - Repr as Ipv4Repr, - Cidr as Ipv4Cidr, - HEADER_LEN as IPV4_HEADER_LEN, - MIN_MTU as IPV4_MIN_MTU}; +pub use self::ipv4::{ + Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr, + HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, +}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6::{Address as Ipv6Address, - Packet as Ipv6Packet, - Repr as Ipv6Repr, - Cidr as Ipv6Cidr, - HEADER_LEN as IPV6_HEADER_LEN, - MIN_MTU as IPV6_MIN_MTU}; +pub use self::ipv6::{ + Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr, + HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU, +}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6option::{Ipv6Option, - Repr as Ipv6OptionRepr, - Type as Ipv6OptionType, - FailureType as Ipv6OptionFailureType}; +pub use self::ipv6option::{ + FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr, + Type as Ipv6OptionType, +}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, - Repr as Ipv6HopByHopRepr}; +pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, - Repr as Ipv6FragmentRepr}; +pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6routing::{Header as Ipv6RoutingHeader, - Repr as Ipv6RoutingRepr}; +pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr}; #[cfg(feature = "proto-ipv4")] -pub use self::icmpv4::{Message as Icmpv4Message, - DstUnreachable as Icmpv4DstUnreachable, - Redirect as Icmpv4Redirect, - TimeExceeded as Icmpv4TimeExceeded, - ParamProblem as Icmpv4ParamProblem, - Packet as Icmpv4Packet, - Repr as Icmpv4Repr}; +pub use self::icmpv4::{ + DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet, + ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr, + TimeExceeded as Icmpv4TimeExceeded, +}; #[cfg(feature = "proto-igmp")] -pub use self::igmp::{Packet as IgmpPacket, - Repr as IgmpRepr, - IgmpVersion}; +pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr}; #[cfg(feature = "proto-ipv6")] -pub use self::icmpv6::{Message as Icmpv6Message, - DstUnreachable as Icmpv6DstUnreachable, - TimeExceeded as Icmpv6TimeExceeded, - ParamProblem as Icmpv6ParamProblem, - Packet as Icmpv6Packet, - Repr as Icmpv6Repr}; +pub use self::icmpv6::{ + DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet, + ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded, +}; #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] pub use self::icmp::Repr as IcmpRepr; - #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] -pub use self::ndisc::{Repr as NdiscRepr, - RouterFlags as NdiscRouterFlags, - NeighborFlags as NdiscNeighborFlags}; +pub use self::ndisc::{ + NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags, +}; #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] -pub use self::ndiscoption::{NdiscOption, - Repr as NdiscOptionRepr, - Type as NdiscOptionType, - PrefixInformation as NdiscPrefixInformation, - RedirectedHeader as NdiscRedirectedHeader, - PrefixInfoFlags as NdiscPrefixInfoFlags}; +pub use self::ndiscoption::{ + NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags, + PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader, + Repr as NdiscOptionRepr, Type as NdiscOptionType, +}; #[cfg(feature = "proto-ipv6")] -pub use self::mld::{AddressRecord as MldAddressRecord, - Repr as MldRepr}; +pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr}; -pub use self::udp::{Packet as UdpPacket, - Repr as UdpRepr, - HEADER_LEN as UDP_HEADER_LEN}; +pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN}; -pub use self::tcp::{SeqNumber as TcpSeqNumber, - Packet as TcpPacket, - TcpOption, - Repr as TcpRepr, - Control as TcpControl, - HEADER_LEN as TCP_HEADER_LEN}; +pub use self::tcp::{ + Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber, + TcpOption, HEADER_LEN as TCP_HEADER_LEN, +}; #[cfg(feature = "proto-dhcpv4")] -pub use self::dhcpv4::{Packet as DhcpPacket, - Repr as DhcpRepr, - MessageType as DhcpMessageType, - CLIENT_PORT as DHCP_CLIENT_PORT, - SERVER_PORT as DHCP_SERVER_PORT, - MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT}; +pub use self::dhcpv4::{ + MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr, + CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, + SERVER_PORT as DHCP_SERVER_PORT, +}; diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index b31cb1c6b..89c8c711b 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -1,13 +1,13 @@ -use byteorder::{ByteOrder, NetworkEndian}; use bitflags::bitflags; +use byteorder::{ByteOrder, NetworkEndian}; -use crate::{Error, Result}; +use crate::time::Duration; use crate::wire::icmpv6::{field, Message, Packet}; -use crate::wire::{EthernetAddress, Ipv6Repr, Ipv6Packet}; +use crate::wire::Ipv6Address; +use crate::wire::{EthernetAddress, Ipv6Packet, Ipv6Repr}; use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; -use crate::time::Duration; -use crate::wire::Ipv6Address; +use crate::{Error, Result}; bitflags! { #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -82,7 +82,6 @@ impl> Packet { } } - /// Getters for the Neighbor Solicitation message header. /// See [RFC 4861 § 4.3]. /// @@ -194,7 +193,7 @@ impl + AsMut<[u8]>> Packet { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { RouterSolicit { - lladdr: Option + lladdr: Option, }, RouterAdvert { hop_limit: u8, @@ -204,44 +203,47 @@ pub enum Repr<'a> { retrans_time: Duration, lladdr: Option, mtu: Option, - prefix_info: Option + prefix_info: Option, }, NeighborSolicit { target_addr: Ipv6Address, - lladdr: Option + lladdr: Option, }, NeighborAdvert { flags: NeighborFlags, target_addr: Ipv6Address, - lladdr: Option + lladdr: Option, }, Redirect { target_addr: Ipv6Address, dest_addr: Ipv6Address, lladdr: Option, - redirected_hdr: Option> - } + redirected_hdr: Option>, + }, } impl<'a> Repr<'a> { /// Parse an NDISC packet and return a high-level representation of the /// packet. - pub fn parse(packet: &Packet<&'a T>) - -> Result> - where T: AsRef<[u8]> + ?Sized { + pub fn parse(packet: &Packet<&'a T>) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { match packet.msg_type() { Message::RouterSolicit => { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { return Err(Error::Unrecognized); } + _ => { + return Err(Error::Unrecognized); + } } } else { None }; Ok(Repr::RouterSolicit { lladdr }) - }, + } Message::RouterAdvert => { let mut offset = 0; let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); @@ -252,7 +254,9 @@ impl<'a> Repr<'a> { NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info), - _ => { return Err(Error::Unrecognized); } + _ => { + return Err(Error::Unrecognized); + } } offset += opt.buffer_len(); } @@ -262,29 +266,36 @@ impl<'a> Repr<'a> { router_lifetime: packet.router_lifetime(), reachable_time: packet.reachable_time(), retrans_time: packet.retrans_time(), - lladdr, mtu, prefix_info + lladdr, + mtu, + prefix_info, }) - }, + } Message::NeighborSolicit => { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { return Err(Error::Unrecognized); } + _ => { + return Err(Error::Unrecognized); + } } } else { None }; Ok(Repr::NeighborSolicit { - target_addr: packet.target_addr(), lladdr + target_addr: packet.target_addr(), + lladdr, }) - }, + } Message::NeighborAdvert => { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { return Err(Error::Unrecognized); } + _ => { + return Err(Error::Unrecognized); + } } } else { None @@ -292,9 +303,9 @@ impl<'a> Repr<'a> { Ok(Repr::NeighborAdvert { flags: packet.neighbor_flags(), target_addr: packet.target_addr(), - lladdr + lladdr, }) - }, + } Message::Redirect => { let mut offset = 0; let (mut lladdr, mut redirected_hdr) = (None, None); @@ -304,43 +315,50 @@ impl<'a> Repr<'a> { NdiscOptionType::SourceLinkLayerAddr => { lladdr = Some(opt.link_layer_addr()); offset += 8; - }, + } NdiscOptionType::RedirectedHeader => { if opt.data_len() < 6 { - return Err(Error::Truncated) + return Err(Error::Truncated); } else { let ip_packet = Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]); let ip_repr = Ipv6Repr::parse(&ip_packet)?; let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..]; redirected_hdr = Some(NdiscRedirectedHeader { - header: ip_repr, data + header: ip_repr, + data, }); offset += 8 + ip_repr.buffer_len() + data.len(); } } - _ => { return Err(Error::Unrecognized); } + _ => { + return Err(Error::Unrecognized); + } } } Ok(Repr::Redirect { target_addr: packet.target_addr(), dest_addr: packet.dest_addr(), - lladdr, redirected_hdr + lladdr, + redirected_hdr, }) - }, - _ => Err(Error::Unrecognized) + } + _ => Err(Error::Unrecognized), } } pub fn buffer_len(&self) -> usize { match self { - &Repr::RouterSolicit { lladdr } => { - match lladdr { - Some(_) => field::UNUSED.end + 8, - None => field::UNUSED.end, - } + &Repr::RouterSolicit { lladdr } => match lladdr { + Some(_) => field::UNUSED.end + 8, + None => field::UNUSED.end, }, - &Repr::RouterAdvert { lladdr, mtu, prefix_info, .. } => { + &Repr::RouterAdvert { + lladdr, + mtu, + prefix_info, + .. + } => { let mut offset = 0; if lladdr.is_some() { offset += 8; @@ -352,14 +370,18 @@ impl<'a> Repr<'a> { offset += 32; } field::RETRANS_TM.end + offset - }, + } &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => { match lladdr { Some(_) => field::TARGET_ADDR.end + 8, None => field::TARGET_ADDR.end, } - }, - &Repr::Redirect { lladdr, redirected_hdr, .. } => { + } + &Repr::Redirect { + lladdr, + redirected_hdr, + .. + } => { let mut offset = 0; if lladdr.is_some() { offset += 8; @@ -373,7 +395,9 @@ impl<'a> Repr<'a> { } pub fn emit(&self, packet: &mut Packet<&mut T>) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { match *self { Repr::RouterSolicit { lladdr } => { packet.set_msg_type(Message::RouterSolicit); @@ -383,10 +407,18 @@ impl<'a> Repr<'a> { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); } - }, + } - Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time, - retrans_time, lladdr, mtu, prefix_info } => { + Repr::RouterAdvert { + hop_limit, + flags, + router_lifetime, + reachable_time, + retrans_time, + lladdr, + mtu, + prefix_info, + } => { packet.set_msg_type(Message::RouterAdvert); packet.set_msg_code(0); packet.set_current_hop_limit(hop_limit); @@ -396,8 +428,7 @@ impl<'a> Repr<'a> { packet.set_retrans_time(retrans_time); let mut offset = 0; if let Some(lladdr) = lladdr { - let mut opt_pkt = - NdiscOption::new_unchecked(packet.payload_mut()); + let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); offset += 8; } @@ -412,34 +443,44 @@ impl<'a> Repr<'a> { NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt) } - }, + } - Repr::NeighborSolicit { target_addr, lladdr } => { + Repr::NeighborSolicit { + target_addr, + lladdr, + } => { packet.set_msg_type(Message::NeighborSolicit); packet.set_msg_code(0); packet.clear_reserved(); packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { - let mut opt_pkt = - NdiscOption::new_unchecked(packet.payload_mut()); + let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); } - }, + } - Repr::NeighborAdvert { flags, target_addr, lladdr } => { + Repr::NeighborAdvert { + flags, + target_addr, + lladdr, + } => { packet.set_msg_type(Message::NeighborAdvert); packet.set_msg_code(0); packet.clear_reserved(); packet.set_neighbor_flags(flags); packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { - let mut opt_pkt = - NdiscOption::new_unchecked(packet.payload_mut()); + let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); } - }, + } - Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => { + Repr::Redirect { + target_addr, + dest_addr, + lladdr, + redirected_hdr, + } => { packet.set_msg_type(Message::Redirect); packet.set_msg_code(0); packet.clear_reserved(); @@ -447,11 +488,10 @@ impl<'a> Repr<'a> { packet.set_dest_addr(dest_addr); let offset = match lladdr { Some(lladdr) => { - let mut opt_pkt = - NdiscOption::new_unchecked(packet.payload_mut()); + let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); 8 - }, + } None => 0, }; if let Some(redirected_hdr) = redirected_hdr { @@ -459,28 +499,23 @@ impl<'a> Repr<'a> { NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt); } - }, + } } } } #[cfg(test)] mod test { - use crate::phy::ChecksumCapabilities; use super::*; - use crate::wire::Icmpv6Repr; + use crate::phy::ChecksumCapabilities; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; + use crate::wire::Icmpv6Repr; - static ROUTER_ADVERT_BYTES: [u8; 24] = - [0x86, 0x00, 0xa9, 0xde, - 0x40, 0x80, 0x03, 0x84, - 0x00, 0x00, 0x03, 0x84, - 0x00, 0x00, 0x03, 0x84, - 0x01, 0x01, 0x52, 0x54, - 0x00, 0x12, 0x34, 0x56]; - static SOURCE_LINK_LAYER_OPT: [u8; 8] = - [0x01, 0x01, 0x52, 0x54, - 0x00, 0x12, 0x34, 0x56]; + static ROUTER_ADVERT_BYTES: [u8; 24] = [ + 0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, + 0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56, + ]; + static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56]; fn create_repr<'a>() -> Icmpv6Repr<'a> { Icmpv6Repr::Ndisc(Repr::RouterAdvert { @@ -491,7 +526,7 @@ mod test { retrans_time: Duration::from_millis(900), lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])), mtu: None, - prefix_info: None + prefix_info: None, }) } @@ -519,7 +554,9 @@ mod test { packet.set_router_lifetime(Duration::from_secs(900)); packet.set_reachable_time(Duration::from_millis(900)); packet.set_retrans_time(Duration::from_millis(900)); - packet.payload_mut().copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]); + packet + .payload_mut() + .copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); } @@ -527,17 +564,28 @@ mod test { #[test] fn test_router_advert_repr_parse() { let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]); - assert_eq!(Icmpv6Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &packet, &ChecksumCapabilities::default()).unwrap(), - create_repr()); + assert_eq!( + Icmpv6Repr::parse( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &packet, + &ChecksumCapabilities::default() + ) + .unwrap(), + create_repr() + ); } #[test] fn test_router_advert_repr_emit() { let mut bytes = vec![0x2a; 24]; let mut packet = Packet::new_unchecked(&mut bytes[..]); - create_repr().emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, - &mut packet, &ChecksumCapabilities::default()); + create_repr().emit( + &MOCK_IP_ADDR_1, + &MOCK_IP_ADDR_2, + &mut packet, + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); } } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index b0426bdcd..6c79b5ff2 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -1,10 +1,10 @@ -use core::fmt; -use byteorder::{NetworkEndian, ByteOrder}; use bitflags::bitflags; +use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; -use crate::{Error, Result}; use crate::time::Duration; use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; +use crate::{Error, Result}; enum_with_unknown! { /// NDISC Option Type @@ -27,10 +27,10 @@ impl fmt::Display for Type { match self { Type::SourceLinkLayerAddr => write!(f, "source link-layer address"), Type::TargetLinkLayerAddr => write!(f, "target link-layer address"), - Type::PrefixInformation => write!(f, "prefix information"), - Type::RedirectedHeader => write!(f, "redirected header"), - Type::Mtu => write!(f, "mtu"), - Type::Unknown(id) => write!(f, "{}", id) + Type::PrefixInformation => write!(f, "prefix information"), + Type::RedirectedHeader => write!(f, "redirected header"), + Type::Mtu => write!(f, "mtu"), + Type::Unknown(id) => write!(f, "{}", id), } } } @@ -49,7 +49,7 @@ bitflags! { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NdiscOption> { - buffer: T + buffer: T, } // Format of an NDISC Option @@ -67,11 +67,11 @@ mod field { use crate::wire::field::*; // 8-bit identifier of the type of option. - pub const TYPE: usize = 0; + pub const TYPE: usize = 0; // 8-bit unsigned integer. Length of the option, in units of 8 octests. - pub const LENGTH: usize = 1; + pub const LENGTH: usize = 1; // Minimum length of an option. - pub const MIN_OPT_LEN: usize = 8; + pub const MIN_OPT_LEN: usize = 8; // Variable-length field. Option-Type-specific data. pub fn DATA(length: u8) -> Field { 2..length as usize * 8 @@ -83,7 +83,7 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Link-Layer Address - pub const LL_ADDR: Field = 2..8; + pub const LL_ADDR: Field = 2..8; // Prefix Information Option fields. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -105,17 +105,17 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Prefix length. - pub const PREFIX_LEN: usize = 2; + pub const PREFIX_LEN: usize = 2; // Flags field of prefix header. - pub const FLAGS: usize = 3; + pub const FLAGS: usize = 3; // Valid lifetime. - pub const VALID_LT: Field = 4..8; + pub const VALID_LT: Field = 4..8; // Preferred lifetime. - pub const PREF_LT: Field = 8..12; + pub const PREF_LT: Field = 8..12; // Reserved bits pub const PREF_RESERVED: Field = 12..16; // Prefix - pub const PREFIX: Field = 16..32; + pub const PREFIX: Field = 16..32; // Redirected Header Option fields. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -129,10 +129,10 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Reserved bits. - pub const IP_RESERVED: Field = 4..8; + pub const IP_RESERVED: Field = 4..8; // Redirected header IP header + data. - pub const IP_DATA: usize = 8; - pub const REDIR_MIN_SZ: usize = 48; + pub const IP_DATA: usize = 8; + pub const REDIR_MIN_SZ: usize = 48; // MTU Option fields // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -142,7 +142,7 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // MTU - pub const MTU: Field = 4..8; + pub const MTU: Field = 4..8; } /// Core getter methods relevant to any type of NDISC option. @@ -180,16 +180,11 @@ impl> NdiscOption { Err(Error::Truncated) } else { match self.option_type() { - Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => - Ok(()), - Type::PrefixInformation if data_range.end >= field::PREFIX.end => - Ok(()), - Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => - Ok(()), - Type::Unknown(_) => - Ok(()), - _ => - Err(Error::Truncated), + Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()), + Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()), + Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()), + Type::Unknown(_) => Ok(()), + _ => Err(Error::Truncated), } } } @@ -371,7 +366,6 @@ impl + AsMut<[u8]>> NdiscOption { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> { /// Return a mutable pointer to the option data. #[inline] @@ -401,14 +395,14 @@ pub struct PrefixInformation { pub flags: PrefixInfoFlags, pub valid_lifetime: Duration, pub preferred_lifetime: Duration, - pub prefix: Ipv6Address + pub prefix: Ipv6Address, } #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RedirectedHeader<'a> { pub header: Ipv6Repr, - pub data: &'a [u8] + pub data: &'a [u8], } /// A high-level representation of an NDISC Option. @@ -421,16 +415,18 @@ pub enum Repr<'a> { RedirectedHeader(RedirectedHeader<'a>), Mtu(u32), Unknown { - type_: u8, + type_: u8, length: u8, - data: &'a [u8] + data: &'a [u8], }, } impl<'a> Repr<'a> { /// Parse an NDISC Option and return a high-level representation. pub fn parse(opt: &'a NdiscOption<&'a T>) -> Result> - where T: AsRef<[u8]> + ?Sized { + where + T: AsRef<[u8]> + ?Sized, + { match opt.option_type() { Type::SourceLinkLayerAddr => { if opt.data_len() == 1 { @@ -438,14 +434,14 @@ impl<'a> Repr<'a> { } else { Err(Error::Malformed) } - }, + } Type::TargetLinkLayerAddr => { if opt.data_len() == 1 { Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) } else { Err(Error::Malformed) } - }, + } Type::PrefixInformation => { if opt.data_len() == 4 { Ok(Repr::PrefixInformation(PrefixInformation { @@ -453,12 +449,12 @@ impl<'a> Repr<'a> { flags: opt.prefix_flags(), valid_lifetime: opt.valid_lifetime(), preferred_lifetime: opt.preferred_lifetime(), - prefix: opt.prefix() + prefix: opt.prefix(), })) } else { Err(Error::Malformed) } - }, + } Type::RedirectedHeader => { // If the options data length is less than 6, the option // does not have enough data to fill out the IP header @@ -470,60 +466,60 @@ impl<'a> Repr<'a> { let ip_repr = Ipv6Repr::parse(&ip_packet)?; Ok(Repr::RedirectedHeader(RedirectedHeader { header: ip_repr, - data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..] + data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..], })) } - }, + } Type::Mtu => { if opt.data_len() == 1 { Ok(Repr::Mtu(opt.mtu())) } else { Err(Error::Malformed) } - }, - Type::Unknown(id) => { - Ok(Repr::Unknown { - type_: id, - length: opt.data_len(), - data: opt.data() - }) } + Type::Unknown(id) => Ok(Repr::Unknown { + type_: id, + length: opt.data_len(), + data: opt.data(), + }), } } /// Return the length of a header that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { match self { - &Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => - field::LL_ADDR.end, - &Repr::PrefixInformation(_) => - field::PREFIX.end, - &Repr::RedirectedHeader(RedirectedHeader { header, data }) => - field::IP_DATA + header.buffer_len() + data.len(), - &Repr::Mtu(_) => - field::MTU.end, - &Repr::Unknown { length, .. } => - field::DATA(length).end + &Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end, + &Repr::PrefixInformation(_) => field::PREFIX.end, + &Repr::RedirectedHeader(RedirectedHeader { header, data }) => { + field::IP_DATA + header.buffer_len() + data.len() + } + &Repr::Mtu(_) => field::MTU.end, + &Repr::Unknown { length, .. } => field::DATA(length).end, } } /// Emit a high-level representation into an NDISC Option. pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { match *self { Repr::SourceLinkLayerAddr(addr) => { opt.set_option_type(Type::SourceLinkLayerAddr); opt.set_data_len(1); opt.set_link_layer_addr(addr); - }, + } Repr::TargetLinkLayerAddr(addr) => { opt.set_option_type(Type::TargetLinkLayerAddr); opt.set_data_len(1); opt.set_link_layer_addr(addr); - }, + } Repr::PrefixInformation(PrefixInformation { - prefix_len, flags, valid_lifetime, - preferred_lifetime, prefix + prefix_len, + flags, + valid_lifetime, + preferred_lifetime, + prefix, }) => { opt.clear_prefix_reserved(); opt.set_option_type(Type::PrefixInformation); @@ -533,10 +529,8 @@ impl<'a> Repr<'a> { opt.set_valid_lifetime(valid_lifetime); opt.set_preferred_lifetime(preferred_lifetime); opt.set_prefix(prefix); - }, - Repr::RedirectedHeader(RedirectedHeader { - header, data - }) => { + } + Repr::RedirectedHeader(RedirectedHeader { header, data }) => { let data_len = data.len() / 8; opt.clear_redirected_reserved(); opt.set_option_type(Type::RedirectedHeader); @@ -552,7 +546,11 @@ impl<'a> Repr<'a> { opt.set_data_len(1); opt.set_mtu(mtu); } - Repr::Unknown { type_: id, length, data } => { + Repr::Unknown { + type_: id, + length, + data, + } => { opt.set_option_type(Type::Unknown(id)); opt.set_data_len(length); opt.data_mut().copy_from_slice(data); @@ -567,67 +565,61 @@ impl<'a> fmt::Display for Repr<'a> { match *self { Repr::SourceLinkLayerAddr(addr) => { write!(f, "SourceLinkLayer addr={}", addr) - }, + } Repr::TargetLinkLayerAddr(addr) => { write!(f, "TargetLinkLayer addr={}", addr) - }, + } Repr::PrefixInformation(PrefixInformation { - prefix, prefix_len, - .. + prefix, prefix_len, .. }) => { write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) - }, - Repr::RedirectedHeader(RedirectedHeader { - header, - .. - }) => { + } + Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { write!(f, "RedirectedHeader header={}", header) - }, + } Repr::Mtu(mtu) => { write!(f, "MTU mtu={}", mtu) - }, - Repr::Unknown { type_: id, length, .. } => { + } + Repr::Unknown { + type_: id, length, .. + } => { write!(f, "Unknown({}) length={}", id, length) } } } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for NdiscOption { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { match NdiscOption::new_checked(buffer) { Err(err) => return write!(f, "{}({})", indent, err), - Ok(ndisc) => { - match Repr::parse(&ndisc) { - Err(_) => Ok(()), - Ok(repr) => { - write!(f, "{}{}", indent, repr) - } + Ok(ndisc) => match Repr::parse(&ndisc) { + Err(_) => Ok(()), + Ok(repr) => { + write!(f, "{}{}", indent, repr) } - } + }, } } } #[cfg(test)] mod test { - use crate::Error; + use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type}; use crate::time::Duration; use crate::wire::{EthernetAddress, Ipv6Address}; - use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr}; + use crate::Error; static PREFIX_OPT_BYTES: [u8; 32] = [ - 0x03, 0x04, 0x40, 0xc0, - 0x00, 0x00, 0x03, 0x84, - 0x00, 0x00, 0x03, 0xe8, - 0x00, 0x00, 0x00, 0x00, - 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01 + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, ]; #[test] @@ -636,7 +628,10 @@ mod test { assert_eq!(opt.option_type(), Type::PrefixInformation); assert_eq!(opt.data_len(), 4); assert_eq!(opt.prefix_len(), 64); - assert_eq!(opt.prefix_flags(), PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF); + assert_eq!( + opt.prefix_flags(), + PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF + ); assert_eq!(opt.valid_lifetime(), Duration::from_secs(900)); assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000)); assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)); @@ -658,11 +653,11 @@ mod test { #[test] fn test_short_packet() { - assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error::Truncated)); - let bytes = [ - 0x03, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - ]; + assert_eq!( + NdiscOption::new_checked(&[0x00, 0x00]), + Err(Error::Truncated) + ); + let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated)); } @@ -671,13 +666,17 @@ mod test { let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34]; let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); { - assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), - Ok(Repr::SourceLinkLayerAddr(addr))); + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Ok(Repr::SourceLinkLayerAddr(addr)) + ); } bytes[0] = 0x02; { - assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), - Ok(Repr::TargetLinkLayerAddr(addr))); + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Ok(Repr::TargetLinkLayerAddr(addr)) + ); } } @@ -688,9 +687,12 @@ mod test { flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF, valid_lifetime: Duration::from_secs(900), preferred_lifetime: Duration::from_secs(1000), - prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1) + prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); - assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), Ok(repr)); + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), + Ok(repr) + ); } #[test] @@ -701,7 +703,7 @@ mod test { flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF, valid_lifetime: Duration::from_secs(900), preferred_lifetime: Duration::from_secs(1000), - prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1) + prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); let mut opt = NdiscOption::new_unchecked(&mut bytes); repr.emit(&mut opt); @@ -711,6 +713,9 @@ mod test { #[test] fn test_repr_parse_mtu() { let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc]; - assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::Mtu(1500))); + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Ok(Repr::Mtu(1500)) + ); } } diff --git a/src/wire/pretty_print.rs b/src/wire/pretty_print.rs index 6d900de7c..a972cb641 100644 --- a/src/wire/pretty_print.rs +++ b/src/wire/pretty_print.rs @@ -37,7 +37,7 @@ use core::marker::PhantomData; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PrettyIndent { prefix: &'static str, - level: usize + level: usize, } impl PrettyIndent { @@ -72,24 +72,27 @@ pub trait PrettyPrint { /// /// `pretty_print` accepts a buffer and not a packet wrapper because the packet might /// be truncated, and so it might not be possible to create the packet wrapper. - fn pretty_print(buffer: &dyn AsRef<[u8]>, fmt: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result; + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + fmt: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result; } /// Wrapper for using a `PrettyPrint` where a `Display` is expected. pub struct PrettyPrinter<'a, T: PrettyPrint> { - prefix: &'static str, - buffer: &'a dyn AsRef<[u8]>, - phantom: PhantomData + prefix: &'static str, + buffer: &'a dyn AsRef<[u8]>, + phantom: PhantomData, } impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> { /// Format the listing with the recorded parameters when Display::fmt is called. pub fn new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T> { PrettyPrinter { - prefix: prefix, - buffer: buffer, - phantom: PhantomData + prefix: prefix, + buffer: buffer, + phantom: PhantomData, } } } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index d5622d97f..c3aa08943 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -1,10 +1,10 @@ -use core::{i32, ops, cmp, fmt}; use byteorder::{ByteOrder, NetworkEndian}; +use core::{cmp, fmt, i32, ops}; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; -use crate::wire::{IpProtocol, IpAddress}; use crate::wire::ip::checksum; +use crate::wire::{IpAddress, IpProtocol}; +use crate::{Error, Result}; /// A TCP sequence number. /// @@ -70,7 +70,7 @@ impl cmp::PartialOrd for SeqNumber { #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } mod field { @@ -80,12 +80,12 @@ mod field { pub const SRC_PORT: Field = 0..2; pub const DST_PORT: Field = 2..4; - pub const SEQ_NUM: Field = 4..8; - pub const ACK_NUM: Field = 8..12; - pub const FLAGS: Field = 12..14; + pub const SEQ_NUM: Field = 4..8; + pub const ACK_NUM: Field = 8..12; + pub const FLAGS: Field = 12..14; pub const WIN_SIZE: Field = 14..16; pub const CHECKSUM: Field = 16..18; - pub const URGENT: Field = 18..20; + pub const URGENT: Field = 18..20; pub fn OPTIONS(length: u8) -> Field { URGENT.end..(length as usize) @@ -99,14 +99,14 @@ mod field { pub const FLG_URG: u16 = 0x020; pub const FLG_ECE: u16 = 0x040; pub const FLG_CWR: u16 = 0x080; - pub const FLG_NS: u16 = 0x100; + pub const FLG_NS: u16 = 0x100; pub const OPT_END: u8 = 0x00; pub const OPT_NOP: u8 = 0x01; pub const OPT_MSS: u8 = 0x02; - pub const OPT_WS: u8 = 0x03; + pub const OPT_WS: u8 = 0x03; pub const OPT_SACKPERM: u8 = 0x04; - pub const OPT_SACKRNG: u8 = 0x05; + pub const OPT_SACKRNG: u8 = 0x05; } pub const HEADER_LEN: usize = field::URGENT.end; @@ -289,8 +289,12 @@ impl> Packet { pub fn segment_len(&self) -> usize { let data = self.buffer.as_ref(); let mut length = data.len() - self.header_len() as usize; - if self.syn() { length += 1 } - if self.fin() { length += 1 } + if self.syn() { + length += 1 + } + if self.fin() { + length += 1 + } length } @@ -333,13 +337,14 @@ impl> Packet { /// # Fuzzing /// This function always returns `true` when fuzzing. pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { - if cfg!(fuzzing) { return true } + if cfg!(fuzzing) { + return true; + } let data = self.buffer.as_ref(); checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, - data.len() as u32), - checksum::data(data) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32), + checksum::data(data), ]) == !0 } } @@ -405,7 +410,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_fin(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_FIN } else { raw & !field::FLG_FIN }; + let raw = if value { + raw | field::FLG_FIN + } else { + raw & !field::FLG_FIN + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -414,7 +423,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_syn(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_SYN } else { raw & !field::FLG_SYN }; + let raw = if value { + raw | field::FLG_SYN + } else { + raw & !field::FLG_SYN + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -423,7 +436,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_rst(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_RST } else { raw & !field::FLG_RST }; + let raw = if value { + raw | field::FLG_RST + } else { + raw & !field::FLG_RST + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -432,7 +449,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_psh(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_PSH } else { raw & !field::FLG_PSH }; + let raw = if value { + raw | field::FLG_PSH + } else { + raw & !field::FLG_PSH + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -441,7 +462,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_ack(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_ACK } else { raw & !field::FLG_ACK }; + let raw = if value { + raw | field::FLG_ACK + } else { + raw & !field::FLG_ACK + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -450,7 +475,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_urg(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_URG } else { raw & !field::FLG_URG }; + let raw = if value { + raw | field::FLG_URG + } else { + raw & !field::FLG_URG + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -459,7 +488,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_ece(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_ECE } else { raw & !field::FLG_ECE }; + let raw = if value { + raw | field::FLG_ECE + } else { + raw & !field::FLG_ECE + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -468,7 +501,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_cwr(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_CWR } else { raw & !field::FLG_CWR }; + let raw = if value { + raw | field::FLG_CWR + } else { + raw & !field::FLG_CWR + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -477,7 +514,11 @@ impl + AsMut<[u8]>> Packet { pub fn set_ns(&mut self, value: bool) { let data = self.buffer.as_mut(); let raw = NetworkEndian::read_u16(&data[field::FLAGS]); - let raw = if value { raw | field::FLG_NS } else { raw & !field::FLG_NS }; + let raw = if value { + raw | field::FLG_NS + } else { + raw & !field::FLG_NS + }; NetworkEndian::write_u16(&mut data[field::FLAGS], raw) } @@ -521,9 +562,8 @@ impl + AsMut<[u8]>> Packet { let checksum = { let data = self.buffer.as_ref(); !checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, - data.len() as u32), - checksum::data(data) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32), + checksum::data(data), ]) }; self.set_checksum(checksum) @@ -562,7 +602,7 @@ pub enum TcpOption<'a> { WindowScale(u8), SackPermitted, SackRange([Option<(u32, u32)>; 3]), - Unknown { kind: u8, data: &'a [u8] } + Unknown { kind: u8, data: &'a [u8] }, } impl<'a> TcpOption<'a> { @@ -581,24 +621,18 @@ impl<'a> TcpOption<'a> { length = *buffer.get(1).ok_or(Error::Truncated)? as usize; let data = buffer.get(2..length).ok_or(Error::Truncated)?; match (kind, length) { - (field::OPT_END, _) | - (field::OPT_NOP, _) => - unreachable!(), - (field::OPT_MSS, 4) => - option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)), - (field::OPT_MSS, _) => - return Err(Error::Malformed), - (field::OPT_WS, 3) => - option = TcpOption::WindowScale(data[0]), - (field::OPT_WS, _) => - return Err(Error::Malformed), - (field::OPT_SACKPERM, 2) => - option = TcpOption::SackPermitted, - (field::OPT_SACKPERM, _) => - return Err(Error::Malformed), + (field::OPT_END, _) | (field::OPT_NOP, _) => unreachable!(), + (field::OPT_MSS, 4) => { + option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)) + } + (field::OPT_MSS, _) => return Err(Error::Malformed), + (field::OPT_WS, 3) => option = TcpOption::WindowScale(data[0]), + (field::OPT_WS, _) => return Err(Error::Malformed), + (field::OPT_SACKPERM, 2) => option = TcpOption::SackPermitted, + (field::OPT_SACKPERM, _) => return Err(Error::Malformed), (field::OPT_SACKRNG, n) => { - if n < 10 || (n-2) % 8 != 0 { - return Err(Error::Malformed) + if n < 10 || (n - 2) % 8 != 0 { + return Err(Error::Malformed); } if n > 26 { // It's possible for a remote to send 4 SACK blocks, but extremely rare. @@ -622,19 +656,16 @@ impl<'a> TcpOption<'a> { *nmut = if left < data.len() { let mid = left + 4; let right = mid + 4; - let range_left = NetworkEndian::read_u32( - &data[left..mid]); - let range_right = NetworkEndian::read_u32( - &data[mid..right]); + let range_left = NetworkEndian::read_u32(&data[left..mid]); + let range_right = NetworkEndian::read_u32(&data[mid..right]); Some((range_left, range_right)) } else { None }; }); option = TcpOption::SackRange(sack_ranges); - }, - (_, _) => - option = TcpOption::Unknown { kind, data } + } + (_, _) => option = TcpOption::Unknown { kind, data }, } } } @@ -649,7 +680,7 @@ impl<'a> TcpOption<'a> { TcpOption::WindowScale(_) => 3, TcpOption::SackPermitted => 2, TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2, - TcpOption::Unknown { data, .. } => 2 + data.len() + TcpOption::Unknown { data, .. } => 2 + data.len(), } } @@ -657,23 +688,21 @@ impl<'a> TcpOption<'a> { let length; match *self { TcpOption::EndOfList => { - length = 1; + length = 1; // There may be padding space which also should be initialized. for p in buffer.iter_mut() { *p = field::OPT_END; } } TcpOption::NoOperation => { - length = 1; + length = 1; buffer[0] = field::OPT_NOP; } _ => { - length = self.buffer_len(); + length = self.buffer_len(); buffer[1] = length as u8; match self { - &TcpOption::EndOfList | - &TcpOption::NoOperation => - unreachable!(), + &TcpOption::EndOfList | &TcpOption::NoOperation => unreachable!(), &TcpOption::MaxSegmentSize(value) => { buffer[0] = field::OPT_MSS; NetworkEndian::write_u16(&mut buffer[2..], value) @@ -687,14 +716,21 @@ impl<'a> TcpOption<'a> { } &TcpOption::SackRange(slice) => { buffer[0] = field::OPT_SACKRNG; - slice.iter().filter(|s| s.is_some()).enumerate().for_each(|(i, s)| { - let (first, second) = *s.as_ref().unwrap(); - let pos = i * 8 + 2; - NetworkEndian::write_u32(&mut buffer[pos..], first); - NetworkEndian::write_u32(&mut buffer[pos+4..], second); - }); + slice + .iter() + .filter(|s| s.is_some()) + .enumerate() + .for_each(|(i, s)| { + let (first, second) = *s.as_ref().unwrap(); + let pos = i * 8 + 2; + NetworkEndian::write_u32(&mut buffer[pos..], first); + NetworkEndian::write_u32(&mut buffer[pos + 4..], second); + }); } - &TcpOption::Unknown { kind, data: provided } => { + &TcpOption::Unknown { + kind, + data: provided, + } => { buffer[0] = kind; buffer[2..].copy_from_slice(provided) } @@ -713,7 +749,7 @@ pub enum Control { Psh, Syn, Fin, - Rst + Rst, } #[allow(clippy::len_without_is_empty)] @@ -721,8 +757,8 @@ impl Control { /// Return the length of a control flag, in terms of sequence space. pub fn len(self) -> usize { match self { - Control::Syn | Control::Fin => 1, - _ => 0 + Control::Syn | Control::Fin => 1, + _ => 0, } } @@ -730,7 +766,7 @@ impl Control { pub fn quash_psh(self) -> Control { match self { Control::Psh => Control::None, - _ => self + _ => self, } } } @@ -739,46 +775,54 @@ impl Control { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { - pub src_port: u16, - pub dst_port: u16, - pub control: Control, - pub seq_number: SeqNumber, - pub ack_number: Option, - pub window_len: u16, + pub src_port: u16, + pub dst_port: u16, + pub control: Control, + pub seq_number: SeqNumber, + pub ack_number: Option, + pub window_len: u16, pub window_scale: Option, pub max_seg_size: Option, pub sack_permitted: bool, - pub sack_ranges: [Option<(u32, u32)>; 3], - pub payload: &'a [u8] + pub sack_ranges: [Option<(u32, u32)>; 3], + pub payload: &'a [u8], } impl<'a> Repr<'a> { /// Parse a Transmission Control Protocol packet and return a high-level representation. - pub fn parse(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress, - checksum_caps: &ChecksumCapabilities) -> Result> - where T: AsRef<[u8]> + ?Sized { + pub fn parse( + packet: &Packet<&'a T>, + src_addr: &IpAddress, + dst_addr: &IpAddress, + checksum_caps: &ChecksumCapabilities, + ) -> Result> + where + T: AsRef<[u8]> + ?Sized, + { // Source and destination ports must be present. - if packet.src_port() == 0 { return Err(Error::Malformed) } - if packet.dst_port() == 0 { return Err(Error::Malformed) } + if packet.src_port() == 0 { + return Err(Error::Malformed); + } + if packet.dst_port() == 0 { + return Err(Error::Malformed); + } // Valid checksum is expected. if checksum_caps.tcp.rx() && !packet.verify_checksum(src_addr, dst_addr) { - return Err(Error::Checksum) + return Err(Error::Checksum); } - let control = - match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) { - (false, false, false, false) => Control::None, - (false, false, false, true) => Control::Psh, - (true, false, false, _) => Control::Syn, - (false, true, false, _) => Control::Fin, - (false, false, true , _) => Control::Rst, - _ => return Err(Error::Malformed) - }; - let ack_number = - match packet.ack() { - true => Some(packet.ack_number()), - false => None - }; + let control = match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) { + (false, false, false, false) => Control::None, + (false, false, false, true) => Control::Psh, + (true, false, false, _) => Control::Syn, + (false, true, false, _) => Control::Fin, + (false, false, true, _) => Control::Rst, + _ => return Err(Error::Malformed), + }; + let ack_number = match packet.ack() { + true => Some(packet.ack_number()), + false => None, + }; // The PSH flag is ignored. // The URG flag and the urgent field is ignored. This behavior is standards-compliant, // however, most deployed systems (e.g. Linux) are *not* standards-compliant, and would @@ -794,41 +838,44 @@ impl<'a> Repr<'a> { match option { TcpOption::EndOfList => break, TcpOption::NoOperation => (), - TcpOption::MaxSegmentSize(value) => - max_seg_size = Some(value), + TcpOption::MaxSegmentSize(value) => max_seg_size = Some(value), TcpOption::WindowScale(value) => { // RFC 1323: Thus, the shift count must be limited to 14 (which allows windows // of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt // value exceeding 14, the TCP should log the error but use 14 instead of the // specified value. window_scale = if value > 14 { - net_debug!("{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", src_addr, packet.src_port(), dst_addr, packet.dst_port()); + net_debug!( + "{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", + src_addr, + packet.src_port(), + dst_addr, + packet.dst_port() + ); Some(14) } else { Some(value) }; - }, - TcpOption::SackPermitted => - sack_permitted = true, - TcpOption::SackRange(slice) => - sack_ranges = slice, + } + TcpOption::SackPermitted => sack_permitted = true, + TcpOption::SackRange(slice) => sack_ranges = slice, _ => (), } options = next_options; } Ok(Repr { - src_port: packet.src_port(), - dst_port: packet.dst_port(), - control: control, - seq_number: packet.seq_number(), - ack_number: ack_number, - window_len: packet.window_len(), + src_port: packet.src_port(), + dst_port: packet.dst_port(), + control: control, + seq_number: packet.seq_number(), + ack_number: ack_number, + window_len: packet.window_len(), window_scale: window_scale, max_seg_size: max_seg_size, sack_permitted: sack_permitted, - sack_ranges: sack_ranges, - payload: packet.payload() + sack_ranges: sack_ranges, + payload: packet.payload(), }) } @@ -847,9 +894,11 @@ impl<'a> Repr<'a> { if self.sack_permitted { length += 2; } - let sack_range_len: usize = self.sack_ranges.iter().map( - |o| o.map(|_| 8).unwrap_or(0) - ).sum(); + let sack_range_len: usize = self + .sack_ranges + .iter() + .map(|o| o.map(|_| 8).unwrap_or(0)) + .sum(); if sack_range_len > 0 { length += sack_range_len + 2; } @@ -865,9 +914,15 @@ impl<'a> Repr<'a> { } /// Emit a high-level representation into a Transmission Control Protocol packet. - pub fn emit(&self, packet: &mut Packet<&mut T>, src_addr: &IpAddress, dst_addr: &IpAddress, - checksum_caps: &ChecksumCapabilities) - where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized { + pub fn emit( + &self, + packet: &mut Packet<&mut T>, + src_addr: &IpAddress, + dst_addr: &IpAddress, + checksum_caps: &ChecksumCapabilities, + ) where + T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, + { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); packet.set_seq_number(self.seq_number); @@ -877,24 +932,28 @@ impl<'a> Repr<'a> { packet.clear_flags(); match self.control { Control::None => (), - Control::Psh => packet.set_psh(true), - Control::Syn => packet.set_syn(true), - Control::Fin => packet.set_fin(true), - Control::Rst => packet.set_rst(true) + Control::Psh => packet.set_psh(true), + Control::Syn => packet.set_syn(true), + Control::Fin => packet.set_fin(true), + Control::Rst => packet.set_rst(true), } packet.set_ack(self.ack_number.is_some()); { let mut options = packet.options_mut(); if let Some(value) = self.max_seg_size { - let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp); + let tmp = options; + options = TcpOption::MaxSegmentSize(value).emit(tmp); } if let Some(value) = self.window_scale { - let tmp = options; options = TcpOption::WindowScale(value).emit(tmp); + let tmp = options; + options = TcpOption::WindowScale(value).emit(tmp); } if self.sack_permitted { - let tmp = options; options = TcpOption::SackPermitted.emit(tmp); + let tmp = options; + options = TcpOption::SackPermitted.emit(tmp); } else if self.ack_number.is_some() && self.sack_ranges.iter().any(|s| s.is_some()) { - let tmp = options; options = TcpOption::SackRange(self.sack_ranges).emit(tmp); + let tmp = options; + options = TcpOption::SackRange(self.sack_ranges).emit(tmp); } if !options.is_empty() { @@ -922,8 +981,8 @@ impl<'a> Repr<'a> { pub fn is_empty(&self) -> bool { match self.control { _ if !self.payload.is_empty() => false, - Control::Syn | Control::Fin | Control::Rst => false, - Control::None | Control::Psh => true + Control::Syn | Control::Fin | Control::Rst => false, + Control::None | Control::Psh => true, } } } @@ -931,15 +990,28 @@ impl<'a> Repr<'a> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Cannot use Repr::parse because we don't have the IP addresses. - write!(f, "TCP src={} dst={}", - self.src_port(), self.dst_port())?; - if self.syn() { write!(f, " syn")? } - if self.fin() { write!(f, " fin")? } - if self.rst() { write!(f, " rst")? } - if self.psh() { write!(f, " psh")? } - if self.ece() { write!(f, " ece")? } - if self.cwr() { write!(f, " cwr")? } - if self.ns() { write!(f, " ns" )? } + write!(f, "TCP src={} dst={}", self.src_port(), self.dst_port())?; + if self.syn() { + write!(f, " syn")? + } + if self.fin() { + write!(f, " fin")? + } + if self.rst() { + write!(f, " rst")? + } + if self.psh() { + write!(f, " psh")? + } + if self.ece() { + write!(f, " ece")? + } + if self.cwr() { + write!(f, " cwr")? + } + if self.ns() { + write!(f, " ns")? + } write!(f, " seq={}", self.seq_number())?; if self.ack() { write!(f, " ack={}", self.ack_number())?; @@ -952,24 +1024,18 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { let mut options = self.options(); while !options.is_empty() { - let (next_options, option) = - match TcpOption::parse(options) { - Ok(res) => res, - Err(err) => return write!(f, " ({})", err) - }; + let (next_options, option) = match TcpOption::parse(options) { + Ok(res) => res, + Err(err) => return write!(f, " ({})", err), + }; match option { TcpOption::EndOfList => break, TcpOption::NoOperation => (), - TcpOption::MaxSegmentSize(value) => - write!(f, " mss={}", value)?, - TcpOption::WindowScale(value) => - write!(f, " ws={}", value)?, - TcpOption::SackPermitted => - write!(f, " sACK")?, - TcpOption::SackRange(slice) => - write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s - TcpOption::Unknown { kind, .. } => - write!(f, " opt({})", kind)?, + TcpOption::MaxSegmentSize(value) => write!(f, " mss={}", value)?, + TcpOption::WindowScale(value) => write!(f, " ws={}", value)?, + TcpOption::SackPermitted => write!(f, " sACK")?, + TcpOption::SackRange(slice) => write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s + TcpOption::Unknown { kind, .. } => write!(f, " opt({})", kind)?, } options = next_options; } @@ -979,14 +1045,13 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TCP src={} dst={}", - self.src_port, self.dst_port)?; + write!(f, "TCP src={} dst={}", self.src_port, self.dst_port)?; match self.control { Control::Syn => write!(f, " syn")?, Control::Fin => write!(f, " fin")?, Control::Rst => write!(f, " rst")?, Control::Psh => write!(f, " psh")?, - Control::None => () + Control::None => (), } write!(f, " seq={}", self.seq_number)?; if let Some(ack_number) = self.ack_number { @@ -1001,23 +1066,26 @@ impl<'a> fmt::Display for Repr<'a> { } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet) + Err(err) => write!(f, "{}({})", indent, err), + Ok(packet) => write!(f, "{}{}", indent, packet), } } } #[cfg(test)] mod test { + use super::*; #[cfg(feature = "proto-ipv4")] use crate::wire::Ipv4Address; - use super::*; #[cfg(feature = "proto-ipv4")] const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]); @@ -1025,22 +1093,16 @@ mod test { const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]); #[cfg(feature = "proto-ipv4")] - static PACKET_BYTES: [u8; 28] = - [0xbf, 0x00, 0x00, 0x50, - 0x01, 0x23, 0x45, 0x67, - 0x89, 0xab, 0xcd, 0xef, - 0x60, 0x35, 0x01, 0x23, - 0x01, 0xb6, 0x02, 0x01, - 0x03, 0x03, 0x0c, 0x01, - 0xaa, 0x00, 0x00, 0xff]; + static PACKET_BYTES: [u8; 28] = [ + 0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x60, 0x35, 0x01, + 0x23, 0x01, 0xb6, 0x02, 0x01, 0x03, 0x03, 0x0c, 0x01, 0xaa, 0x00, 0x00, 0xff, + ]; #[cfg(feature = "proto-ipv4")] - static OPTION_BYTES: [u8; 4] = - [0x03, 0x03, 0x0c, 0x01]; + static OPTION_BYTES: [u8; 4] = [0x03, 0x03, 0x0c, 0x01]; #[cfg(feature = "proto-ipv4")] - static PAYLOAD_BYTES: [u8; 4] = - [0xaa, 0x00, 0x00, 0xff]; + static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; #[test] #[cfg(feature = "proto-ipv4")] @@ -1062,7 +1124,10 @@ mod test { assert_eq!(packet.checksum(), 0x01b6); assert_eq!(packet.options(), &OPTION_BYTES[..]); assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]); - assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true); + assert_eq!( + packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), + true + ); } #[test] @@ -1107,28 +1172,25 @@ mod test { } #[cfg(feature = "proto-ipv4")] - static SYN_PACKET_BYTES: [u8; 24] = - [0xbf, 0x00, 0x00, 0x50, - 0x01, 0x23, 0x45, 0x67, - 0x00, 0x00, 0x00, 0x00, - 0x50, 0x02, 0x01, 0x23, - 0x7a, 0x8d, 0x00, 0x00, - 0xaa, 0x00, 0x00, 0xff]; + static SYN_PACKET_BYTES: [u8; 24] = [ + 0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x01, + 0x23, 0x7a, 0x8d, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff, + ]; #[cfg(feature = "proto-ipv4")] fn packet_repr() -> Repr<'static> { Repr { - src_port: 48896, - dst_port: 80, - seq_number: SeqNumber(0x01234567), - ack_number: None, - window_len: 0x0123, + src_port: 48896, + dst_port: 80, + seq_number: SeqNumber(0x01234567), + ack_number: None, + window_len: 0x0123, window_scale: None, - control: Control::Syn, + control: Control::Syn, max_seg_size: None, sack_permitted: false, - sack_ranges: [None, None, None], - payload: &PAYLOAD_BYTES + sack_ranges: [None, None, None], + payload: &PAYLOAD_BYTES, } } @@ -1136,7 +1198,13 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_parse() { let packet = Packet::new_unchecked(&SYN_PACKET_BYTES[..]); - let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap(); + let repr = Repr::parse( + &packet, + &SRC_ADDR.into(), + &DST_ADDR.into(), + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(repr, packet_repr()); } @@ -1146,7 +1214,12 @@ mod test { let repr = packet_repr(); let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); - repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()); + repr.emit( + &mut packet, + &SRC_ADDR.into(), + &DST_ADDR.into(), + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]); } @@ -1159,57 +1232,62 @@ mod test { } macro_rules! assert_option_parses { - ($opt:expr, $data:expr) => ({ + ($opt:expr, $data:expr) => {{ assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt))); let buffer = &mut [0; 40][..$opt.buffer_len()]; assert_eq!($opt.emit(buffer), &mut []); assert_eq!(&*buffer, $data); - }) + }}; } #[test] fn test_tcp_options() { - assert_option_parses!(TcpOption::EndOfList, - &[0x00]); - assert_option_parses!(TcpOption::NoOperation, - &[0x01]); - assert_option_parses!(TcpOption::MaxSegmentSize(1500), - &[0x02, 0x04, 0x05, 0xdc]); - assert_option_parses!(TcpOption::WindowScale(12), - &[0x03, 0x03, 0x0c]); - assert_option_parses!(TcpOption::SackPermitted, - &[0x4, 0x02]); - assert_option_parses!(TcpOption::SackRange([Some((500, 1500)), None, None]), - &[0x05, 0x0a, - 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]); - assert_option_parses!(TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]), - &[0x05, 0x12, - 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9, - 0x00, 0x00, 0x05, 0xdc, 0x00, 0x00, 0x09, 0xc4]); - assert_option_parses!(TcpOption::SackRange([Some((875000, 1225000)), - Some((1500000, 2500000)), - Some((876543210, 876654320))]), - &[0x05, 0x1a, - 0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28, - 0x00, 0x16, 0xe3, 0x60, 0x00, 0x26, 0x25, 0xa0, - 0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0]); - assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] }, - &[0x0c, 0x05, 0x01, 0x02, 0x03]) + assert_option_parses!(TcpOption::EndOfList, &[0x00]); + assert_option_parses!(TcpOption::NoOperation, &[0x01]); + assert_option_parses!(TcpOption::MaxSegmentSize(1500), &[0x02, 0x04, 0x05, 0xdc]); + assert_option_parses!(TcpOption::WindowScale(12), &[0x03, 0x03, 0x0c]); + assert_option_parses!(TcpOption::SackPermitted, &[0x4, 0x02]); + assert_option_parses!( + TcpOption::SackRange([Some((500, 1500)), None, None]), + &[0x05, 0x0a, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc] + ); + assert_option_parses!( + TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]), + &[ + 0x05, 0x12, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9, 0x00, 0x00, 0x05, 0xdc, + 0x00, 0x00, 0x09, 0xc4 + ] + ); + assert_option_parses!( + TcpOption::SackRange([ + Some((875000, 1225000)), + Some((1500000, 2500000)), + Some((876543210, 876654320)) + ]), + &[ + 0x05, 0x1a, 0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28, 0x00, 0x16, 0xe3, 0x60, + 0x00, 0x26, 0x25, 0xa0, 0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0 + ] + ); + assert_option_parses!( + TcpOption::Unknown { + kind: 12, + data: &[1, 2, 3][..] + }, + &[0x0c, 0x05, 0x01, 0x02, 0x03] + ) } #[test] fn test_malformed_tcp_options() { - assert_eq!(TcpOption::parse(&[]), - Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0xc]), - Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]), - Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0xc, 0x01]), - Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0x2, 0x02]), - Err(Error::Malformed)); - assert_eq!(TcpOption::parse(&[0x3, 0x02]), - Err(Error::Malformed)); + assert_eq!(TcpOption::parse(&[]), Err(Error::Truncated)); + assert_eq!(TcpOption::parse(&[0xc]), Err(Error::Truncated)); + assert_eq!( + TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]), + Err(Error::Truncated) + ); + assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error::Truncated)); + assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error::Malformed)); + assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error::Malformed)); } } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 49433495e..02983ec8f 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -1,16 +1,16 @@ -use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; +use core::fmt; -use crate::{Error, Result}; use crate::phy::ChecksumCapabilities; -use crate::wire::{IpProtocol, IpAddress}; use crate::wire::ip::checksum; +use crate::wire::{IpAddress, IpProtocol}; +use crate::{Error, Result}; /// A read/write wrapper around an User Datagram Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { - buffer: T + buffer: T, } mod field { @@ -20,7 +20,7 @@ mod field { pub const SRC_PORT: Field = 0..2; pub const DST_PORT: Field = 2..4; - pub const LENGTH: Field = 4..6; + pub const LENGTH: Field = 4..6; pub const CHECKSUM: Field = 6..8; pub fn PAYLOAD(length: u16) -> Field { @@ -113,13 +113,14 @@ impl> Packet { /// # Fuzzing /// This function always returns `true` when fuzzing. pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool { - if cfg!(fuzzing) { return true } + if cfg!(fuzzing) { + return true; + } let data = self.buffer.as_ref(); checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, - self.len() as u32), - checksum::data(&data[..self.len() as usize]) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32), + checksum::data(&data[..self.len() as usize]), ]) == !0 } } @@ -173,9 +174,8 @@ impl + AsMut<[u8]>> Packet { let checksum = { let data = self.buffer.as_ref(); !checksum::combine(&[ - checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, - self.len() as u32), - checksum::data(&data[..self.len() as usize]) + checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32), + checksum::data(&data[..self.len() as usize]), ]) }; // UDP checksum value of 0 means no checksum; if the checksum really is zero, @@ -210,21 +210,26 @@ pub struct Repr { impl Repr { /// Parse an User Datagram Protocol packet and return a high-level representation. - pub fn parse(packet: &Packet<&T>, src_addr: &IpAddress, dst_addr: &IpAddress, - checksum_caps: &ChecksumCapabilities) -> Result - where T: AsRef<[u8]> + ?Sized { + pub fn parse( + packet: &Packet<&T>, + src_addr: &IpAddress, + dst_addr: &IpAddress, + checksum_caps: &ChecksumCapabilities, + ) -> Result + where + T: AsRef<[u8]> + ?Sized, + { // Destination port cannot be omitted (but source port can be). - if packet.dst_port() == 0 { return Err(Error::Malformed) } + if packet.dst_port() == 0 { + return Err(Error::Malformed); + } // Valid checksum is expected... if checksum_caps.udp.rx() && !packet.verify_checksum(src_addr, dst_addr) { match (src_addr, dst_addr) { // ... except on UDP-over-IPv4, where it can be omitted. #[cfg(feature = "proto-ipv4")] - (&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) - if packet.checksum() == 0 => (), - _ => { - return Err(Error::Checksum) - } + (&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) if packet.checksum() == 0 => (), + _ => return Err(Error::Checksum), } } @@ -240,13 +245,17 @@ impl Repr { } /// Emit a high-level representation into an User Datagram Protocol packet. - pub fn emit(&self, packet: &mut Packet<&mut T>, - src_addr: &IpAddress, - dst_addr: &IpAddress, - payload_len: usize, - emit_payload: impl FnOnce(&mut [u8]), - checksum_caps: &ChecksumCapabilities) - where T: AsRef<[u8]> + AsMut<[u8]> { + pub fn emit( + &self, + packet: &mut Packet<&mut T>, + src_addr: &IpAddress, + dst_addr: &IpAddress, + payload_len: usize, + emit_payload: impl FnOnce(&mut [u8]), + checksum_caps: &ChecksumCapabilities, + ) where + T: AsRef<[u8]> + AsMut<[u8]>, + { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); packet.set_len((HEADER_LEN + payload_len) as u16); @@ -265,8 +274,13 @@ impl Repr { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Cannot use Repr::parse because we don't have the IP addresses. - write!(f, "UDP src={} dst={} len={}", - self.src_port(), self.dst_port(), self.payload().len()) + write!( + f, + "UDP src={} dst={} len={}", + self.src_port(), + self.dst_port(), + self.payload().len() + ) } } @@ -276,23 +290,26 @@ impl fmt::Display for Repr { } } -use crate::wire::pretty_print::{PrettyPrint, PrettyIndent}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { - fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, - indent: &mut PrettyIndent) -> fmt::Result { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet) + Err(err) => write!(f, "{}({})", indent, err), + Ok(packet) => write!(f, "{}{}", indent, packet), } } } #[cfg(test)] mod test { + use super::*; #[cfg(feature = "proto-ipv4")] use crate::wire::Ipv4Address; - use super::*; #[cfg(feature = "proto-ipv4")] const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]); @@ -300,20 +317,17 @@ mod test { const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]); #[cfg(feature = "proto-ipv4")] - static PACKET_BYTES: [u8; 12] = - [0xbf, 0x00, 0x00, 0x35, - 0x00, 0x0c, 0x12, 0x4d, - 0xaa, 0x00, 0x00, 0xff]; + static PACKET_BYTES: [u8; 12] = [ + 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff, + ]; #[cfg(feature = "proto-ipv4")] - static NO_CHECKSUM_PACKET: [u8; 12] = - [0xbf, 0x00, 0x00, 0x35, - 0x00, 0x0c, 0x00, 0x00, - 0xaa, 0x00, 0x00, 0xff]; + static NO_CHECKSUM_PACKET: [u8; 12] = [ + 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff, + ]; #[cfg(feature = "proto-ipv4")] - static PAYLOAD_BYTES: [u8; 4] = - [0xaa, 0x00, 0x00, 0xff]; + static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; #[test] #[cfg(feature = "proto-ipv4")] @@ -324,7 +338,10 @@ mod test { assert_eq!(packet.len(), 12); assert_eq!(packet.checksum(), 0x124d); assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]); - assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true); + assert_eq!( + packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), + true + ); } #[test] @@ -373,8 +390,13 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_parse() { let packet = Packet::new_unchecked(&PACKET_BYTES[..]); - let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), - &ChecksumCapabilities::default()).unwrap(); + let repr = Repr::parse( + &packet, + &SRC_ADDR.into(), + &DST_ADDR.into(), + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(repr, packet_repr()); } @@ -384,10 +406,14 @@ mod test { let repr = packet_repr(); let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()]; let mut packet = Packet::new_unchecked(&mut bytes); - repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), - PAYLOAD_BYTES.len(), - |payload| payload.copy_from_slice(&PAYLOAD_BYTES), - &ChecksumCapabilities::default()); + repr.emit( + &mut packet, + &SRC_ADDR.into(), + &DST_ADDR.into(), + PAYLOAD_BYTES.len(), + |payload| payload.copy_from_slice(&PAYLOAD_BYTES), + &ChecksumCapabilities::default(), + ); assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); } @@ -395,8 +421,13 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_checksum_omitted() { let packet = Packet::new_unchecked(&NO_CHECKSUM_PACKET[..]); - let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), - &ChecksumCapabilities::default()).unwrap(); + let repr = Repr::parse( + &packet, + &SRC_ADDR.into(), + &DST_ADDR.into(), + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(repr, packet_repr()); } } diff --git a/utils/packet2pcap.rs b/utils/packet2pcap.rs index 89b990636..5c062f2fc 100644 --- a/utils/packet2pcap.rs +++ b/utils/packet2pcap.rs @@ -1,15 +1,18 @@ +use getopts::Options; +use smoltcp::phy::{PcapLinkType, PcapSink}; +use smoltcp::time::Instant; use std::cell::RefCell; +use std::env; +use std::fs::File; use std::io::{self, Read, Write}; use std::path::Path; -use std::fs::File; -use std::env; use std::process::exit; -use smoltcp::phy::{PcapLinkType, PcapSink}; -use smoltcp::time::Instant; -use getopts::Options; -fn convert(packet_filename: &Path, pcap_filename: &Path, link_type: PcapLinkType) - -> io::Result<()> { +fn convert( + packet_filename: &Path, + pcap_filename: &Path, + link_type: PcapLinkType, +) -> io::Result<()> { let mut packet_file = File::open(packet_filename)?; let mut packet = Vec::new(); packet_file.read_to_end(&mut packet)?; @@ -35,31 +38,37 @@ fn main() { let mut opts = Options::new(); opts.optflag("h", "help", "print this help menu"); - opts.optopt("t", "link-type", "set link type (one of: ethernet ip)", "TYPE"); + opts.optopt( + "t", + "link-type", + "set link type (one of: ethernet ip)", + "TYPE", + ); let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(e) => { eprintln!("{}", e); - return + return; } }; - let link_type = - match matches.opt_str("t").as_ref().map(|s| &s[..]) { - Some("ethernet") => Some(PcapLinkType::Ethernet), - Some("ip") => Some(PcapLinkType::Ip), - _ => None - }; + let link_type = match matches.opt_str("t").as_ref().map(|s| &s[..]) { + Some("ethernet") => Some(PcapLinkType::Ethernet), + Some("ip") => Some(PcapLinkType::Ip), + _ => None, + }; if matches.opt_present("h") || matches.free.len() != 2 || link_type.is_none() { print_usage(&program, opts); - return + return; } - match convert(Path::new(&matches.free[0]), - Path::new(&matches.free[1]), - link_type.unwrap()) { + match convert( + Path::new(&matches.free[0]), + Path::new(&matches.free[1]), + link_type.unwrap(), + ) { Ok(()) => (), Err(e) => { eprintln!("Cannot convert packet to pcap: {}", e); From 750fcb18873d2f0a5c1987f6f3d22df62a6e4dcf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Jun 2021 10:43:05 +0200 Subject: [PATCH 161/566] Fix redundant closure clippy --- examples/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/utils.rs b/examples/utils.rs index 9e1cfca64..93dc259df 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -64,7 +64,7 @@ where #[cfg(feature = "log")] pub fn setup_logging(filter: &str) { - setup_logging_with_clock(filter, move || Instant::now()) + setup_logging_with_clock(filter, Instant::now) } pub fn create_options() -> (Options, Vec<&'static str>) { From 7dda148ad54ee9da424c55ecb30f6778b5c3bbf3 Mon Sep 17 00:00:00 2001 From: qiujiangkun Date: Sun, 27 Jun 2021 10:30:19 +0200 Subject: [PATCH 162/566] various clippy fix --- src/iface/interface.rs | 4 ++-- src/parsers.rs | 2 +- src/phy/fault_injector.rs | 16 ++++++++-------- src/phy/pcap_writer.rs | 2 +- src/phy/tracer.rs | 2 +- src/socket/dhcpv4.rs | 12 +++++------- src/socket/set.rs | 4 ++-- src/socket/tcp.rs | 2 +- src/wire/icmpv4.rs | 2 +- src/wire/icmpv6.rs | 10 +++++----- src/wire/ip.rs | 6 +++--- src/wire/ipv6.rs | 2 +- 12 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 0fd63ac69..314192359 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1082,7 +1082,7 @@ impl<'a> InterfaceInner<'a> { // 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) { + if !raw_socket.accepts(ip_repr) { continue; } @@ -1105,7 +1105,7 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ipv6_packet: &Ipv6Packet<&'frame T>, ) -> Result>> { - let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; + let ipv6_repr = Ipv6Repr::parse(ipv6_packet)?; if !ipv6_repr.src_addr.is_unicast() { // Discard packets with non-unicast source addresses. diff --git a/src/parsers.rs b/src/parsers.rs index bc6175ee0..cf67b44e5 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -472,7 +472,7 @@ impl FromStr for IpEndpoint { type Err = (); fn from_str(s: &str) -> Result { - Parser::new(s).until_eof(|p| Ok(p.accept_ip_endpoint()?)) + Parser::new(s).until_eof(|p| p.accept_ip_endpoint()) } } diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 49cc7ca6c..3e2963f6e 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -111,7 +111,7 @@ impl Device<'a>> FaultInjector { rx_bucket: 0, }; FaultInjector { - inner: inner, + inner, state: RefCell::new(state), config: Config::default(), } @@ -219,14 +219,14 @@ where } = self; inner.receive().map(|(rx_token, tx_token)| { let rx = RxToken { - state: &state, - config: config, + state, + config, token: rx_token, corrupt: [0; MTU], }; let tx = TxToken { - state: &state, - config: config, + state, + config, token: tx_token, junk: [0; MTU], }; @@ -241,9 +241,9 @@ where config, } = self; inner.transmit().map(|token| TxToken { - state: &state, - config: config, - token: token, + state, + config, + token, junk: [0; MTU], }) } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 2834e9398..2d96fb357 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -229,7 +229,7 @@ impl phy::TxToken for TxToken { token.consume(timestamp, len, |buffer| { let result = f(buffer); match mode { - PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, &buffer), + PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, buffer), PcapMode::RxOnly => (), }; result diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index d5b899146..40b140b0b 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -171,7 +171,7 @@ impl<'a> fmt::Display for Packet<'a> { &mut indent, ), #[cfg(feature = "medium-ip")] - Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) { + Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) { #[cfg(feature = "proto-ipv4")] Ok(crate::wire::IpVersion::Ipv4) => { crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print( diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 97711e6f1..0df203db2 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -307,13 +307,11 @@ impl Dhcpv4Socket { let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT]; if let Some(received) = dhcp_repr.dns_servers { let mut i = 0; - for addr in received.iter() { - if let Some(addr) = addr { - if addr.is_unicast() { - // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT - dns_servers[i] = Some(*addr); - i += 1; - } + for addr in received.iter().flatten() { + if addr.is_unicast() { + // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT + dns_servers[i] = Some(*addr); + i += 1; } } } diff --git a/src/socket/set.rs b/src/socket/set.rs index cdfd75756..482819516 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -201,7 +201,7 @@ impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> { type Item = &'a Socket<'b>; fn next(&mut self) -> Option { - while let Some(item_opt) = self.lower.next() { + for item_opt in &mut self.lower { if let Some(item) = item_opt.as_ref() { return Some(&item.socket); } @@ -222,7 +222,7 @@ impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> { type Item = SocketRef<'a, Socket<'b>>; fn next(&mut self) -> Option { - while let Some(item_opt) = self.lower.next() { + for item_opt in &mut self.lower { if let Some(item) = item_opt.as_mut() { return Some(SocketRef::new(&mut item.socket)); } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index dc8cceaa3..d8d30c22a 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1424,7 +1424,7 @@ impl<'a> TcpSocket<'a> { ack_min, ack_max ); - return Ok(Some(self.ack_reply(ip_repr, &repr))); + return Ok(Some(self.ack_reply(ip_repr, repr))); } } } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 7964ee339..78eb67a9d 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -485,7 +485,7 @@ impl<'a> Repr<'a> { let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut()); header.emit(&mut ip_packet, checksum_caps); let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; - payload.copy_from_slice(&data[..]) + payload.copy_from_slice(data) } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 395509b80..e093243d4 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -657,7 +657,7 @@ impl<'a> Repr<'a> { let mut ip_packet = Ipv6Packet::new_unchecked(buffer); header.emit(&mut ip_packet); let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; - payload.copy_from_slice(&data[..]); + payload.copy_from_slice(data); } match *self { @@ -669,7 +669,7 @@ impl<'a> Repr<'a> { packet.set_msg_type(Message::DstUnreachable); packet.set_msg_code(reason.into()); - emit_contained_packet(packet.payload_mut(), header, &data); + emit_contained_packet(packet.payload_mut(), header, data); } Repr::PktTooBig { mtu, header, data } => { @@ -677,7 +677,7 @@ impl<'a> Repr<'a> { packet.set_msg_code(0); packet.set_pkt_too_big_mtu(mtu); - emit_contained_packet(packet.payload_mut(), header, &data); + emit_contained_packet(packet.payload_mut(), header, data); } Repr::TimeExceeded { @@ -688,7 +688,7 @@ impl<'a> Repr<'a> { packet.set_msg_type(Message::TimeExceeded); packet.set_msg_code(reason.into()); - emit_contained_packet(packet.payload_mut(), header, &data); + emit_contained_packet(packet.payload_mut(), header, data); } Repr::ParamProblem { @@ -701,7 +701,7 @@ impl<'a> Repr<'a> { packet.set_msg_code(reason.into()); packet.set_param_problem_ptr(pointer); - emit_contained_packet(packet.payload_mut(), header, &data); + emit_contained_packet(packet.payload_mut(), header, data); } Repr::EchoRequest { diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 346284106..7f9f483c0 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -433,7 +433,7 @@ impl From<::std::net::SocketAddr> for Endpoint { impl From<::std::net::SocketAddrV4> for Endpoint { fn from(x: ::std::net::SocketAddrV4) -> Endpoint { Endpoint { - addr: x.ip().clone().into(), + addr: (*x.ip()).into(), port: x.port(), } } @@ -443,7 +443,7 @@ impl From<::std::net::SocketAddrV4> for Endpoint { impl From<::std::net::SocketAddrV6> for Endpoint { fn from(x: ::std::net::SocketAddrV6) -> Endpoint { Endpoint { - addr: x.ip().clone().into(), + addr: (*x.ip()).into(), port: x.port(), } } @@ -772,7 +772,7 @@ impl Repr { match *self { Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), &_checksum_caps), + Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index cb00586d3..60c384876 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -168,7 +168,7 @@ impl Address { let idx = (mask as usize) / 8; let modulus = (mask as usize) % 8; let (first, second) = self.0.split_at(idx); - bytes[0..idx].copy_from_slice(&first); + bytes[0..idx].copy_from_slice(first); if idx < 16 { let part = second[0]; bytes[idx] = part & (!(0xff >> modulus) as u8); From 1ccfef7e4208698abc0179a7f370c5feaf9aace8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Jun 2021 19:57:55 +0200 Subject: [PATCH 163/566] Update changelog. --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf01bfb4..591c63bba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) - udp: Add `close()` method to unbind socket. +## [0.7.5] - 2021-06-28 + +- dhcpv4: emit DNS servers in repr (#505) + +## [0.7.4] - 2021-06-11 + +- tcp: fix "subtract sequence numbers with underflow" on remote window shrink. (#490) +- tcp: fix substract with overflow when receiving a SYNACK with unincremented ACK number. (#491) +- tcp: use nonzero initial sequence number to workaround misbehaving servers. (#492) + ## [0.7.3] - 2021-05-29 - Fix "unused attribute" error in recent nightlies. @@ -73,6 +83,8 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.7.5]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.4...v0.7.5 +[0.7.4]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.3...v0.7.4 [0.7.3]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.2...v0.7.3 [0.7.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.1...v0.7.2 [0.7.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.7.1 From 201aa6da9a408dbf5331d95fd1f946f1b11f5851 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 28 Jun 2021 20:03:39 +0200 Subject: [PATCH 164/566] dhcpv4: Emit DNS servers in repr --- src/wire/dhcpv4.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index cbb5f1e89..8ac8e5923 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -727,6 +727,10 @@ impl<'a> Repr<'a> { if self.lease_duration.is_some() { len += 6; } + if let Some(dns_servers) = self.dns_servers { + len += 2; + len += dns_servers.iter().flatten().count() * core::mem::size_of::(); + } if let Some(list) = self.parameter_request_list { len += list.len() + 2; } @@ -892,6 +896,25 @@ impl<'a> Repr<'a> { if let Some(duration) = self.lease_duration { options = DhcpOption::IpLeaseTime(duration).emit(options); } + if let Some(dns_servers) = self.dns_servers { + const IP_SIZE: usize = core::mem::size_of::(); + let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE]; + + let data_len = dns_servers + .iter() + .flatten() + .enumerate() + .inspect(|(i, ip)| { + servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes()); + }) + .count() + * IP_SIZE; + let option = DhcpOption::Other { + kind: field::OPT_DOMAIN_NAME_SERVER, + data: &servers[..data_len], + }; + options = option.emit(options); + } if let Some(list) = self.parameter_request_list { options = DhcpOption::Other { kind: field::OPT_PARAMETER_REQUEST_LIST, @@ -1151,6 +1174,34 @@ mod test { repr.emit(&mut packet).unwrap(); } + #[test] + fn test_emit_offer_dns() { + let repr = { + let mut repr = offer_repr(); + repr.dns_servers = Some([ + Some(Ipv4Address([163, 1, 74, 6])), + Some(Ipv4Address([163, 1, 74, 7])), + Some(Ipv4Address([163, 1, 74, 3])), + ]); + repr + }; + let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut packet = Packet::new_unchecked(&mut bytes); + repr.emit(&mut packet).unwrap(); + + let packet = Packet::new_unchecked(&bytes); + let repr_parsed = Repr::parse(&packet).unwrap(); + + assert_eq!( + repr_parsed.dns_servers, + Some([ + Some(Ipv4Address([163, 1, 74, 6])), + Some(Ipv4Address([163, 1, 74, 7])), + Some(Ipv4Address([163, 1, 74, 3])) + ]) + ); + } + #[test] fn test_emit_dhcp_option() { static DATA: &[u8] = &[1, 3, 6]; From 3abc8cf72ece3b650c581393f1a203879ebd2fa8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Jun 2021 20:13:06 +0200 Subject: [PATCH 165/566] Enforce rustfmt in CI --- .github/workflows/rustfmt.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/rustfmt.yaml diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml new file mode 100644 index 000000000..fe6913344 --- /dev/null +++ b/.github/workflows/rustfmt.yaml @@ -0,0 +1,18 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Rustfmt check +jobs: + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + protile: minimal + components: rustfmt + - name: Check fmt + run: cargo fmt -- --check From 7ad5fe78cc35a4be61ff34bc40e09c6bf7fff849 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Jun 2021 20:22:18 +0200 Subject: [PATCH 166/566] Fix typo in rustfmt ci --- .github/workflows/rustfmt.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml index fe6913344..062306c39 100644 --- a/.github/workflows/rustfmt.yaml +++ b/.github/workflows/rustfmt.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: toolchain: stable - protile: minimal + profile: minimal components: rustfmt - name: Check fmt run: cargo fmt -- --check From f8ace98755d0494a81e501f237d658ab3c7f29a2 Mon Sep 17 00:00:00 2001 From: qiujiangkun Date: Thu, 1 Jul 2021 22:22:44 +0800 Subject: [PATCH 167/566] pcap timestamp bugfix --- src/phy/pcap_writer.rs | 2 +- src/time.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 2d96fb357..03fee45f6 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -73,7 +73,7 @@ pub trait PcapSink { assert!(length <= 65535); self.write_u32(timestamp.secs() as u32); // timestamp seconds - self.write_u32(timestamp.millis() as u32); // timestamp microseconds + self.write_u32(timestamp.micros() as u32); // timestamp microseconds self.write_u32(length as u32); // captured length self.write_u32(length as u32); // original length } diff --git a/src/time.rs b/src/time.rs index ebf13d3a6..d2f0cc187 100644 --- a/src/time.rs +++ b/src/time.rs @@ -59,6 +59,12 @@ impl Instant { self.millis % 1000 } + /// The fractional number of microseconds that have passed + /// since the beginning of time. + pub const fn micros(&self) -> i64 { + self.millis % 1000 * 1000 + } + /// The number of whole seconds that have passed since the /// beginning of time. pub const fn secs(&self) -> i64 { From c73d3d1b4a46147a6e2bab77b45381bab7025b1a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Aug 2021 15:17:13 +0200 Subject: [PATCH 168/566] Bump MSRV to 1.46 --- .github/workflows/test.yml | 10 +++++----- CHANGELOG.md | 3 ++- README.md | 2 +- src/lib.rs | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dafbe2452..65bae355e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,11 +11,11 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.40, and nightly. + # Test on stable, MSRV 1.46, and nightly. # Failure is permitted on nightly. rust: - stable - - 1.40.0 + - 1.46.0 - nightly features: @@ -58,18 +58,18 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.40, and nightly. + # Test on stable, MSRV 1.46, and nightly. # Failure is permitted on nightly. rust: - stable - - 1.40.0 + - 1.46.0 - nightly features: # These feature sets cannot run tests, so we only check they build. - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async include: - # defmt doesn't support 1.40 + # defmt doesn't support 1.46 - rust: stable features: defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - rust: nightly diff --git a/CHANGELOG.md b/CHANGELOG.md index 591c63bba..94f3879a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Version bumped to 0.8 +- Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.46 - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) - udp: Add `close()` method to unbind socket. @@ -46,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.7.0] - 2021-01-20 -Minimum Supported Rust Version (MSRV) **bumped** from 1.36 to 1.40 +- Minimum Supported Rust Version (MSRV) **bumped** from 1.36 to 1.40 ### New features - tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. ([351](https://github.com/smoltcp-rs/smoltcp/pull/351)) diff --git a/README.md b/README.md index dc616ec09..e55f8c2df 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.40 and later. +and compiles on stable Rust 1.46 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. diff --git a/src/lib.rs b/src/lib.rs index c0fd0fac5..353708814 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,11 +72,11 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.40 and up with any valid set of features. +//! This crate is guaranteed to compile on stable Rust 1.46 and up with any valid set of features. //! It *might* compile on older versions but that may change in any new patch release. //! //! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which -//! is higher than 1.40. +//! is higher than 1.46. //! //! [wire]: wire/index.html //! [osi]: https://en.wikipedia.org/wiki/OSI_model From 51b2adddadee499fc3d633ea1656d46809aa8be2 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 18 Aug 2021 09:29:29 +0200 Subject: [PATCH 169/566] Fix clippy lints --- src/iface/interface.rs | 56 +++++++++++++++--------------------- src/iface/neighbor.rs | 52 +++++++-------------------------- src/socket/dhcpv4.rs | 6 ++-- src/socket/icmp.rs | 10 +++---- src/socket/raw.rs | 6 ++-- src/socket/tcp.rs | 10 +++---- src/socket/udp.rs | 6 ++-- src/storage/packet_buffer.rs | 14 ++++----- src/time.rs | 12 ++++---- src/wire/icmpv4.rs | 2 +- src/wire/icmpv6.rs | 10 ++----- src/wire/igmp.rs | 4 +-- src/wire/ipv4.rs | 16 ++++------- src/wire/ipv6.rs | 7 ++--- src/wire/ipv6fragment.rs | 4 +-- src/wire/mld.rs | 2 +- src/wire/tcp.rs | 17 +++++------ src/wire/udp.rs | 5 +--- 18 files changed, 88 insertions(+), 151 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 314192359..e5b548719 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1086,7 +1086,7 @@ impl<'a> InterfaceInner<'a> { continue; } - match raw_socket.process(cx, &ip_repr, ip_payload) { + 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 @@ -1188,7 +1188,7 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'frame T>, ) -> Result>> { - let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &cx.caps.checksum)?; + 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. @@ -2296,17 +2296,15 @@ mod test { }); }); - assert_eq!( + assert!( iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), - true ); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), - false ); iface.update_ip_addrs(|addrs| { @@ -2314,29 +2312,25 @@ mod test { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); }); }); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), - false ); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), - false ); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), - false ); - assert_eq!( + assert!( iface .inner .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), - true ); iface.update_ip_addrs(|addrs| { @@ -2344,29 +2338,25 @@ mod test { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); }); }); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), - false ); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), - false ); - assert_eq!( - iface + assert!( + !iface .inner .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), - false ); - assert_eq!( + assert!( iface .inner .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), - true ); } @@ -2419,7 +2409,7 @@ mod test { payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }, - data: &data, + data: data, }; let expected_repr = IpPacket::Icmpv4(( Ipv4Repr { @@ -3002,7 +2992,7 @@ mod test { assert_eq!( socket.recv(), Ok(( - &icmp_data[..], + icmp_data, IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) )) ); @@ -3060,7 +3050,7 @@ mod test { hbh_pkt.set_header_len(0); offset += 8; { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.options_mut()[..]); + let mut pad_pkt = Ipv6Option::new_unchecked(&mut *hbh_pkt.options_mut()); Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt); } { @@ -3118,7 +3108,7 @@ mod test { #[cfg(feature = "medium-ip")] Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, }; - let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps).ok()?; + let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 64d239caf..03596a2e5 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -227,47 +227,26 @@ mod test { let mut cache_storage = [Default::default(); 3]; let mut cache = Cache::new(&mut cache_storage[..]); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) - .found(), - false - ); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) - .found(), - false - ); + assert!(!cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found()); + assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); assert_eq!( cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A) ); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) - .found(), - false - ); - assert_eq!( - cache + assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); + assert!( + !cache .lookup( &MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 ) .found(), - false ); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) - .found(), - false - ); + assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); } #[test] @@ -280,14 +259,13 @@ mod test { cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A) ); - assert_eq!( - cache + assert!( + !cache .lookup( &MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 ) .found(), - false ); } @@ -343,20 +321,10 @@ mod test { cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B) ); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)) - .found(), - false - ); + assert!(!cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found()); cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300)); - assert_eq!( - cache - .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)) - .found(), - false - ); + assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found()); assert_eq!( cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 0df203db2..f4593a8d4 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -501,8 +501,8 @@ impl Dhcpv4Socket { } } -impl<'a> Into> for Dhcpv4Socket { - fn into(self) -> Socket<'a> { - Socket::Dhcpv4(self) +impl<'a> From for Socket<'a> { + fn from(val: Dhcpv4Socket) -> Self { + Socket::Dhcpv4(val) } } diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 90f2516f3..acdee1204 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -515,9 +515,9 @@ impl<'a> IcmpSocket<'a> { } } -impl<'a> Into> for IcmpSocket<'a> { - fn into(self) -> Socket<'a> { - Socket::Icmp(self) +impl<'a> From> for Socket<'a> { + fn from(val: IcmpSocket<'a>) -> Self { + Socket::Icmp(val) } } @@ -711,7 +711,7 @@ mod test_ipv4 { Err(Error::Exhausted) ); - assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into()))); + assert_eq!(socket.recv(), Ok((data, REMOTE_IPV4.into()))); assert!(!socket.can_recv()); } @@ -972,7 +972,7 @@ mod test_ipv6 { Err(Error::Exhausted) ); - assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into()))); + assert_eq!(socket.recv(), Ok((data, REMOTE_IPV6.into()))); assert!(!socket.can_recv()); } diff --git a/src/socket/raw.rs b/src/socket/raw.rs index bf149c7ac..acd61bf49 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -330,9 +330,9 @@ impl<'a> RawSocket<'a> { } } -impl<'a> Into> for RawSocket<'a> { - fn into(self) -> Socket<'a> { - Socket::Raw(self) +impl<'a> From> for Socket<'a> { + fn from(val: RawSocket<'a>) -> Self { + Socket::Raw(val) } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index d8d30c22a..270918fbb 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1490,7 +1490,7 @@ impl<'a> TcpSocket<'a> { self.timer.set_for_close(cx.now); } - return Ok(Some(self.ack_reply(ip_repr, &repr))); + return Ok(Some(self.ack_reply(ip_repr, repr))); } } } @@ -1929,7 +1929,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - Ok(Some(self.ack_reply(ip_repr, &repr))) + Ok(Some(self.ack_reply(ip_repr, repr))) } else { Ok(None) } @@ -2392,9 +2392,9 @@ impl<'a> TcpSocket<'a> { } } -impl<'a> Into> for TcpSocket<'a> { - fn into(self) -> Socket<'a> { - Socket::Tcp(self) +impl<'a> From> for Socket<'a> { + fn from(val: TcpSocket<'a>) -> Self { + Socket::Tcp(val) } } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index ee57c3779..88df7e069 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -398,9 +398,9 @@ impl<'a> UdpSocket<'a> { } } -impl<'a> Into> for UdpSocket<'a> { - fn into(self) -> Socket<'a> { - Socket::Udp(self) +impl<'a> From> for Socket<'a> { + fn from(val: UdpSocket<'a>) -> Self { + Socket::Udp(val) } } diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 5bb168136..6ce6d17db 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -292,17 +292,17 @@ mod test { #[test] fn test_metadata_full_empty() { let mut buffer = buffer(); - assert_eq!(buffer.is_empty(), true); - assert_eq!(buffer.is_full(), false); + assert!(buffer.is_empty()); + assert!(!buffer.is_full()); assert!(buffer.enqueue(1, ()).is_ok()); - assert_eq!(buffer.is_empty(), false); + assert!(!buffer.is_empty()); assert!(buffer.enqueue(1, ()).is_ok()); assert!(buffer.enqueue(1, ()).is_ok()); - assert_eq!(buffer.is_full(), false); - assert_eq!(buffer.is_empty(), false); + assert!(!buffer.is_full()); + assert!(!buffer.is_empty()); assert!(buffer.enqueue(1, ()).is_ok()); - assert_eq!(buffer.is_full(), true); - assert_eq!(buffer.is_empty(), false); + assert!(buffer.is_full()); + assert!(!buffer.is_empty()); assert_eq!(buffer.metadata_ring.len(), 4); assert_eq!(buffer.enqueue(1, ()), Err(Error::Exhausted)); } diff --git a/src/time.rs b/src/time.rs index d2f0cc187..648eafe7d 100644 --- a/src/time.rs +++ b/src/time.rs @@ -97,9 +97,9 @@ impl From<::std::time::SystemTime> for Instant { } #[cfg(feature = "std")] -impl Into<::std::time::SystemTime> for Instant { - fn into(self) -> ::std::time::SystemTime { - ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(self.millis as u64) +impl From for ::std::time::SystemTime { + fn from(val: Instant) -> Self { + ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(val.millis as u64) } } @@ -284,9 +284,9 @@ impl From<::core::time::Duration> for Duration { } } -impl Into<::core::time::Duration> for Duration { - fn into(self) -> ::core::time::Duration { - ::core::time::Duration::from_millis(self.total_millis()) +impl From for ::core::time::Duration { + fn from(val: Duration) -> Self { + ::core::time::Duration::from_millis(val.total_millis()) } } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 78eb67a9d..ca3e38447 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -592,7 +592,7 @@ mod test { assert_eq!(packet.echo_ident(), 0x1234); assert_eq!(packet.echo_seq_no(), 0xabcd); assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]); - assert_eq!(packet.verify_checksum(), true); + assert!(packet.verify_checksum()); } #[test] diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index e093243d4..333b4900c 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -812,10 +812,7 @@ mod test { assert_eq!(packet.echo_ident(), 0x1234); assert_eq!(packet.echo_seq_no(), 0xabcd); assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]); - assert_eq!( - packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), - true - ); + assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2)); assert!(!packet.msg_type().is_error()); } @@ -869,10 +866,7 @@ mod test { assert_eq!(packet.checksum(), 0x0fc9); assert_eq!(packet.pkt_too_big_mtu(), 1500); assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]); - assert_eq!( - packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), - true - ); + assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2)); assert!(packet.msg_type().is_error()); } diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 2a11772f4..c2bfd0c90 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -393,7 +393,7 @@ mod test { packet.group_addr(), Ipv4Address::from_bytes(&[224, 0, 6, 150]) ); - assert_eq!(packet.verify_checksum(), true); + assert!(packet.verify_checksum()); } #[test] @@ -406,7 +406,7 @@ mod test { packet.group_addr(), Ipv4Address::from_bytes(&[225, 0, 0, 37]) ); - assert_eq!(packet.verify_checksum(), true); + assert!(packet.verify_checksum()); } #[test] diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index bdadc99b8..74140a17a 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -773,15 +773,15 @@ mod test { assert_eq!(packet.ecn(), 0); assert_eq!(packet.total_len(), 30); assert_eq!(packet.ident(), 0x102); - assert_eq!(packet.more_frags(), true); - assert_eq!(packet.dont_frag(), true); + assert!(packet.more_frags()); + assert!(packet.dont_frag()); assert_eq!(packet.frag_offset(), 0x203 * 8); assert_eq!(packet.hop_limit(), 0x1a); assert_eq!(packet.protocol(), Protocol::Icmp); assert_eq!(packet.checksum(), 0xd56e); assert_eq!(packet.src_addr(), Address([0x11, 0x12, 0x13, 0x14])); assert_eq!(packet.dst_addr(), Address([0x21, 0x22, 0x23, 0x24])); - assert_eq!(packet.verify_checksum(), true); + assert!(packet.verify_checksum()); assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]); } @@ -969,14 +969,8 @@ mod test { #[test] fn test_cidr_from_netmask() { - assert_eq!( - Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(), - true - ); - assert_eq!( - Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(), - true - ); + assert!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err()); + assert!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err()); assert_eq!( Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(), Cidr::new(Address([0, 0, 0, 1]), 24) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 60c384876..3dee62947 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -849,11 +849,8 @@ mod test { #[cfg(feature = "proto-ipv4")] #[test] fn test_is_ipv4_mapped() { - assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped()); - assert_eq!( - true, - Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped() - ); + assert!(!Address::UNSPECIFIED.is_ipv4_mapped()); + assert!(Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped()); } #[cfg(feature = "proto-ipv4")] diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index 36ccdfe77..24dc97110 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -241,13 +241,13 @@ mod test { let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG); assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.frag_offset(), 0); - assert_eq!(header.more_frags(), true); + assert!(header.more_frags()); assert_eq!(header.ident(), 12345); let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG); assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.frag_offset(), 320); - assert_eq!(header.more_frags(), false); + assert!(!header.more_frags()); assert_eq!(header.ident(), 67890); } diff --git a/src/wire/mld.rs b/src/wire/mld.rs index a8590924d..c77e1dae7 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -448,7 +448,7 @@ mod test { assert_eq!(packet.checksum(), 0x7374); assert_eq!(packet.max_resp_code(), 0x0400); assert_eq!(packet.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES); - assert_eq!(packet.s_flag(), true); + assert!(packet.s_flag()); assert_eq!(packet.qrv(), 0x02); assert_eq!(packet.qqic(), 0x12); assert_eq!(packet.num_srcs(), 0x01); diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index c3aa08943..bea318908 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -1113,21 +1113,18 @@ mod test { assert_eq!(packet.seq_number(), SeqNumber(0x01234567)); assert_eq!(packet.ack_number(), SeqNumber(0x89abcdefu32 as i32)); assert_eq!(packet.header_len(), 24); - assert_eq!(packet.fin(), true); - assert_eq!(packet.syn(), false); - assert_eq!(packet.rst(), true); - assert_eq!(packet.psh(), false); - assert_eq!(packet.ack(), true); - assert_eq!(packet.urg(), true); + assert!(packet.fin()); + assert!(!packet.syn()); + assert!(packet.rst()); + assert!(!packet.psh()); + assert!(packet.ack()); + assert!(packet.urg()); assert_eq!(packet.window_len(), 0x0123); assert_eq!(packet.urgent_at(), 0x0201); assert_eq!(packet.checksum(), 0x01b6); assert_eq!(packet.options(), &OPTION_BYTES[..]); assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]); - assert_eq!( - packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), - true - ); + assert!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into())); } #[test] diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 02983ec8f..458ec509c 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -338,10 +338,7 @@ mod test { assert_eq!(packet.len(), 12); assert_eq!(packet.checksum(), 0x124d); assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]); - assert_eq!( - packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), - true - ); + assert!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into())); } #[test] From 7da0f16f8afe38ead433672f9d73866debcd106c Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 18 Aug 2021 10:41:28 +0200 Subject: [PATCH 170/566] cargo fmt --- src/iface/interface.rs | 80 ++++++++++++++++-------------------------- src/iface/neighbor.rs | 52 +++++++++++++++------------ 2 files changed, 60 insertions(+), 72 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index e5b548719..a98bad80a 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -2296,68 +2296,48 @@ mod test { }); }); - assert!( - iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), - ); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), - ); + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])),); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); }); }); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), - ); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), - ); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), - ); - assert!( - iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), - ); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])),); + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])),); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); }); }); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), - ); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), - ); - assert!( - !iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), - ); - assert!( - iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), - ); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])),); + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])),); } #[test] diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 03596a2e5..c902d2878 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -227,26 +227,32 @@ mod test { let mut cache_storage = [Default::default(); 3]; let mut cache = Cache::new(&mut cache_storage[..]); - assert!(!cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found()); - assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) + .found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found()); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); assert_eq!( cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A) ); - assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); - assert!( - !cache - .lookup( - &MOCK_IP_ADDR_1, - Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 - ) - .found(), - ); + assert!(!cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found()); + assert!(!cache + .lookup( + &MOCK_IP_ADDR_1, + Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 + ) + .found(),); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); - assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found()); } #[test] @@ -259,14 +265,12 @@ mod test { cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A) ); - assert!( - !cache - .lookup( - &MOCK_IP_ADDR_1, - Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 - ) - .found(), - ); + assert!(!cache + .lookup( + &MOCK_IP_ADDR_1, + Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2 + ) + .found(),); } #[test] @@ -321,10 +325,14 @@ mod test { cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B) ); - assert!(!cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)) + .found()); cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300)); - assert!(!cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)) + .found()); assert_eq!( cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D) From 53a7da3bfc194cce73f6c29cc4a98026a1252669 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 19 Aug 2021 12:07:56 +0200 Subject: [PATCH 171/566] Remove trailing commas in macros This is going to become a hard error in future releases of the compiler. --- src/macros.rs | 8 ++++---- src/socket/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 3dc8305e0..b69af365b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,13 +1,13 @@ #[cfg(feature = "log")] macro_rules! net_log { - (trace, $($arg:expr),*) => { log::trace!($($arg),*); }; - (debug, $($arg:expr),*) => { log::debug!($($arg),*); }; + (trace, $($arg:expr),*) => { log::trace!($($arg),*) }; + (debug, $($arg:expr),*) => { log::debug!($($arg),*) }; } #[cfg(feature = "defmt")] macro_rules! net_log { - (trace, $($arg:expr),*) => { defmt::trace!($($arg),*); }; - (debug, $($arg:expr),*) => { defmt::debug!($($arg),*); }; + (trace, $($arg:expr),*) => { defmt::trace!($($arg),*) }; + (debug, $($arg:expr),*) => { defmt::debug!($($arg),*) }; } #[cfg(not(any(feature = "log", feature = "defmt")))] diff --git a/src/socket/mod.rs b/src/socket/mod.rs index b37c228eb..e87860b52 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -103,10 +103,10 @@ pub enum Socket<'a> { macro_rules! dispatch_socket { ($self_:expr, |$socket:ident| $code:expr) => { - dispatch_socket!(@inner $self_, |$socket| $code); + dispatch_socket!(@inner $self_, |$socket| $code) }; (mut $self_:expr, |$socket:ident| $code:expr) => { - dispatch_socket!(@inner mut $self_, |$socket| $code); + dispatch_socket!(@inner mut $self_, |$socket| $code) }; (@inner $( $mut_:ident )* $self_:expr, |$socket:ident| $code:expr) => { match $self_ { From c4a88d7a1477aef732f715e2f7ab31c07aa99785 Mon Sep 17 00:00:00 2001 From: bdbai Date: Tue, 24 Aug 2021 16:07:19 +0800 Subject: [PATCH 172/566] Expose underlying device from PcapWriter --- src/phy/pcap_writer.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 03fee45f6..a1a629b49 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -143,6 +143,21 @@ impl Device<'a>, S: PcapSink + Clone> PcapWriter { sink.global_header(link_type); PcapWriter { lower, sink, mode } } + + /// Get a reference to the underlying device. + /// + /// Even if the device offers reading through a standard reference, it is inadvisable to + /// directly read from the device as doing so will circumvent the packet capture. + pub fn get_ref(&self) -> &D { + &self.lower + } + + /// Get a mutable reference to the underlying device. + /// + /// It is inadvisable to directly read from the device as doing so will circumvent the packet capture. + pub fn get_mut(&mut self) -> &mut D { + &mut self.lower + } } impl<'a, D, S> Device<'a> for PcapWriter From 1cf2a20168914d1ea39523517ac2aae076c3411e Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 23 Aug 2021 17:11:53 +0200 Subject: [PATCH 173/566] Fix benches --- benches/bench.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 943a5421b..ed91ad59e 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -9,7 +9,8 @@ mod wire { use smoltcp::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr}; use smoltcp::wire::{TcpControl, TcpPacket, TcpRepr, TcpSeqNumber}; use smoltcp::wire::{UdpPacket, UdpRepr}; - use test; + + extern crate test; #[cfg(feature = "proto-ipv6")] const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([ @@ -32,18 +33,20 @@ mod wire { let repr = TcpRepr { src_port: 48896, dst_port: 80, + control: TcpControl::Syn, seq_number: TcpSeqNumber(0x01234567), ack_number: None, window_len: 0x0123, - control: TcpControl::Syn, - max_seg_size: None, window_scale: None, + max_seg_size: None, + sack_permitted: false, + sack_ranges: [None, None, None], payload: &PAYLOAD_BYTES, }; let mut bytes = vec![0xa5; repr.buffer_len()]; b.iter(|| { - let mut packet = TcpPacket::new(&mut bytes); + let mut packet = TcpPacket::new_unchecked(&mut bytes); repr.emit( &mut packet, &SRC_ADDR, @@ -60,16 +63,17 @@ mod wire { let repr = UdpRepr { src_port: 48896, dst_port: 80, - payload: &PAYLOAD_BYTES, }; - let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()]; b.iter(|| { - let mut packet = UdpPacket::new(&mut bytes); + let mut packet = UdpPacket::new_unchecked(&mut bytes); repr.emit( &mut packet, &SRC_ADDR, &DST_ADDR, + PAYLOAD_BYTES.len(), + |buf| buf.copy_from_slice(&PAYLOAD_BYTES), &ChecksumCapabilities::default(), ); }); @@ -88,7 +92,7 @@ mod wire { let mut bytes = vec![0xa5; repr.buffer_len()]; b.iter(|| { - let mut packet = Ipv4Packet::new(&mut bytes); + let mut packet = Ipv4Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &ChecksumCapabilities::default()); }); } @@ -106,7 +110,7 @@ mod wire { let mut bytes = vec![0xa5; repr.buffer_len()]; b.iter(|| { - let mut packet = Ipv6Packet::new(&mut bytes); + let mut packet = Ipv6Packet::new_unchecked(&mut bytes); repr.emit(&mut packet); }); } From c94f669497565df0fbe5acf7cb832e698c1a835e Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 10 Sep 2021 14:39:46 +0200 Subject: [PATCH 174/566] Update MSV of Rust --- .github/workflows/clippy.yml | 2 +- .github/workflows/test.yml | 4 ++-- README.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 585aab7f6..5e2ec84b1 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -17,7 +17,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.49.0 + toolchain: 1.53.0 override: true components: clippy - uses: actions-rs/clippy-check@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65bae355e..6a97b1bec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.46.0 + - 1.53.0 - nightly features: @@ -62,7 +62,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.46.0 + - 1.53.0 - nightly features: diff --git a/README.md b/README.md index e55f8c2df..327888054 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.46 and later. +and compiles on stable Rust 1.53 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. @@ -125,7 +125,7 @@ To use the _smoltcp_ library in your project, add the following to `Cargo.toml`: ```toml [dependencies] -smoltcp = "0.5" +smoltcp = "0.7.5" ``` The default configuration assumes a hosted environment, for ease of evaluation. @@ -133,7 +133,7 @@ You probably want to disable default features and configure them one by one: ```toml [dependencies] -smoltcp = { version = "0.5", default-features = false, features = ["log"] } +smoltcp = { version = "0.7.5", default-features = false, features = ["log"] } ``` ### Feature `std` From 09494c2e45bedfad0f167f7d4f69002543c3b67b Mon Sep 17 00:00:00 2001 From: david-sawatzke Date: Tue, 14 Sep 2021 18:08:04 +0200 Subject: [PATCH 175/566] Fix typos in tcp docs --- src/socket/tcp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 270918fbb..21fdb39b4 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -883,7 +883,7 @@ impl<'a> TcpSocket<'a> { } /// Check whether the transmit half of the full-duplex connection is open - /// (see [may_send](#method.may_send), and the transmit buffer is not full. + /// (see [may_send](#method.may_send)), and the transmit buffer is not full. #[inline] pub fn can_send(&self) -> bool { if !self.may_send() { @@ -906,7 +906,7 @@ impl<'a> TcpSocket<'a> { } /// Check whether the receive half of the full-duplex connection buffer is open - /// (see [may_recv](#method.may_recv), and the receive buffer is not empty. + /// (see [may_recv](#method.may_recv)), and the receive buffer is not empty. #[inline] pub fn can_recv(&self) -> bool { if !self.may_recv() { From 0d304bc496a739421eee15c850b449b3bc306589 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 15 Sep 2021 04:01:43 +0200 Subject: [PATCH 176/566] tcp: fix delayed ack causing ack not to be sent after 3 packets. --- src/socket/tcp.rs | 124 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 23 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 270918fbb..8600d54c0 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -300,6 +300,13 @@ impl Timer { } } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum AckDelayTimer { + Idle, + Waiting(Instant), + Immediate, +} + /// A Transmission Control Protocol socket. /// /// A TCP socket may passively listen for connections or actively connect to another endpoint. @@ -375,7 +382,7 @@ pub struct TcpSocket<'a> { ack_delay: Option, /// Delayed ack timer. If set, packets containing exclusively /// ACK or window updates (ie, no data) won't be sent until expiry. - ack_delay_until: Option, + ack_delay_timer: AckDelayTimer, /// Nagle's Algorithm enabled. nagle: bool, @@ -437,7 +444,7 @@ impl<'a> TcpSocket<'a> { local_rx_last_seq: None, local_rx_dup_acks: 0, ack_delay: Some(ACK_DELAY_DEFAULT), - ack_delay_until: None, + ack_delay_timer: AckDelayTimer::Idle, nagle: true, #[cfg(feature = "async")] @@ -660,7 +667,7 @@ impl<'a> TcpSocket<'a> { self.remote_mss = DEFAULT_MSS; self.remote_last_ts = None; self.ack_delay = Some(ACK_DELAY_DEFAULT); - self.ack_delay_until = None; + self.ack_delay_timer = AckDelayTimer::Idle; self.nagle = true; #[cfg(feature = "async")] @@ -1889,8 +1896,8 @@ impl<'a> TcpSocket<'a> { // Handle delayed acks if let Some(ack_delay) = self.ack_delay { if self.ack_to_transmit() || self.window_to_update() { - self.ack_delay_until = match self.ack_delay_until { - None => { + self.ack_delay_timer = match self.ack_delay_timer { + AckDelayTimer::Idle => { net_trace!( "{}:{}:{}: starting delayed ack timer", self.meta.handle, @@ -1898,19 +1905,28 @@ impl<'a> TcpSocket<'a> { self.remote_endpoint ); - Some(cx.now + ack_delay) + AckDelayTimer::Waiting(cx.now + ack_delay) } // RFC1122 says "in a stream of full-sized segments there SHOULD be an ACK // for at least every second segment". // For now, we send an ACK every second received packet, full-sized or not. - Some(_) => { + AckDelayTimer::Waiting(_) => { net_trace!( "{}:{}:{}: delayed ack timer already started, forcing expiry", self.meta.handle, self.local_endpoint, self.remote_endpoint ); - None + AckDelayTimer::Immediate + } + AckDelayTimer::Immediate => { + net_trace!( + "{}:{}:{}: delayed ack timer already force-expired", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + AckDelayTimer::Immediate } }; } @@ -1998,9 +2014,10 @@ impl<'a> TcpSocket<'a> { } fn delayed_ack_expired(&self, timestamp: Instant) -> bool { - match self.ack_delay_until { - None => true, - Some(t) => t <= timestamp, + match self.ack_delay_timer { + AckDelayTimer::Idle => true, + AckDelayTimer::Waiting(t) => t <= timestamp, + AckDelayTimer::Immediate => true, } } @@ -2309,16 +2326,26 @@ impl<'a> TcpSocket<'a> { self.timer.rewind_keep_alive(cx.now, self.keep_alive); // Reset delayed-ack timer - if self.ack_delay_until.is_some() { - net_trace!( - "{}:{}:{}: stop delayed ack timer", - self.meta.handle, - self.local_endpoint, - self.remote_endpoint - ); - - self.ack_delay_until = None; + match self.ack_delay_timer { + AckDelayTimer::Idle => {} + AckDelayTimer::Waiting(_) => { + net_trace!( + "{}:{}:{}: stop delayed ack timer", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ) + } + AckDelayTimer::Immediate => { + net_trace!( + "{}:{}:{}: stop delayed ack timer (was force-expired)", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ) + } } + self.ack_delay_timer = AckDelayTimer::Idle; // Leave the rest of the state intact if sending a keep-alive packet, since those // carry a fake segment. @@ -2369,10 +2396,12 @@ impl<'a> TcpSocket<'a> { PollAt::Now } else { let want_ack = self.ack_to_transmit() || self.window_to_update(); - let delayed_ack_poll_at = match (want_ack, self.ack_delay_until) { + + let delayed_ack_poll_at = match (want_ack, self.ack_delay_timer) { (false, _) => PollAt::Ingress, - (true, None) => PollAt::Now, - (true, Some(t)) => PollAt::Time(t), + (true, AckDelayTimer::Idle) => PollAt::Now, + (true, AckDelayTimer::Waiting(t)) => PollAt::Time(t), + (true, AckDelayTimer::Immediate) => PollAt::Now, }; let timeout_poll_at = match (self.remote_last_ts, self.timeout) { @@ -6544,6 +6573,55 @@ mod test { ); } + #[test] + fn test_delayed_ack_three_packets() { + let mut s = socket_established(); + s.set_ack_delay(Some(ACK_DELAY_DEFAULT)); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"abc"[..], + ..SEND_TEMPL + } + ); + + // No ACK is immediately sent. + recv!(s, Err(Error::Exhausted)); + + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 3, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"def"[..], + ..SEND_TEMPL + } + ); + + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 6, + ack_number: Some(LOCAL_SEQ + 1), + payload: &b"ghi"[..], + ..SEND_TEMPL + } + ); + + // Every 2nd (or more) packet, ACK is sent without delay. + recv!( + s, + Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1 + 9), + window_len: 55, + ..RECV_TEMPL + }) + ); + } + // =========================================================================================// // Tests for Nagle's Algorithm // =========================================================================================// From 51ca2b7566ff5786777c47f70c28dc12e58d5d86 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 16 Sep 2021 19:39:23 +0200 Subject: [PATCH 177/566] Add bors --- .github/workflows/test.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a97b1bec..1b4e33e6b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -68,12 +68,7 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - include: - # defmt doesn't support 1.46 - - rust: stable - features: defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - rust: nightly - features: defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 From f72e471eb03d618426445c376f1dfc7a2db332de Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 13 Sep 2021 16:18:42 +0200 Subject: [PATCH 178/566] Fix clippy because of MSV change --- src/iface/interface.rs | 2 +- src/iface/neighbor.rs | 13 ++++++++++++- src/phy/fault_injector.rs | 1 - src/socket/dhcpv4.rs | 2 +- src/socket/mod.rs | 3 ++- src/socket/tcp.rs | 2 +- src/wire/ip.rs | 12 ++++++------ 7 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a98bad80a..686ebe957 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -854,7 +854,7 @@ where Context { now, caps: self.device.capabilities(), - #[cfg(feature = "medium-ethernet")] + #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] ethernet_address: self.inner.ethernet_addr, } } diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index c902d2878..82f25c7fc 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -63,6 +63,7 @@ impl Answer { pub struct Cache<'a> { storage: ManagedMap<'a, IpAddress, Neighbor>, silent_until: Instant, + #[cfg(any(feature = "std", feature = "alloc"))] gc_threshold: usize, } @@ -74,6 +75,7 @@ impl<'a> Cache<'a> { pub(crate) const ENTRY_LIFETIME: Duration = Duration { millis: 60_000 }; /// Default number of entries in the cache before GC kicks in + #[cfg(any(feature = "std", feature = "alloc"))] pub(crate) const GC_THRESHOLD: usize = 1024; /// Create a cache. The backing storage is cleared upon creation. @@ -84,9 +86,18 @@ impl<'a> Cache<'a> { where T: Into>, { - Cache::new_with_limit(storage, Cache::GC_THRESHOLD) + let mut storage = storage.into(); + storage.clear(); + + Cache { + storage, + #[cfg(any(feature = "std", feature = "alloc"))] + gc_threshold: Self::GC_THRESHOLD, + silent_until: Instant::from_millis(0), + } } + #[cfg(any(feature = "std", feature = "alloc"))] pub fn new_with_limit(storage: T, gc_threshold: usize) -> Cache<'a> where T: Into>, diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 3e2963f6e..da21fea31 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -23,7 +23,6 @@ const MTU: usize = 1536; struct Config { corrupt_pct: u8, drop_pct: u8, - reorder_pct: u8, max_size: usize, max_tx_rate: u64, max_rx_rate: u64, diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index f4593a8d4..d93ae4f79 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -281,7 +281,7 @@ impl Dhcpv4Socket { } }; - let prefix_len = match IpAddress::Ipv4(subnet_mask).to_prefix_len() { + let prefix_len = match IpAddress::Ipv4(subnet_mask).prefix_len() { Some(prefix_len) => prefix_len, None => { net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask"); diff --git a/src/socket/mod.rs b/src/socket/mod.rs index e87860b52..76c442316 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -187,7 +187,7 @@ from_socket!(Dhcpv4Socket, Dhcpv4); #[derive(Clone, Debug)] pub(crate) struct Context { pub now: Instant, - #[cfg(feature = "medium-ethernet")] + #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] pub ethernet_address: Option, pub caps: DeviceCapabilities, } @@ -215,6 +215,7 @@ impl Context { #[cfg(not(feature = "medium-ethernet"))] max_transmission_unit: 1500, }, + #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] ethernet_address: None, now: Instant { millis: 0 }, }; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 270918fbb..8f678bb25 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -732,7 +732,7 @@ impl<'a> TcpSocket<'a> { // This lets us lower IpRepr later to determine IP header size and calculate MSS, // but without committing to a specific address right away. let local_addr = match local_endpoint.addr { - IpAddress::Unspecified => remote_endpoint.addr.to_unspecified(), + IpAddress::Unspecified => remote_endpoint.addr.as_unspecified(), ip => ip, }; let local_endpoint = IpEndpoint { diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 7f9f483c0..3fc85ea6c 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -167,7 +167,7 @@ impl Address { } /// Return an unspecified address that has the same IP version as `self`. - pub fn to_unspecified(&self) -> Address { + pub fn as_unspecified(&self) -> Address { match *self { Address::Unspecified => Address::Unspecified, #[cfg(feature = "proto-ipv4")] @@ -179,7 +179,7 @@ impl Address { /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`, /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise. - pub fn to_prefix_len(&self) -> Option { + pub fn prefix_len(&self) -> Option { let mut ones = true; let mut prefix_len = 0; for byte in self.as_bytes() { @@ -1216,7 +1216,7 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv4")] fn to_prefix_len_ipv4() { fn test_eq>(prefix_len: u8, mask: A) { - assert_eq!(Some(prefix_len), mask.into().to_prefix_len()); + assert_eq!(Some(prefix_len), mask.into().prefix_len()); } test_eq(0, Ipv4Address::new(0, 0, 0, 0)); @@ -1258,7 +1258,7 @@ pub(crate) mod test { fn to_prefix_len_ipv4_error() { assert_eq!( None, - IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).to_prefix_len() + IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len() ); } @@ -1266,7 +1266,7 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv6")] fn to_prefix_len_ipv6() { fn test_eq>(prefix_len: u8, mask: A) { - assert_eq!(Some(prefix_len), mask.into().to_prefix_len()); + assert_eq!(Some(prefix_len), mask.into().prefix_len()); } test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0)); @@ -1285,7 +1285,7 @@ pub(crate) mod test { IpAddress::from(Ipv6Address::new( 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1 )) - .to_prefix_len() + .prefix_len() ); } } From 176df1ffe60d7c00b3503d0c09dfb78ec19fd8a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 16 Sep 2021 19:44:57 +0200 Subject: [PATCH 179/566] Add bors.toml --- .github/bors.toml | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/bors.toml diff --git a/.github/bors.toml b/.github/bors.toml new file mode 100644 index 000000000..5f14937cb --- /dev/null +++ b/.github/bors.toml @@ -0,0 +1,48 @@ +status = [ + "fuzz", + "test (stable, default)", + "clippy", + "fmt", + "test (stable, std proto-ipv4)", + "test (stable, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", + "test (stable, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", + "test (stable, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", + "test (stable, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", + "test (stable, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", + "test (stable, std medium-ethernet proto-ipv6 socket-udp)", + "test (stable, std medium-ethernet proto-ipv6 socket-tcp)", + "test (stable, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", + "test (stable, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", + "test (stable, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-i...", + "test (1.53.0, default)", + "test (1.53.0, std proto-ipv4)", + "test (1.53.0, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", + "test (1.53.0, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", + "test (1.53.0, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", + "test (1.53.0, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", + "test (1.53.0, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", + "test (1.53.0, std medium-ethernet proto-ipv6 socket-udp)", + "test (1.53.0, std medium-ethernet proto-ipv6 socket-tcp)", + "test (1.53.0, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", + "test (1.53.0, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", + "test (1.53.0, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-i...", + "test (nightly, default)", + "test (nightly, std proto-ipv4)", + "test (nightly, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", + "test (nightly, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", + "test (nightly, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", + "test (nightly, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", + "test (nightly, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", + "test (nightly, std medium-ethernet proto-ipv6 socket-udp)", + "test (nightly, std medium-ethernet proto-ipv6 socket-tcp)", + "test (nightly, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", + "test (nightly, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", + "test (nightly, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-...", + "test (nightly, alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socke...", + "check (stable, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw...", + "check (stable, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto...", + "check (1.53.0, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw...", + "check (1.53.0, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto...", + "check (nightly, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-ra...", + "check (nightly, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp prot...", +] From 21e0a30d6432c31bffd40d9aaf6f764a79fa471c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Sep 2021 21:45:46 +0200 Subject: [PATCH 180/566] Fix assert with any_ip + broadcast dst_addr. Fixes #533 --- src/iface/interface.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 686ebe957..a24cdaafe 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1239,6 +1239,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 + || !ipv4_repr.dst_addr.is_unicast() || self .routes .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), cx.now) From 22400fe20c437b332d9fe93a5197bdca62f47398 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Sep 2021 22:09:48 +0200 Subject: [PATCH 181/566] Fix fuzz on latest nightly. See https://github.com/rust-fuzz/cargo-fuzz/issues/276 --- .github/workflows/fuzz.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index c09bb5d85..98046b4c5 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -16,6 +16,9 @@ jobs: toolchain: nightly override: true - name: Install cargo-fuzz - run: cargo install cargo-fuzz + # Fix for cargo-fuzz on latest nightly: https://github.com/rust-fuzz/cargo-fuzz/issues/276 + # Switch back to installing from crates.io when it's released. + #run: cargo install cargo-fuzz + run: cargo install --git https://github.com/rust-fuzz/cargo-fuzz --rev b4df3e58f767b5cad8d1aa6753961003f56f3609 - name: Fuzz run: cargo fuzz run packet_parser -- -max_len=1536 -max_total_time=30 From d204131e39041ab44b0a3336010fb62accac6ff4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 3 Oct 2021 21:02:46 +0200 Subject: [PATCH 182/566] tcp: Fix clippy --- src/socket/tcp.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index c34001b94..642b969d9 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -188,15 +188,13 @@ enum Timer { const ACK_DELAY_DEFAULT: Duration = Duration { millis: 10 }; const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; -impl Default for Timer { - fn default() -> Timer { +impl Timer { + fn new() -> Timer { Timer::Idle { keep_alive_at: None, } } -} -impl Timer { fn should_keep_alive(&self, timestamp: Instant) -> bool { match *self { Timer::Idle { @@ -417,7 +415,7 @@ impl<'a> TcpSocket<'a> { TcpSocket { meta: SocketMeta::default(), state: State::Closed, - timer: Timer::default(), + timer: Timer::new(), rtte: RttEstimator::default(), assembler: Assembler::new(rx_buffer.capacity()), tx_buffer: tx_buffer, @@ -644,7 +642,7 @@ impl<'a> TcpSocket<'a> { mem::size_of::() * 8 - self.rx_buffer.capacity().leading_zeros() as usize; self.state = State::Closed; - self.timer = Timer::default(); + self.timer = Timer::new(); self.rtte = RttEstimator::default(); self.assembler = Assembler::new(self.rx_buffer.capacity()); self.tx_buffer.clear(); @@ -6761,7 +6759,7 @@ mod test { #[test] fn test_timer_retransmit() { const RTO: Duration = Duration::from_millis(100); - let mut r = Timer::default(); + let mut r = Timer::new(); assert_eq!(r.should_retransmit(Instant::from_secs(1)), None); r.set_for_retransmit(Instant::from_millis(1000), RTO); assert_eq!(r.should_retransmit(Instant::from_millis(1000)), None); From dfe8265772d86c27f61f3b62fab3024927d9e306 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Sep 2021 14:42:59 +0200 Subject: [PATCH 183/566] phy: simplify PcapSink trait --- examples/utils.rs | 8 ++- src/phy/fault_injector.rs | 2 +- src/phy/fuzz_injector.rs | 4 +- src/phy/pcap_writer.rs | 107 +++++++++++++++++--------------------- utils/packet2pcap.rs | 10 ++-- 5 files changed, 58 insertions(+), 73 deletions(-) diff --git a/examples/utils.rs b/examples/utils.rs index 93dc259df..2a3518ba6 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -5,12 +5,10 @@ use env_logger::Builder; use getopts::{Matches, Options}; #[cfg(feature = "log")] use log::{trace, Level, LevelFilter}; -use std::cell::RefCell; use std::env; use std::fs::File; use std::io::{self, Write}; use std::process; -use std::rc::Rc; use std::str::{self, FromStr}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -18,7 +16,7 @@ use smoltcp::phy::RawSocket; #[cfg(feature = "phy-tuntap_interface")] use smoltcp::phy::TunTapInterface; use smoltcp::phy::{Device, FaultInjector, Medium, Tracer}; -use smoltcp::phy::{PcapMode, PcapSink, PcapWriter}; +use smoltcp::phy::{PcapMode, PcapWriter}; use smoltcp::time::{Duration, Instant}; #[cfg(feature = "log")] @@ -165,7 +163,7 @@ pub fn parse_middleware_options( matches: &mut Matches, device: D, loopback: bool, -) -> FaultInjector>>> +) -> FaultInjector>>> where D: for<'a> Device<'a>, { @@ -208,7 +206,7 @@ where let device = PcapWriter::new( device, - Rc::new(RefCell::new(pcap_writer)) as Rc, + pcap_writer, if loopback { PcapMode::TxOnly } else { diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index da21fea31..6a83bf759 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -289,7 +289,7 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { let mut corrupt = &mut corrupt[..buffer.len()]; corrupt.copy_from_slice(buffer); state.borrow_mut().corrupt(&mut corrupt); - f(&mut corrupt) + f(corrupt) } else { f(buffer) } diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index b22b91220..24983733d 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -122,8 +122,8 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { F: FnOnce(&mut [u8]) -> Result, { let Self { fuzzer, token } = self; - token.consume(timestamp, len, |mut buf| { - fuzzer.fuzz_packet(&mut buf); + token.consume(timestamp, len, |buf| { + fuzzer.fuzz_packet(buf); f(buf) }) } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index a1a629b49..89b8c8bec 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -1,8 +1,7 @@ use byteorder::{ByteOrder, NativeEndian}; +use core::cell::RefCell; use phy::Medium; #[cfg(feature = "std")] -use std::cell::RefCell; -#[cfg(feature = "std")] use std::io::Write; use crate::phy::{self, Device, DeviceCapabilities}; @@ -34,17 +33,20 @@ pub enum PcapMode { /// A packet capture sink. pub trait PcapSink { /// Write data into the sink. - fn write(&self, data: &[u8]); + fn write(&mut self, data: &[u8]); + + /// Flush data written into the sync. + fn flush(&mut self) {} /// Write an `u16` into the sink, in native byte order. - fn write_u16(&self, value: u16) { + fn write_u16(&mut self, value: u16) { let mut bytes = [0u8; 2]; NativeEndian::write_u16(&mut bytes, value); self.write(&bytes[..]) } /// Write an `u32` into the sink, in native byte order. - fn write_u32(&self, value: u32) { + fn write_u32(&mut self, value: u32) { let mut bytes = [0u8; 4]; NativeEndian::write_u32(&mut bytes, value); self.write(&bytes[..]) @@ -53,7 +55,7 @@ pub trait PcapSink { /// Write the libpcap global header into the sink. /// /// This method may be overridden e.g. if special synchronization is necessary. - fn global_header(&self, link_type: PcapLinkType) { + fn global_header(&mut self, link_type: PcapLinkType) { self.write_u32(0xa1b2c3d4); // magic number self.write_u16(2); // major version self.write_u16(4); // minor version @@ -69,7 +71,7 @@ pub trait PcapSink { /// /// # Panics /// This function panics if `length` is greater than 65535. - fn packet_header(&self, timestamp: Instant, length: usize) { + fn packet_header(&mut self, timestamp: Instant, length: usize) { assert!(length <= 65535); self.write_u32(timestamp.secs() as u32); // timestamp seconds @@ -81,28 +83,21 @@ pub trait PcapSink { /// Write the libpcap packet header followed by packet data into the sink. /// /// See also the note for [global_header](#method.global_header). - fn packet(&self, timestamp: Instant, packet: &[u8]) { + fn packet(&mut self, timestamp: Instant, packet: &[u8]) { self.packet_header(timestamp, packet.len()); - self.write(packet) - } -} - -impl> PcapSink for T { - fn write(&self, data: &[u8]) { - self.as_ref().write(data) + self.write(packet); + self.flush(); } } #[cfg(feature = "std")] -impl PcapSink for RefCell { - fn write(&self, data: &[u8]) { - self.borrow_mut().write_all(data).expect("cannot write") +impl PcapSink for T { + fn write(&mut self, data: &[u8]) { + T::write_all(self, data).expect("cannot write") } - fn packet(&self, timestamp: Instant, packet: &[u8]) { - self.packet_header(timestamp, packet.len()); - PcapSink::write(self, packet); - self.borrow_mut().flush().expect("cannot flush") + fn flush(&mut self) { + T::flush(self).expect("cannot flush") } } @@ -119,20 +114,19 @@ impl PcapSink for RefCell { /// [libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat /// [sink]: trait.PcapSink.html #[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PcapWriter where D: for<'a> Device<'a>, - S: PcapSink + Clone, + S: PcapSink, { lower: D, - sink: S, + sink: RefCell, mode: PcapMode, } -impl Device<'a>, S: PcapSink + Clone> PcapWriter { +impl Device<'a>, S: PcapSink> PcapWriter { /// Creates a packet capture writer. - pub fn new(lower: D, sink: S, mode: PcapMode) -> PcapWriter { + pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter { let medium = lower.capabilities().medium; let link_type = match medium { #[cfg(feature = "medium-ip")] @@ -141,7 +135,11 @@ impl Device<'a>, S: PcapSink + Clone> PcapWriter { Medium::Ethernet => PcapLinkType::Ethernet, }; sink.global_header(link_type); - PcapWriter { lower, sink, mode } + PcapWriter { + lower, + sink: RefCell::new(sink), + mode, + } } /// Get a reference to the underlying device. @@ -163,31 +161,27 @@ impl Device<'a>, S: PcapSink + Clone> PcapWriter { impl<'a, D, S> Device<'a> for PcapWriter where D: for<'b> Device<'b>, - S: PcapSink + Clone + 'a, + S: PcapSink + 'a, { - type RxToken = RxToken<>::RxToken, S>; - type TxToken = TxToken<>::TxToken, S>; + type RxToken = RxToken<'a, >::RxToken, S>; + type TxToken = TxToken<'a, >::TxToken, S>; fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - let &mut Self { - ref mut lower, - ref sink, - mode, - .. - } = self; - lower.receive().map(|(rx_token, tx_token)| { + let sink = &self.sink; + let mode = self.mode; + self.lower.receive().map(move |(rx_token, tx_token)| { let rx = RxToken { token: rx_token, - sink: sink.clone(), + sink, mode, }; let tx = TxToken { token: tx_token, - sink: sink.clone(), + sink, mode, }; (rx, tx) @@ -195,32 +189,29 @@ where } fn transmit(&'a mut self) -> Option { - let &mut Self { - ref mut lower, - ref sink, - mode, - } = self; - lower.transmit().map(|token| TxToken { - token, - sink: sink.clone(), - mode, - }) + let sink = &self.sink; + let mode = self.mode; + self.lower + .transmit() + .map(move |token| TxToken { token, sink, mode }) } } #[doc(hidden)] -pub struct RxToken { +pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> { token: Rx, - sink: S, + sink: &'a RefCell, mode: PcapMode, } -impl phy::RxToken for RxToken { +impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> { fn consume Result>(self, timestamp: Instant, f: F) -> Result { let Self { token, sink, mode } = self; token.consume(timestamp, |buffer| { match mode { - PcapMode::Both | PcapMode::RxOnly => sink.packet(timestamp, buffer.as_ref()), + PcapMode::Both | PcapMode::RxOnly => { + sink.borrow_mut().packet(timestamp, buffer.as_ref()) + } PcapMode::TxOnly => (), } f(buffer) @@ -229,13 +220,13 @@ impl phy::RxToken for RxToken { } #[doc(hidden)] -pub struct TxToken { +pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> { token: Tx, - sink: S, + sink: &'a RefCell, mode: PcapMode, } -impl phy::TxToken for TxToken { +impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result, @@ -244,7 +235,7 @@ impl phy::TxToken for TxToken { token.consume(timestamp, len, |buffer| { let result = f(buffer); match mode { - PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, buffer), + PcapMode::Both | PcapMode::TxOnly => sink.borrow_mut().packet(timestamp, buffer), PcapMode::RxOnly => (), }; result diff --git a/utils/packet2pcap.rs b/utils/packet2pcap.rs index 5c062f2fc..b76780ea9 100644 --- a/utils/packet2pcap.rs +++ b/utils/packet2pcap.rs @@ -1,10 +1,9 @@ use getopts::Options; use smoltcp::phy::{PcapLinkType, PcapSink}; use smoltcp::time::Instant; -use std::cell::RefCell; use std::env; use std::fs::File; -use std::io::{self, Read, Write}; +use std::io::{self, Read}; use std::path::Path; use std::process::exit; @@ -17,12 +16,9 @@ fn convert( let mut packet = Vec::new(); packet_file.read_to_end(&mut packet)?; - let pcap = RefCell::new(Vec::new()); - PcapSink::global_header(&pcap, link_type); - PcapSink::packet(&pcap, Instant::from_millis(0), &packet[..]); - let mut pcap_file = File::create(pcap_filename)?; - pcap_file.write_all(&pcap.borrow()[..])?; + PcapSink::global_header(&mut pcap_file, link_type); + PcapSink::packet(&mut pcap_file, Instant::from_millis(0), &packet[..]); Ok(()) } From a5e4e5a0fb778e760c57f3d1d929fb4ac229504d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Sep 2021 14:43:51 +0200 Subject: [PATCH 184/566] fuzz: Modernize fuzz crate, fix tcp_headers not compiling. --- fuzz/Cargo.toml | 16 ++-- fuzz/fuzz_targets/packet_parser.rs | 10 +- fuzz/fuzz_targets/tcp_headers.rs | 93 ++++++++++--------- fuzz/utils.rs | 144 +++++++++++++++++++++-------- 4 files changed, 165 insertions(+), 98 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 895812411..622bc773d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,21 +3,15 @@ name = "smoltcp-fuzz" version = "0.0.1" authors = ["Automatically generated"] publish = false +edition = "2018" [package.metadata] cargo-fuzz = true [dependencies] +libfuzzer-sys = "0.4" getopts = "0.2" - -[dependencies.smoltcp] -path = ".." - -[dependencies.libfuzzer-sys] -git = "https://github.com/rust-fuzz/libfuzzer-sys.git" - -[profile.release] -codegen-units = 1 # needed to prevent weird linker error about sancov guards +smoltcp = { path = "..", features = [ "medium-ethernet" ] } # Prevent this from interfering with workspaces [workspace] @@ -26,7 +20,11 @@ members = ["."] [[bin]] name = "packet_parser" path = "fuzz_targets/packet_parser.rs" +test = false +doc = false [[bin]] name = "tcp_headers" path = "fuzz_targets/tcp_headers.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/packet_parser.rs b/fuzz/fuzz_targets/packet_parser.rs index 357e1f333..e9e58bffb 100644 --- a/fuzz/fuzz_targets/packet_parser.rs +++ b/fuzz/fuzz_targets/packet_parser.rs @@ -1,8 +1,10 @@ #![no_main] -#[macro_use] extern crate libfuzzer_sys; -extern crate smoltcp; +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::*; fuzz_target!(|data: &[u8]| { - use smoltcp::wire::*; - format!("{}", PrettyPrinter::>::new("", &data)); + format!( + "{}", + PrettyPrinter::>::new("", &data) + ); }); diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index 86a274dfb..24482ef86 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -1,26 +1,20 @@ #![no_main] -#[macro_use] extern crate libfuzzer_sys; -extern crate smoltcp; - -use std as core; -extern crate getopts; - -use core::cmp; +use libfuzzer_sys::fuzz_target; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{Loopback, Medium}; -use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; -use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket}; -use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; +use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; +use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket}; +use std::cmp; -mod utils { - include!("../utils.rs"); -} +#[path = "../utils.rs"] +mod utils; mod mock { + use smoltcp::time::{Duration, Instant}; + use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; - use std::sync::atomic::{Ordering, AtomicUsize}; - use smoltcp::time::{Duration, Instant}; // should be AtomicU64 but that's unstable #[derive(Debug, Clone)] @@ -33,7 +27,8 @@ mod mock { } pub fn advance(&self, duration: Duration) { - self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst); + self.0 + .fetch_add(duration.total_millis() as usize, Ordering::SeqCst); } pub fn elapsed(&self) -> Instant { @@ -52,7 +47,10 @@ impl TcpHeaderFuzzer { // // Otherwise, it replaces the entire rest of the TCP header with the fuzzer's output. pub fn new(data: &[u8]) -> TcpHeaderFuzzer { - let copy_len = cmp::min(data.len(), 56 /* max TCP header length without port numbers*/); + let copy_len = cmp::min( + data.len(), + 56, /* max TCP header length without port numbers*/ + ); let mut fuzzer = TcpHeaderFuzzer([0; 56], copy_len); fuzzer.0[..copy_len].copy_from_slice(&data[..copy_len]); @@ -68,13 +66,16 @@ impl smoltcp::phy::Fuzzer for TcpHeaderFuzzer { let tcp_packet_offset = { let eth_frame = EthernetFrame::new_unchecked(&frame_data); - EthernetFrame::<&mut [u8]>::header_len() + match eth_frame.ethertype() { - EthernetProtocol::Ipv4 => - Ipv4Packet::new_unchecked(eth_frame.payload()).header_len() as usize, - EthernetProtocol::Ipv6 => - Ipv6Packet::new_unchecked(eth_frame.payload()).header_len() as usize, - _ => return - } + EthernetFrame::<&mut [u8]>::header_len() + + match eth_frame.ethertype() { + EthernetProtocol::Ipv4 => { + Ipv4Packet::new_unchecked(eth_frame.payload()).header_len() as usize + } + EthernetProtocol::Ipv6 => { + Ipv6Packet::new_unchecked(eth_frame.payload()).header_len() as usize + } + _ => return, + } }; let tcp_is_syn = { @@ -95,7 +96,7 @@ impl smoltcp::phy::Fuzzer for TcpHeaderFuzzer { (tcp_packet[12] as usize >> 4) * 4 }; - let tcp_packet = &mut frame_data[tcp_packet_offset+4..]; + let tcp_packet = &mut frame_data[tcp_packet_offset + 4..]; let replacement_data = &self.0[..self.1]; let copy_len = cmp::min(replacement_data.len(), tcp_header_len); @@ -114,17 +115,17 @@ fuzz_target!(|data: &[u8]| { let clock = mock::Clock::new(); let device = { - let (mut opts, mut free) = utils::create_options(); utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_middleware_options(&mut matches, Loopback::new(Medium::Ethernet), - /*loopback=*/true); + let device = utils::parse_middleware_options( + &mut matches, + Loopback::new(Medium::Ethernet), + /*loopback=*/ true, + ); - smoltcp::phy::FuzzInjector::new(device, - EmptyFuzzer(), - TcpHeaderFuzzer::new(data)) + smoltcp::phy::FuzzInjector::new(device, EmptyFuzzer(), TcpHeaderFuzzer::new(data)) }; let mut neighbor_cache_entries = [None; 8]; @@ -132,10 +133,10 @@ fuzz_target!(|data: &[u8]| { let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(EthernetAddress::default()) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .ethernet_addr(EthernetAddress::default()) + .neighbor_cache(neighbor_cache) + .ip_addrs(ip_addrs) + .finalize(); let server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but @@ -162,7 +163,7 @@ fuzz_target!(|data: &[u8]| { let server_handle = socket_set.add(server_socket); let client_handle = socket_set.add(client_socket); - let mut did_listen = false; + let mut did_listen = false; let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(4_000) { @@ -187,24 +188,28 @@ fuzz_target!(|data: &[u8]| { let mut socket = socket_set.get::(client_handle); if !socket.is_open() { if !did_connect { - socket.connect((IpAddress::v4(127, 0, 0, 1), 1234), - (IpAddress::Unspecified, 65000)).unwrap(); + socket + .connect( + (IpAddress::v4(127, 0, 0, 1), 1234), + (IpAddress::Unspecified, 65000), + ) + .unwrap(); did_connect = true; } } if socket.can_send() { - socket.send_slice(b"0123456789abcdef0123456789abcdef0123456789abcdef").unwrap(); + socket + .send_slice(b"0123456789abcdef0123456789abcdef0123456789abcdef") + .unwrap(); socket.close(); } } match iface.poll_delay(&socket_set, clock.elapsed()) { - Some(Duration { millis: 0 }) => {}, - Some(delay) => { - clock.advance(delay) - }, - None => clock.advance(Duration::from_millis(1)) + Some(Duration { millis: 0 }) => {} + Some(delay) => clock.advance(delay), + None => clock.advance(Duration::from_millis(1)), } } }); diff --git a/fuzz/utils.rs b/fuzz/utils.rs index 26763c23a..89329d99d 100644 --- a/fuzz/utils.rs +++ b/fuzz/utils.rs @@ -1,18 +1,17 @@ // TODO: this is literally a copy of examples/utils.rs, but without an allow dead code attribute. // The include logic does not allow having attributes in included files. -use std::cell::RefCell; -use std::str::{self, FromStr}; -use std::rc::Rc; -use std::io; -use std::fs::File; -use std::time::{SystemTime, UNIX_EPOCH}; +use getopts::{Matches, Options}; use std::env; +use std::fs::File; +use std::io; +use std::io::Write; use std::process; -use getopts::{Options, Matches}; +use std::str::{self, FromStr}; +use std::time::{SystemTime, UNIX_EPOCH}; -use smoltcp::phy::{Device, EthernetTracer, FaultInjector}; -use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; +use smoltcp::phy::{Device, FaultInjector, Tracer}; +use smoltcp::phy::{PcapMode, PcapWriter}; use smoltcp::time::Duration; pub fn create_options() -> (Options, Vec<&'static str>) { @@ -29,10 +28,17 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { } Ok(matches) => { if matches.opt_present("h") || matches.free.len() != free.len() { - let brief = format!("Usage: {} [OPTION]... {}", - env::args().nth(0).unwrap(), free.join(" ")); + let brief = format!( + "Usage: {} [OPTION]... {}", + env::args().nth(0).unwrap(), + free.join(" ") + ); print!("{}", options.usage(&brief)); - process::exit(if matches.free.len() != free.len() { 1 } else { 0 }) + process::exit(if matches.free.len() != free.len() { + 1 + } else { + 0 + }) } matches } @@ -41,46 +47,102 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { opts.optopt("", "pcap", "Write a packet capture file", "FILE"); - opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE"); - opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE"); - opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE"); - opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \ - (packets per interval)", "RATE"); - opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \ - (packets per interval)", "RATE"); - opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE"); + opts.optopt( + "", + "drop-chance", + "Chance of dropping a packet (%)", + "CHANCE", + ); + opts.optopt( + "", + "corrupt-chance", + "Chance of corrupting a packet (%)", + "CHANCE", + ); + opts.optopt( + "", + "size-limit", + "Drop packets larger than given size (octets)", + "SIZE", + ); + opts.optopt( + "", + "tx-rate-limit", + "Drop packets after transmit rate exceeds given limit \ + (packets per interval)", + "RATE", + ); + opts.optopt( + "", + "rx-rate-limit", + "Drop packets after transmit rate exceeds given limit \ + (packets per interval)", + "RATE", + ); + opts.optopt( + "", + "shaping-interval", + "Sets the interval for rate limiting (ms)", + "RATE", + ); } -pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: bool) - -> FaultInjector>>> - where D: for<'a> Device<'a> +pub fn parse_middleware_options( + matches: &mut Matches, + device: D, + loopback: bool, +) -> FaultInjector>>> +where + D: for<'a> Device<'a>, { - let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap()) - .unwrap_or(0); - let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap()) - .unwrap_or(0); - let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap()) - .unwrap_or(0); - let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); - let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); - let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap()) - .unwrap_or(0); + let drop_chance = matches + .opt_str("drop-chance") + .map(|s| u8::from_str(&s).unwrap()) + .unwrap_or(0); + let corrupt_chance = matches + .opt_str("corrupt-chance") + .map(|s| u8::from_str(&s).unwrap()) + .unwrap_or(0); + let size_limit = matches + .opt_str("size-limit") + .map(|s| usize::from_str(&s).unwrap()) + .unwrap_or(0); + let tx_rate_limit = matches + .opt_str("tx-rate-limit") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); + let rx_rate_limit = matches + .opt_str("rx-rate-limit") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); + let shaping_interval = matches + .opt_str("shaping-interval") + .map(|s| u64::from_str(&s).unwrap()) + .unwrap_or(0); - let pcap_writer: Box; + let pcap_writer: Box; if let Some(pcap_filename) = matches.opt_str("pcap") { pcap_writer = Box::new(File::create(pcap_filename).expect("cannot open file")) } else { pcap_writer = Box::new(io::sink()) } - let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos(); + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .subsec_nanos(); + + let device = PcapWriter::new( + device, + pcap_writer, + if loopback { + PcapMode::TxOnly + } else { + PcapMode::Both + }, + ); - let device = PcapWriter::new(device, Rc::new(RefCell::new(pcap_writer)) as Rc, - if loopback { PcapMode::TxOnly } else { PcapMode::Both }, - PcapLinkType::Ethernet); - let device = EthernetTracer::new(device, |_timestamp, _printer| { + let device = Tracer::new(device, |_timestamp, _printer| { #[cfg(feature = "log")] trace!("{}", _printer); }); From 41667f86685d10ddeec5c7d913e6b21eccf90d3c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 3 Oct 2021 21:39:55 +0200 Subject: [PATCH 185/566] phy: fix wrong order in FuzzInjector TX. Fixes #525. --- src/phy/fuzz_injector.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 24983733d..4be1fc0e2 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -123,8 +123,9 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { { let Self { fuzzer, token } = self; token.consume(timestamp, len, |buf| { + let result = f(buf); fuzzer.fuzz_packet(buf); - f(buf) + result }) } } From 7e118f0c9d126d7a9f722f82a2abe8d6d72ab36b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Oct 2021 20:35:17 +0200 Subject: [PATCH 186/566] tcp: fix use of fractional .millis() that should be .total_millis() in rtte. --- src/socket/tcp.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 642b969d9..9ef5868d5 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -110,7 +110,7 @@ impl RttEstimator { self.rto_count = 0; - let rto = self.retransmission_timeout().millis(); + let rto = self.retransmission_timeout().total_millis(); net_trace!( "rtte: sample={:?} rtt={:?} dev={:?} rto={:?}", new_rtt, @@ -137,7 +137,7 @@ impl RttEstimator { fn on_ack(&mut self, timestamp: Instant, seq: TcpSeqNumber) { if let Some((sent_timestamp, sent_seq)) = self.timestamp { if seq >= sent_seq { - self.sample((timestamp - sent_timestamp).millis() as u32); + self.sample((timestamp - sent_timestamp).total_millis() as u32); self.timestamp = None; } } @@ -158,7 +158,7 @@ impl RttEstimator { // increase if we see 3 consecutive retransmissions without any successful sample. self.rto_count = 0; self.rtt = RTTE_MAX_RTO.min(self.rtt * 2); - let rto = self.retransmission_timeout().millis(); + let rto = self.retransmission_timeout().total_millis(); net_trace!( "rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", self.rtt, From 43697943a8c7ec103d536280a7f17ed03e942b0d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Oct 2021 20:36:38 +0200 Subject: [PATCH 187/566] tcp: disallow zero MSS. This causes an infinite loop of zero-length packets: when we have data to send, it sends MSS-length packets until filling the window, which is an infinte amount of packets because mss is zero. Found with cargo-fuzz. --- src/socket/tcp.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 9ef5868d5..8a82373e0 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1575,6 +1575,19 @@ impl<'a> TcpSocket<'a> { // SYN packets in the LISTEN state change it to SYN-RECEIVED. (State::Listen, TcpControl::Syn) => { net_trace!("{}:{}: received SYN", self.meta.handle, self.local_endpoint); + if let Some(max_seg_size) = repr.max_seg_size { + if max_seg_size == 0 { + net_trace!( + "{}:{}:{}: received SYNACK with zero MSS, ignoring", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Ok(None); + } + self.remote_mss = max_seg_size as usize + } + self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); // FIXME: use something more secure here @@ -1582,9 +1595,6 @@ impl<'a> TcpSocket<'a> { self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; self.remote_has_sack = repr.sack_permitted; - if let Some(max_seg_size) = repr.max_seg_size { - self.remote_mss = max_seg_size as usize - } self.remote_win_scale = repr.window_scale; // Remote doesn't support window scaling, don't do it. if self.remote_win_scale.is_none() { @@ -1618,6 +1628,19 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); + if let Some(max_seg_size) = repr.max_seg_size { + if max_seg_size == 0 { + net_trace!( + "{}:{}:{}: received SYNACK with zero MSS, ignoring", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Ok(None); + } + self.remote_mss = max_seg_size as usize; + } + self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no + 1; @@ -1628,9 +1651,6 @@ impl<'a> TcpSocket<'a> { self.remote_win_shift = 0; } - if let Some(max_seg_size) = repr.max_seg_size { - self.remote_mss = max_seg_size as usize; - } self.set_state(State::Established); self.timer.set_for_idle(cx.now, self.keep_alive); } From 675c0a109ff34128f6f26bbb931ed3061186e3aa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Oct 2021 22:49:31 +0200 Subject: [PATCH 188/566] tcp: don't force-send data on retransmit. Previous code had an `if` to force sending a packet when retransmitting. When the remote window is zero this would cause an infinite loop of sending empty packets, because the "retransmit" flag would never get cleared. Remove the force-retransmit, and add an explicit check on `seq_to_transmit` for pending SYNs because SYN retransmission relied on it. Found with cargo-fuzz. --- src/socket/tcp.rs | 67 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8a82373e0..4d22946f5 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1994,6 +1994,11 @@ impl<'a> TcpSocket<'a> { // Have we sent data that hasn't been ACKed yet? let data_in_flight = self.remote_last_seq != self.local_seq_no; + // If we want to send a SYN and we haven't done so, do it! + if matches!(self.state, State::SynSent | State::SynReceived) && !data_in_flight { + return true; + } + // max sequence number we can send. let max_send_seq = self.local_seq_no + core::cmp::min(self.remote_win_len, self.tx_buffer.len()); @@ -2097,7 +2102,19 @@ impl<'a> TcpSocket<'a> { self.remote_endpoint, retransmit_delta ); + + // Rewind "last sequence number sent", as if we never + // had sent them. This will cause all data in the queue + // to be sent again. self.remote_last_seq = self.local_seq_no; + + // Clear the `should_retransmit` state. If we can't retransmit right + // now for whatever reason (like zero window), this avoids an + // infinite polling loop where `poll_at` returns `Now` but `dispatch` + // can't actually do anything. + self.timer.set_for_idle(cx.now, self.keep_alive); + + // Inform RTTE, so that it can avoid bogus measurements. self.rtte.on_retransmit(); } } @@ -2135,14 +2152,6 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - } 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(cx.now) { // If we need to transmit a keep-alive packet, do it. net_trace!( @@ -5506,6 +5515,48 @@ mod test { ); } + #[test] + fn test_fast_retransmit_zero_window() { + let mut s = socket_established(); + + send!(s, time 1000, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + + s.send_slice(b"abc").unwrap(); + + recv!(s, time 0, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abc"[..], + ..RECV_TEMPL + })); + + // 3 dup acks + send!(s, time 1050, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + send!(s, time 1050, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }); + send!(s, time 1050, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_len: 0, // boom + ..SEND_TEMPL + }); + + // even though we're in "fast retransmit", we shouldn't + // force-send anything because the remote's window is full. + recv!(s, Err(Error::Exhausted)); + } + // =========================================================================================// // Tests for window management. // =========================================================================================// From 5d31ae01f21daf3ecbb74a7f532f41d45fe613e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Oct 2021 23:39:43 +0200 Subject: [PATCH 189/566] tcp: in SYN_SENT only accept SYNACK, discard everything else. THis would let FIN packets through, breaking the logic below. Found with cargo-fuzz. --- src/socket/tcp.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 4d22946f5..5b1d5ff11 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1355,24 +1355,6 @@ impl<'a> TcpSocket<'a> { ); return Err(Error::Dropped); } - // Any ACK in the SYN-SENT state must have the SYN flag set. - ( - State::SynSent, - &TcpRepr { - control: TcpControl::None, - ack_number: Some(_), - .. - }, - ) => { - net_debug!( - "{}:{}:{}: expecting a SYN|ACK", - self.meta.handle, - self.local_endpoint, - self.remote_endpoint - ); - self.abort(); - return Err(Error::Dropped); - } // SYN|ACK in the SYN-SENT state must have the exact ACK number. ( State::SynSent, @@ -1392,6 +1374,17 @@ impl<'a> TcpSocket<'a> { return Err(Error::Dropped); } } + // Anything else in the SYN-SENT state is invalid. + (State::SynSent, _) => { + net_debug!( + "{}:{}:{}: expecting a SYN|ACK", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + self.abort(); + return Err(Error::Dropped); + } // Every acknowledgement must be for transmitted but unacknowledged data. ( _, From f95b0bf6f4fff01c1f99e52153f804f3692b954f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 5 Oct 2021 00:41:52 +0200 Subject: [PATCH 190/566] tcp: fix "subtract with overflow" when ack in syn-received is one too low. Found with cargo-fuzz. --- src/socket/tcp.rs | 60 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 5b1d5ff11..50f816360 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1499,23 +1499,26 @@ impl<'a> TcpSocket<'a> { let mut ack_of_fin = false; if repr.control != TcpControl::Rst { if let Some(ack_number) = repr.ack_number { - ack_len = ack_number - self.local_seq_no; - // There could have been no data sent before the SYN, so we always remove it - // from the sequence space. - if sent_syn { - ack_len -= 1 - } - // We could've sent data before the FIN, so only remove FIN from the sequence - // space if all of that data is acknowledged. - if sent_fin && self.tx_buffer.len() + 1 == ack_len { - ack_len -= 1; - net_trace!( - "{}:{}:{}: received ACK of FIN", - self.meta.handle, - self.local_endpoint, - self.remote_endpoint - ); - ack_of_fin = true; + // Sequence number corresponding to the first byte in `tx_buffer`. + // This normally equals `local_seq_no`, but is 1 higher if we ahve sent a SYN, + // as the SYN occupies 1 sequence number "before" the data. + let tx_buffer_start_seq = self.local_seq_no + (sent_syn as usize); + + if ack_number >= tx_buffer_start_seq { + ack_len = ack_number - tx_buffer_start_seq; + + // We could've sent data before the FIN, so only remove FIN from the sequence + // space if all of that data is acknowledged. + if sent_fin && self.tx_buffer.len() + 1 == ack_len { + ack_len -= 1; + net_trace!( + "{}:{}:{}: received ACK of FIN", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + ack_of_fin = true; + } } self.rtte.on_ack(cx.now, ack_number); @@ -3039,6 +3042,29 @@ mod test { sanity!(s, socket_established()); } + #[test] + fn test_syn_received_ack_too_low() { + let mut s = socket_syn_received(); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ), // wrong + ..SEND_TEMPL + } + ); + } + #[test] fn test_syn_received_fin() { let mut s = socket_syn_received(); From 1adca5b9ec6463111bc181e627c9ef1e53b1cdf6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 5 Oct 2021 00:48:02 +0200 Subject: [PATCH 191/566] tcp: do not switch to ESTABLISHED when ack in syn-received is one too low. --- src/socket/tcp.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 50f816360..f1fe1df77 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1396,9 +1396,14 @@ impl<'a> TcpSocket<'a> { let unacknowledged = self.tx_buffer.len() + control_len; // Acceptable ACK range (both inclusive) - let ack_min = self.local_seq_no; + let mut ack_min = self.local_seq_no; let ack_max = self.local_seq_no + unacknowledged; + // If we have sent a SYN, it MUST be acknowledged. + if sent_syn { + ack_min += 1; + } + if ack_number < ack_min { net_debug!( "{}:{}:{}: duplicate ACK ({} not in {}...{})", @@ -3061,8 +3066,10 @@ mod test { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ), // wrong ..SEND_TEMPL - } + }, + Err(Error::Dropped) ); + assert_eq!(s.state, State::SynReceived); } #[test] From 9c5f62e63b8806e14f9a88f23914916d77832a06 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 5 Oct 2021 00:48:34 +0200 Subject: [PATCH 192/566] tcp: add test for ack one-too-high in SYN_RECEIVED. --- src/socket/tcp.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f1fe1df77..ba0c29d0c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -3072,6 +3072,37 @@ mod test { assert_eq!(s.state, State::SynReceived); } + #[test] + fn test_syn_received_ack_too_high() { + let mut s = socket_syn_received(); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: Some(REMOTE_SEQ + 1), + max_seg_size: Some(BASE_MSS), + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 2), // wrong + ..SEND_TEMPL + }, + // TODO is this correct? probably not + Ok(Some(TcpRepr { + control: TcpControl::None, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); + assert_eq!(s.state, State::SynReceived); + } + #[test] fn test_syn_received_fin() { let mut s = socket_syn_received(); From 78fb02ac731eb036937a1238f38dd0d234470b06 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 5 Oct 2021 01:24:52 +0200 Subject: [PATCH 193/566] tcp: fix "attempt to negate with overflow" when initial seq is 0xFFFF_FFFF. Found with cargo-fuzz. --- src/socket/tcp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index ba0c29d0c..eca1dbb05 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1592,7 +1592,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); // FIXME: use something more secure here - self.local_seq_no = TcpSeqNumber(-repr.seq_number.0); + self.local_seq_no = TcpSeqNumber(!repr.seq_number.0); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; self.remote_has_sack = repr.sack_permitted; @@ -2489,7 +2489,7 @@ mod test { port: REMOTE_PORT, }; const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000); - const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10000); + const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10001); const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, From 8760cbb03a48134ebd160ddc883ce2d9b4993252 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 5 Oct 2021 21:33:31 +0200 Subject: [PATCH 194/566] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 327888054..35d556428 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # smoltcp +[![docs.rs](https://docs.rs/smoltcp/badge.svg)](https://docs.rs/smoltcp) +[![crates.io](https://img.shields.io/crates/v/smoltcp.svg)](https://crates.io/crates/smoltcp) +[![crates.io](https://img.shields.io/crates/d/smoltcp.svg)](https://crates.io/crates/smoltcp) +[![crates.io](https://img.shields.io/matrix/smoltcp:matrix.org)](https://matrix.to/#/#smoltcp:matrix.org) + _smoltcp_ is a standalone, event-driven TCP/IP stack that is designed for bare-metal, real-time systems. Its design goals are simplicity and robustness. Its design anti-goals include complicated compile-time computations, such as macro or type tricks, even From e6105161b0b932a568b3ba7e7011f7bf86b3f210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Gardstr=C3=B6m?= Date: Tue, 5 Oct 2021 21:50:50 +0200 Subject: [PATCH 195/566] make bors checks much simpler --- .github/bors.toml | 44 +------------------------------------- .github/workflows/test.yml | 6 ++++++ 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/.github/bors.toml b/.github/bors.toml index 5f14937cb..24c019188 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -1,48 +1,6 @@ status = [ + "tests", "fuzz", - "test (stable, default)", "clippy", "fmt", - "test (stable, std proto-ipv4)", - "test (stable, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", - "test (stable, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", - "test (stable, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", - "test (stable, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", - "test (stable, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", - "test (stable, std medium-ethernet proto-ipv6 socket-udp)", - "test (stable, std medium-ethernet proto-ipv6 socket-tcp)", - "test (stable, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", - "test (stable, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", - "test (stable, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-i...", - "test (1.53.0, default)", - "test (1.53.0, std proto-ipv4)", - "test (1.53.0, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", - "test (1.53.0, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", - "test (1.53.0, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", - "test (1.53.0, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", - "test (1.53.0, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", - "test (1.53.0, std medium-ethernet proto-ipv6 socket-udp)", - "test (1.53.0, std medium-ethernet proto-ipv6 socket-tcp)", - "test (1.53.0, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", - "test (1.53.0, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", - "test (1.53.0, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-i...", - "test (nightly, default)", - "test (nightly, std proto-ipv4)", - "test (nightly, std medium-ethernet phy-raw_socket proto-ipv6 socket-udp)", - "test (nightly, std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp)", - "test (nightly, std medium-ethernet proto-ipv4 proto-igmp socket-raw)", - "test (nightly, std medium-ethernet proto-ipv4 socket-udp socket-tcp)", - "test (nightly, std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp)", - "test (nightly, std medium-ethernet proto-ipv6 socket-udp)", - "test (nightly, std medium-ethernet proto-ipv6 socket-tcp)", - "test (nightly, std medium-ethernet proto-ipv4 socket-icmp socket-tcp)", - "test (nightly, std medium-ethernet proto-ipv6 socket-icmp socket-tcp)", - "test (nightly, std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-...", - "test (nightly, alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socke...", - "check (stable, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw...", - "check (stable, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto...", - "check (1.53.0, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw...", - "check (1.53.0, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto...", - "check (nightly, medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-ra...", - "check (nightly, defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp prot...", ] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b4e33e6b..7df5416be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,12 @@ on: name: Test jobs: + tests: + runs-on: ubuntu-20.04 + needs: [test, check] + steps: + - name: Done + run: exit 0 test: runs-on: ubuntu-20.04 continue-on-error: ${{ matrix.rust == 'nightly' }} From a4275a5966902e3fb364c67249f48e928b3f5cbc Mon Sep 17 00:00:00 2001 From: qiujiangkun Date: Sun, 4 Jul 2021 17:51:26 +0800 Subject: [PATCH 196/566] use micros in Instant and Duration --- examples/loopback.rs | 2 +- fuzz/fuzz_targets/tcp_headers.rs | 2 +- src/iface/neighbor.rs | 4 +- src/socket/meta.rs | 2 +- src/socket/mod.rs | 2 +- src/socket/tcp.rs | 4 +- src/time.rs | 123 ++++++++++++++++++++----------- 7 files changed, 90 insertions(+), 49 deletions(-) diff --git a/examples/loopback.rs b/examples/loopback.rs index 00e465146..15f13ac18 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -171,7 +171,7 @@ fn main() { } match iface.poll_delay(&socket_set, clock.elapsed()) { - Some(Duration { millis: 0 }) => debug!("resuming"), + Some(Duration::ZERO) => debug!("resuming"), Some(delay) => { debug!("sleeping for {} ms", delay); clock.advance(delay) diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index 24482ef86..a31c37f91 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -207,7 +207,7 @@ fuzz_target!(|data: &[u8]| { } match iface.poll_delay(&socket_set, clock.elapsed()) { - Some(Duration { millis: 0 }) => {} + Some(Duration::ZERO) => {} Some(delay) => clock.advance(delay), None => clock.advance(Duration::from_millis(1)), } diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 82f25c7fc..7fff0c74c 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -69,10 +69,10 @@ pub struct Cache<'a> { impl<'a> Cache<'a> { /// Minimum delay between discovery requests, in milliseconds. - pub(crate) const SILENT_TIME: Duration = Duration { millis: 1_000 }; + pub(crate) const SILENT_TIME: Duration = Duration::from_millis(1_000); /// Neighbor entry lifetime, in milliseconds. - pub(crate) const ENTRY_LIFETIME: Duration = Duration { millis: 60_000 }; + pub(crate) const ENTRY_LIFETIME: Duration = Duration::from_millis(60_000); /// Default number of entries in the cache before GC kicks in #[cfg(any(feature = "std", feature = "alloc"))] diff --git a/src/socket/meta.rs b/src/socket/meta.rs index 3d490dbb8..a6908a1d9 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -44,7 +44,7 @@ impl Meta { /// in milliseconds. /// /// See also `iface::NeighborCache::SILENT_TIME`. - pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 }; + pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(3_000); pub(crate) fn poll_at(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt where diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 76c442316..d1de47da7 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -217,6 +217,6 @@ impl Context { }, #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] ethernet_address: None, - now: Instant { millis: 0 }, + now: Instant::from_millis_const(0), }; } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index eca1dbb05..db24721a5 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -185,8 +185,8 @@ enum Timer { }, } -const ACK_DELAY_DEFAULT: Duration = Duration { millis: 10 }; -const CLOSE_DELAY: Duration = Duration { millis: 10_000 }; +const ACK_DELAY_DEFAULT: Duration = Duration::from_millis(10); +const CLOSE_DELAY: Duration = Duration::from_millis(10_000); impl Timer { fn new() -> Timer { diff --git a/src/time.rs b/src/time.rs index 648eafe7d..4866ac6fd 100644 --- a/src/time.rs +++ b/src/time.rs @@ -24,21 +24,39 @@ use core::{fmt, ops}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Instant { - pub millis: i64, + micros: i64, } impl Instant { + /// Create a new `Instant` from a number of microseconds. + pub fn from_micros>(micros: T) -> Instant { + Instant { + micros: micros.into(), + } + } + + pub const fn from_micros_const(micros: i64) -> Instant { + Instant { micros } + } + /// Create a new `Instant` from a number of milliseconds. pub fn from_millis>(millis: T) -> Instant { Instant { - millis: millis.into(), + micros: millis.into() * 1000, + } + } + + /// Create a new `Instant` from a number of milliseconds. + pub const fn from_millis_const(millis: i64) -> Instant { + Instant { + micros: millis * 1000, } } /// Create a new `Instant` from a number of seconds. pub fn from_secs>(secs: T) -> Instant { Instant { - millis: secs.into() * 1000, + micros: secs.into() * 1000000, } } @@ -56,25 +74,30 @@ impl Instant { /// The fractional number of milliseconds that have passed /// since the beginning of time. pub const fn millis(&self) -> i64 { - self.millis % 1000 + self.micros % 1000000 / 1000 } /// The fractional number of microseconds that have passed /// since the beginning of time. pub const fn micros(&self) -> i64 { - self.millis % 1000 * 1000 + self.micros % 1000000 } /// The number of whole seconds that have passed since the /// beginning of time. pub const fn secs(&self) -> i64 { - self.millis / 1000 + self.micros / 1000000 } /// The total number of milliseconds that have passed since /// the biginning of time. pub const fn total_millis(&self) -> i64 { - self.millis + self.micros / 1000 + } + /// The total number of milliseconds that have passed since + /// the biginning of time. + pub const fn total_micros(&self) -> i64 { + self.micros } } @@ -82,7 +105,7 @@ impl Instant { impl From<::std::time::Instant> for Instant { fn from(other: ::std::time::Instant) -> Instant { let elapsed = other.elapsed(); - Instant::from_millis((elapsed.as_secs() * 1_000) as i64 + elapsed.subsec_millis() as i64) + Instant::from_millis((elapsed.as_secs() * 1_000000) as i64 + elapsed.subsec_micros() as i64) } } @@ -92,14 +115,14 @@ impl From<::std::time::SystemTime> for Instant { let n = other .duration_since(::std::time::UNIX_EPOCH) .expect("start time must not be before the unix epoch"); - Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64) + Self::from_millis(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64) } } #[cfg(feature = "std")] impl From for ::std::time::SystemTime { fn from(val: Instant) -> Self { - ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(val.millis as u64) + ::std::time::UNIX_EPOCH + ::std::time::Duration::from_micros(val.micros as u64) } } @@ -113,13 +136,13 @@ impl ops::Add for Instant { type Output = Instant; fn add(self, rhs: Duration) -> Instant { - Instant::from_millis(self.millis + rhs.total_millis() as i64) + Instant::from_micros(self.micros + rhs.total_micros() as i64) } } impl ops::AddAssign for Instant { fn add_assign(&mut self, rhs: Duration) { - self.millis += rhs.total_millis() as i64; + self.micros += rhs.total_micros() as i64; } } @@ -127,13 +150,13 @@ impl ops::Sub for Instant { type Output = Instant; fn sub(self, rhs: Duration) -> Instant { - Instant::from_millis(self.millis - rhs.total_millis() as i64) + Instant::from_micros(self.micros - rhs.total_micros() as i64) } } impl ops::SubAssign for Instant { fn sub_assign(&mut self, rhs: Duration) { - self.millis -= rhs.total_millis() as i64; + self.micros -= rhs.total_micros() as i64; } } @@ -141,7 +164,7 @@ impl ops::Sub for Instant { type Output = Duration; fn sub(self, rhs: Instant) -> Duration { - Duration::from_millis((self.millis - rhs.millis).abs() as u64) + Duration::from_micros((self.micros - rhs.micros).abs() as u64) } } @@ -149,35 +172,53 @@ impl ops::Sub for Instant { #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Duration { - pub millis: u64, + micros: u64, } impl Duration { + pub const ZERO: Duration = Duration::from_micros(0); + /// Create a new `Duration` from a number of microeconds. + pub const fn from_micros(micros: u64) -> Duration { + Duration { micros } + } + /// Create a new `Duration` from a number of milliseconds. pub const fn from_millis(millis: u64) -> Duration { - Duration { millis } + Duration { + micros: millis * 1000, + } } /// Create a new `Instant` from a number of seconds. pub const fn from_secs(secs: u64) -> Duration { Duration { - millis: secs * 1000, + micros: secs * 1000000, } } /// The fractional number of milliseconds in this `Duration`. pub const fn millis(&self) -> u64 { - self.millis % 1000 + self.micros / 1000 % 1000 + } + + /// The fractional number of milliseconds in this `Duration`. + pub const fn micros(&self) -> u64 { + self.micros % 1000000 } /// The number of whole seconds in this `Duration`. pub const fn secs(&self) -> u64 { - self.millis / 1000 + self.micros / 1000000 } /// The total number of milliseconds in this `Duration`. pub const fn total_millis(&self) -> u64 { - self.millis + self.micros / 1000 + } + + /// The total number of microseconds in this `Duration`. + pub const fn total_micros(&self) -> u64 { + self.micros } } @@ -191,13 +232,13 @@ impl ops::Add for Duration { type Output = Duration; fn add(self, rhs: Duration) -> Duration { - Duration::from_millis(self.millis + rhs.total_millis()) + Duration::from_micros(self.micros + rhs.total_micros()) } } impl ops::AddAssign for Duration { fn add_assign(&mut self, rhs: Duration) { - self.millis += rhs.total_millis(); + self.micros += rhs.total_micros(); } } @@ -205,9 +246,9 @@ impl ops::Sub for Duration { type Output = Duration; fn sub(self, rhs: Duration) -> Duration { - Duration::from_millis( - self.millis - .checked_sub(rhs.total_millis()) + Duration::from_micros( + self.micros + .checked_sub(rhs.total_micros()) .expect("overflow when subtracting durations"), ) } @@ -215,9 +256,9 @@ impl ops::Sub for Duration { impl ops::SubAssign for Duration { fn sub_assign(&mut self, rhs: Duration) { - self.millis = self - .millis - .checked_sub(rhs.total_millis()) + self.micros = self + .micros + .checked_sub(rhs.total_micros()) .expect("overflow when subtracting durations"); } } @@ -226,13 +267,13 @@ impl ops::Mul for Duration { type Output = Duration; fn mul(self, rhs: u32) -> Duration { - Duration::from_millis(self.millis * rhs as u64) + Duration::from_micros(self.micros * rhs as u64) } } impl ops::MulAssign for Duration { fn mul_assign(&mut self, rhs: u32) { - self.millis *= rhs as u64; + self.micros *= rhs as u64; } } @@ -240,13 +281,13 @@ impl ops::Div for Duration { type Output = Duration; fn div(self, rhs: u32) -> Duration { - Duration::from_millis(self.millis / rhs as u64) + Duration::from_micros(self.micros / rhs as u64) } } impl ops::DivAssign for Duration { fn div_assign(&mut self, rhs: u32) { - self.millis /= rhs as u64; + self.micros /= rhs as u64; } } @@ -254,13 +295,13 @@ impl ops::Shl for Duration { type Output = Duration; fn shl(self, rhs: u32) -> Duration { - Duration::from_millis(self.millis << rhs) + Duration::from_micros(self.micros << rhs) } } impl ops::ShlAssign for Duration { fn shl_assign(&mut self, rhs: u32) { - self.millis <<= rhs; + self.micros <<= rhs; } } @@ -268,25 +309,25 @@ impl ops::Shr for Duration { type Output = Duration; fn shr(self, rhs: u32) -> Duration { - Duration::from_millis(self.millis >> rhs) + Duration::from_micros(self.micros >> rhs) } } impl ops::ShrAssign for Duration { fn shr_assign(&mut self, rhs: u32) { - self.millis >>= rhs; + self.micros >>= rhs; } } impl From<::core::time::Duration> for Duration { fn from(other: ::core::time::Duration) -> Duration { - Duration::from_millis(other.as_secs() * 1000 + other.subsec_millis() as u64) + Duration::from_micros(other.as_secs() * 1000000 + other.subsec_micros() as u64) } } impl From for ::core::time::Duration { fn from(val: Duration) -> Self { - ::core::time::Duration::from_millis(val.total_millis()) + ::core::time::Duration::from_micros(val.total_micros()) } } @@ -353,7 +394,7 @@ mod test { // std::ops::Mul assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286)); // std::ops::Div - assert_eq!(Duration::from_millis(53) / 4, Duration::from_millis(13)); + assert_eq!(Duration::from_millis(53) / 4, Duration::from_micros(13250)); } #[test] @@ -366,7 +407,7 @@ mod test { duration *= 4; assert_eq!(duration, Duration::from_millis(20936)); duration /= 5; - assert_eq!(duration, Duration::from_millis(4187)); + assert_eq!(duration, Duration::from_micros(4187200)); } #[test] From f2c5083a9feda11e1ceec9952be1b38a3a7f3b4d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 01:03:13 +0200 Subject: [PATCH 197/566] tcp: make match on (state, control, ack) more readable. --- src/socket/tcp.rs | 69 +++++++---------------------------------------- 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index db24721a5..f1a9ab7e5 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1279,17 +1279,10 @@ impl<'a> TcpSocket<'a> { let control_len = (sent_syn as usize) + (sent_fin as usize); // Reject unacceptable acknowledgements. - match (self.state, repr) { + match (self.state, repr.control, repr.ack_number) { // An RST received in response to initial SYN is acceptable if it acknowledges // the initial SYN. - ( - State::SynSent, - &TcpRepr { - control: TcpControl::Rst, - ack_number: None, - .. - }, - ) => { + (State::SynSent, TcpControl::Rst, None) => { net_debug!( "{}:{}:{}: unacceptable RST (expecting RST|ACK) \ in response to initial SYN", @@ -1299,14 +1292,7 @@ impl<'a> TcpSocket<'a> { ); return Err(Error::Dropped); } - ( - State::SynSent, - &TcpRepr { - control: TcpControl::Rst, - ack_number: Some(ack_number), - .. - }, - ) => { + (State::SynSent, TcpControl::Rst, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!( "{}:{}:{}: unacceptable RST|ACK in response to initial SYN", @@ -1318,35 +1304,13 @@ impl<'a> TcpSocket<'a> { } } // Any other RST need only have a valid sequence number. - ( - _, - &TcpRepr { - control: TcpControl::Rst, - .. - }, - ) => (), + (_, TcpControl::Rst, _) => (), // The initial SYN cannot contain an acknowledgement. - ( - State::Listen, - &TcpRepr { - ack_number: None, .. - }, - ) => (), + (State::Listen, _, None) => (), // This case is handled above. - ( - State::Listen, - &TcpRepr { - ack_number: Some(_), - .. - }, - ) => unreachable!(), + (State::Listen, _, Some(_)) => unreachable!(), // Every packet after the initial SYN must be an acknowledgement. - ( - _, - &TcpRepr { - ack_number: None, .. - }, - ) => { + (_, _, None) => { net_debug!( "{}:{}:{}: expecting an ACK", self.meta.handle, @@ -1356,14 +1320,7 @@ impl<'a> TcpSocket<'a> { return Err(Error::Dropped); } // SYN|ACK in the SYN-SENT state must have the exact ACK number. - ( - State::SynSent, - &TcpRepr { - control: TcpControl::Syn, - ack_number: Some(ack_number), - .. - }, - ) => { + (State::SynSent, TcpControl::Syn, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!( "{}:{}:{}: unacceptable SYN|ACK in response to initial SYN", @@ -1375,7 +1332,7 @@ impl<'a> TcpSocket<'a> { } } // Anything else in the SYN-SENT state is invalid. - (State::SynSent, _) => { + (State::SynSent, _, _) => { net_debug!( "{}:{}:{}: expecting a SYN|ACK", self.meta.handle, @@ -1386,13 +1343,7 @@ impl<'a> TcpSocket<'a> { return Err(Error::Dropped); } // Every acknowledgement must be for transmitted but unacknowledged data. - ( - _, - &TcpRepr { - ack_number: Some(ack_number), - .. - }, - ) => { + (_, _, Some(ack_number)) => { let unacknowledged = self.tx_buffer.len() + control_len; // Acceptable ACK range (both inclusive) From c9bdbf3b25250622f95fc07aa5bba178c71d3be5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 01:24:01 +0200 Subject: [PATCH 198/566] tcp: clarify comment --- src/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f1a9ab7e5..df471fa0f 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1307,7 +1307,7 @@ impl<'a> TcpSocket<'a> { (_, TcpControl::Rst, _) => (), // The initial SYN cannot contain an acknowledgement. (State::Listen, _, None) => (), - // This case is handled above. + // This case is handled in `accepts()`. (State::Listen, _, Some(_)) => unreachable!(), // Every packet after the initial SYN must be an acknowledgement. (_, _, None) => { From f6a738072367ce082ded2acb744ecd747c0bf040 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 01:52:57 +0200 Subject: [PATCH 199/566] tcp: rate-limit challenge ACKs. This fixes infinite-packet-loop issues where two peers have desynced and both think the other's sequence numbers are wrong. Found with cargo-fuzz. --- src/socket/tcp.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index df471fa0f..f7421ca18 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -382,6 +382,9 @@ pub struct TcpSocket<'a> { /// ACK or window updates (ie, no data) won't be sent until expiry. ack_delay_timer: AckDelayTimer, + /// Used for rate-limiting: No more challenge ACKs will be sent until this instant. + challenge_ack_timer: Instant, + /// Nagle's Algorithm enabled. nagle: bool, @@ -443,6 +446,7 @@ impl<'a> TcpSocket<'a> { local_rx_dup_acks: 0, ack_delay: Some(ACK_DELAY_DEFAULT), ack_delay_timer: AckDelayTimer::Idle, + challenge_ack_timer: Instant::from_secs(0), nagle: true, #[cfg(feature = "async")] @@ -573,7 +577,7 @@ impl<'a> TcpSocket<'a> { /// Set the keep-alive interval. /// - /// An idle socket with a keep-alive interval set will transmit a "challenge ACK" packet + /// An idle socket with a keep-alive interval set will transmit a "keep-alive ACK" packet /// every time it receives no communication during that interval. As a result, three things /// may happen: /// @@ -666,6 +670,8 @@ impl<'a> TcpSocket<'a> { self.remote_last_ts = None; self.ack_delay = Some(ACK_DELAY_DEFAULT); self.ack_delay_timer = AckDelayTimer::Idle; + self.challenge_ack_timer = Instant::from_secs(0); + self.nagle = true; #[cfg(feature = "async")] @@ -1223,6 +1229,22 @@ impl<'a> TcpSocket<'a> { (ip_reply_repr, reply_repr) } + fn challenge_ack_reply( + &mut self, + cx: &Context, + ip_repr: &IpRepr, + repr: &TcpRepr, + ) -> Option<(IpRepr, TcpRepr<'static>)> { + if cx.now < self.challenge_ack_timer { + return None; + } + + // Rate-limit to 1 per second max. + self.challenge_ack_timer = cx.now + Duration::from_secs(1); + + return Some(self.ack_reply(ip_repr, repr)); + } + pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &TcpRepr) -> bool { if self.state == State::Closed { return false; @@ -1378,7 +1400,7 @@ impl<'a> TcpSocket<'a> { ack_min, ack_max ); - return Ok(Some(self.ack_reply(ip_repr, repr))); + return Ok(self.challenge_ack_reply(cx, ip_repr, repr)); } } } @@ -1444,7 +1466,7 @@ impl<'a> TcpSocket<'a> { self.timer.set_for_close(cx.now); } - return Ok(Some(self.ack_reply(ip_repr, repr))); + return Ok(self.challenge_ack_reply(cx, ip_repr, repr)); } } } @@ -3949,6 +3971,35 @@ mod test { })) ); assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); + + // Challenge ACKs are rate-limited, we don't get a second one immediately. + send!( + s, + time 100, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 256, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }, + Ok(None) + ); + + // If we wait a bit, we do get a new one. + send!( + s, + time 2000, + TcpRepr { + seq_number: REMOTE_SEQ + 1 + 256, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + ..RECV_TEMPL + })) + ); + assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); } #[test] @@ -4127,6 +4178,7 @@ mod test { // See https://github.com/smoltcp-rs/smoltcp/issues/338 send!( s, + time 2000, TcpRepr { control: TcpControl::Rst, seq_number: REMOTE_SEQ, // Wrong seq From 4be331f72741d6ae5249cdfb15223c0613ba80fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 02:25:11 +0200 Subject: [PATCH 200/566] tcp: reply with RST to invalid ACKs in SynReceived state. This mirrors the Linux behavior, and helps cleanup desynced state. --- src/socket/tcp.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f7421ca18..636bd38d0 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1364,6 +1364,18 @@ impl<'a> TcpSocket<'a> { self.abort(); return Err(Error::Dropped); } + // ACK in the SYN-RECEIVED state must have the exact ACK number, or we RST it. + (State::SynReceived, _, Some(ack_number)) => { + if ack_number != self.local_seq_no + 1 { + net_debug!( + "{}:{}:{}: unacceptable ACK in response to SYN|ACK", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Ok(Some(Self::rst_reply(ip_repr, repr))); + } + } // Every acknowledgement must be for transmitted but unacknowledged data. (_, _, Some(ack_number)) => { let unacknowledged = self.tx_buffer.len() + control_len; @@ -3040,7 +3052,13 @@ mod test { ack_number: Some(LOCAL_SEQ), // wrong ..SEND_TEMPL }, - Err(Error::Dropped) + Ok(Some(TcpRepr { + control: TcpControl::Rst, + seq_number: LOCAL_SEQ, + ack_number: None, + window_len: 0, + ..RECV_TEMPL + })) ); assert_eq!(s.state, State::SynReceived); } @@ -3065,11 +3083,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 2), // wrong ..SEND_TEMPL }, - // TODO is this correct? probably not Ok(Some(TcpRepr { - control: TcpControl::None, - seq_number: LOCAL_SEQ + 1, - ack_number: Some(REMOTE_SEQ + 1), + control: TcpControl::Rst, + seq_number: LOCAL_SEQ + 2, + ack_number: None, + window_len: 0, ..RECV_TEMPL })) ); From eae480525b3728f46e085973c437f723c29c45d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 02:36:23 +0200 Subject: [PATCH 201/566] tcp: do not abort socket when receiving invalid packets. This matches the Linux behavior. --- src/socket/tcp.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 636bd38d0..cde9397e2 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1361,7 +1361,6 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - self.abort(); return Err(Error::Dropped); } // ACK in the SYN-RECEIVED state must have the exact ACK number, or we RST it. @@ -3479,7 +3478,7 @@ mod test { }, Err(Error::Dropped) ); - assert_eq!(s.state, State::Closed); + assert_eq!(s.state, State::SynSent); } #[test] From 14971876bde4388e321e7b41e21ecd4a2b3deb89 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 02:44:39 +0200 Subject: [PATCH 202/566] tcp: reply with RST to invalid SYNACKs in SynReceived state. This matches the Linux behavior. --- src/socket/tcp.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index cde9397e2..d4adfbe85 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1165,7 +1165,7 @@ impl<'a> TcpSocket<'a> { // of why we sometimes send an RST and sometimes an RST|ACK reply_repr.control = TcpControl::Rst; reply_repr.seq_number = repr.ack_number.unwrap_or_default(); - if repr.control == TcpControl::Syn { + if repr.control == TcpControl::Syn && repr.ack_number.is_none() { reply_repr.ack_number = Some(repr.seq_number + repr.segment_len()); } @@ -1350,7 +1350,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - return Err(Error::Dropped); + return Ok(Some(Self::rst_reply(ip_repr, repr))); } } // Anything else in the SYN-SENT state is invalid. @@ -3414,7 +3414,13 @@ mod test { window_scale: Some(0), ..SEND_TEMPL }, - Err(Error::Dropped) + Ok(Some(TcpRepr { + control: TcpControl::Rst, + seq_number: LOCAL_SEQ, + ack_number: None, + window_len: 0, + ..RECV_TEMPL + })) ); assert_eq!(s.state, State::SynSent); } From 6210612be047ee706ac729015cdbc2581e6ae9a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 1 Oct 2021 21:50:45 +0200 Subject: [PATCH 203/566] arp: Do not fill cache from random packets. On paper this looks great, and in a sane network it should work. However the world out there is full of horribly broken, screwed up networks, which *of course* ruin this. I've seen a customer's network where the router is IP 192.168.1.1, MAC addr xx:03. However, every 1 minute the router broadcasts some "mikrotik discovery" UDP garbage with source IP 192.168.1.1, source MAC addr xx:02 (one less!). This accidentally poisons smoltcp's ARP cache, which then sends all traffic for the default gateway to xx:02, which unsurprisingly blackholes it. And, of course, the broadcast is every 1min and the ARP cache lifetime is 1min. This means the cache is almsot all the time poisoned, and the smoltcp device barely works. Fantastic. Screw you mikrotik. --- src/iface/interface.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a24cdaafe..d2052f1e3 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -952,43 +952,12 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => { let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; - if eth_frame.src_addr().is_unicast() && ipv4_packet.src_addr().is_unicast() { - // 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(), - cx.now, - ); - } - } - self.process_ipv4(cx, sockets, &ipv4_packet) .map(|o| o.map(EthernetPacket::Ip)) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; - if eth_frame.src_addr().is_unicast() && ipv6_packet.src_addr().is_unicast() { - // 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, cx.now) - .found() - { - self.neighbor_cache.as_mut().unwrap().fill( - ip_addr, - eth_frame.src_addr(), - cx.now, - ); - } - } - self.process_ipv6(cx, sockets, &ipv6_packet) .map(|o| o.map(EthernetPacket::Ip)) } From 83c016522c825e5d7486c34ae86cd48c93bb1919 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 04:01:43 +0200 Subject: [PATCH 204/566] arp: fill cache only for ARP packets directed at us. - Mirrors what Linux does, so will hopefully reduce problems in broken networks. - it can actually increase performance: for small ARP caches, it'll reduce the amount of entries that we're not going to use, increasing the chances of the ones that we actually use to stay in the cache. Fixes #537 --- src/iface/interface.rs | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index d2052f1e3..c4d48e80c 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -999,9 +999,6 @@ impl<'a> InterfaceInner<'a> { let arp_repr = ArpRepr::parse(&arp_packet)?; match arp_repr { - // Respond to ARP requests aimed at us, and fill the ARP cache from all ARP - // requests and replies, to minimize the chance that we have to perform - // an explicit ARP request. ArpRepr::EthernetIpv4 { operation, source_hardware_addr, @@ -1009,19 +1006,28 @@ impl<'a> InterfaceInner<'a> { target_protocol_addr, .. } => { - if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() { - self.neighbor_cache.as_mut().unwrap().fill( - source_protocol_addr.into(), - source_hardware_addr, - timestamp, - ); - } else { - // Discard packets with non-unicast source addresses. - net_debug!("non-unicast source address"); + // Only process ARP packets for us. + if !self.has_ip_addr(target_protocol_addr) { + return Ok(None); + } + + // Discard packets with non-unicast source addresses. + if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { + net_debug!("arp: non-unicast source address"); return Err(Error::Malformed); } - if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { + // Fill the ARP cache from any ARP packet aimed at us (both request or response). + // We fill from requests too because if someone is requesting our address they + // are probably going to talk to us, so we avoid having to request their address + // when we later reply to them. + self.neighbor_cache.as_mut().unwrap().fill( + source_protocol_addr.into(), + source_hardware_addr, + timestamp, + ); + + if operation == ArpOperation::Request { Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: self.ethernet_addr.unwrap(), @@ -2854,7 +2860,7 @@ mod test { Ok(None) ); - // Ensure the address of the requestor was entered in the cache + // Ensure the address of the requestor was NOT entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( &cx, @@ -2862,7 +2868,7 @@ mod test { &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), &IpAddress::Ipv4(remote_ip_addr) ), - Ok((remote_hw_addr, MockTxToken)) + Err(Error::Unaddressable) ); } From 6951ccf2974fd42d5bd158fb40eb4faa562dff90 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 04:06:32 +0200 Subject: [PATCH 205/566] arp; reject packets with source address not in our network. Fixes #536 --- src/iface/interface.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index c4d48e80c..8a5e46d53 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1017,6 +1017,11 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Malformed); } + if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) { + net_debug!("arp: source IP address not in same network as us"); + return Err(Error::Malformed); + } + // Fill the ARP cache from any ARP packet aimed at us (both request or response). // We fill from requests too because if someone is requesting our address they // are probably going to talk to us, so we avoid having to request their address From db7b2249edf67ea128dd349ab1dd1b2bead173b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 6 Oct 2021 04:07:45 +0200 Subject: [PATCH 206/566] arp: ignore ARP packets that are not REQUEST or RESPONSE. --- src/iface/interface.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 8a5e46d53..0440cc6b8 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1011,6 +1011,12 @@ impl<'a> InterfaceInner<'a> { return Ok(None); } + // Only process REQUEST and RESPONSE. + if let ArpOperation::Unknown(_) = operation { + net_debug!("arp: unknown operation code"); + return Err(Error::Malformed); + } + // Discard packets with non-unicast source addresses. if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { net_debug!("arp: non-unicast source address"); From 79c40a9921312068455cb3a47839f548c3decba0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 04:54:52 +0200 Subject: [PATCH 207/566] time: fix incorrect conversion from std --- src/time.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time.rs b/src/time.rs index 4866ac6fd..267fb280d 100644 --- a/src/time.rs +++ b/src/time.rs @@ -105,7 +105,7 @@ impl Instant { impl From<::std::time::Instant> for Instant { fn from(other: ::std::time::Instant) -> Instant { let elapsed = other.elapsed(); - Instant::from_millis((elapsed.as_secs() * 1_000000) as i64 + elapsed.subsec_micros() as i64) + Instant::from_micros((elapsed.as_secs() * 1_000000) as i64 + elapsed.subsec_micros() as i64) } } @@ -115,7 +115,7 @@ impl From<::std::time::SystemTime> for Instant { let n = other .duration_since(::std::time::UNIX_EPOCH) .expect("start time must not be before the unix epoch"); - Self::from_millis(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64) + Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64) } } From 67c3b3b7b1aa5456033171fa5f7b66c728f4ad96 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Oct 2021 20:19:23 +0200 Subject: [PATCH 208/566] Add rand module. On `std` targets, `OsRng` is used by default. The user can supply a custom impl by enabling the `rand-custom-impl` Cargo feature and using the `rand_custom_impl!()` macro. Specifying a custom impl is mandatory when `std` is not enabled. --- .github/workflows/test.yml | 6 ++-- Cargo.toml | 4 ++- examples/loopback.rs | 8 +++++ src/lib.rs | 4 +++ src/rand.rs | 67 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/rand.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7df5416be..9f3ca51c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: include: # Test alloc feature which requires nightly. - rust: nightly - features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + features: alloc rand-custom-impl medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -73,8 +73,8 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 54eb6c3b4..170760a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.2.0", optional = true } +rand_core = { version = "0.6.3", optional = true, default-features = false } [dev-dependencies] env_logger = "0.5" @@ -30,9 +31,10 @@ rand = "0.3" url = "1.0" [features] -std = ["managed/std"] +std = ["managed/std", "rand_core/std"] alloc = ["managed/alloc"] verbose = [] +rand-custom-impl = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] "phy-raw_socket" = ["std", "libc", "medium-ethernet"] diff --git a/examples/loopback.rs b/examples/loopback.rs index 15f13ac18..dfbb77346 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -37,6 +37,14 @@ mod mock { self.0.get() } } + + struct Rand; + smoltcp::rand_custom_impl!(Rand); + impl smoltcp::Rand for Rand { + fn rand_bytes(buf: &mut [u8]) { + buf.fill(0x42); + } + } } #[cfg(feature = "std")] diff --git a/src/lib.rs b/src/lib.rs index 353708814..0d0d1ce71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,10 @@ use core::fmt; mod macros; mod parsers; +mod rand; +#[cfg(feature = "rand-custom-impl")] +pub use crate::rand::Rand; + pub mod iface; pub mod phy; #[cfg(feature = "socket")] diff --git a/src/rand.rs b/src/rand.rs new file mode 100644 index 000000000..93f5a009d --- /dev/null +++ b/src/rand.rs @@ -0,0 +1,67 @@ +#![allow(unsafe_code)] +#![allow(unused)] + +#[cfg(not(any(test, feature = "std", feature = "rand-custom-impl")))] +compile_error!("None of the Cargo features `std` or `rand-custom-impl` is enabled. smoltcp needs a `rand` implementation to work. If your target supports `std`, enable the `std` feature to use the OS's RNG. Otherwise, you must enable the `rand-custom-impl` Cargo feature, and supply your own custom implementation using the `smoltcp::rand_custom_impl!()` macro"); + +pub fn rand_u32() -> u32 { + let mut val = [0; 4]; + rand_bytes(&mut val); + u32::from_ne_bytes(val) +} + +/// Fill `buf` with random bytes. +pub fn rand_bytes(buf: &mut [u8]) { + extern "Rust" { + fn _smoltcp_rand(buf: &mut [u8]); + } + + unsafe { _smoltcp_rand(buf) } +} + +/// Methods required for a custom rand implementation. +/// +/// This trait is not intended to be used directly, just to supply a custom rand implementation to smoltcp. +#[cfg(feature = "rand-custom-impl")] +pub trait Rand { + /// Fill `buf` with random bytes. + fn rand_bytes(buf: &mut [u8]); +} + +/// Set the custom rand implementation. +/// +/// # Example +/// +/// ``` +/// struct Rand; +/// smoltcp::rand_custom_impl!(Rand); +/// impl smoltcp::Rand for Rand { +/// fn rand_bytes(buf: &mut [u8]) { +/// // TODO +/// } +/// } +/// +#[macro_export] +#[cfg(feature = "rand-custom-impl")] +macro_rules! rand_custom_impl { + ($t: ty) => { + #[no_mangle] + fn _smoltcp_rand(buf: &mut [u8]) { + <$t as $crate::Rand>::rand_bytes(buf) + } + }; +} + +#[cfg(all(feature = "std", not(feature = "rand-custom-impl"), not(test)))] +#[no_mangle] +fn _smoltcp_rand(buf: &mut [u8]) { + use rand_core::RngCore; + + rand_core::OsRng.fill_bytes(buf) +} + +#[cfg(test)] +#[no_mangle] +fn _smoltcp_rand(buf: &mut [u8]) { + panic!("Rand should not be used when testing"); +} From 52e087e451897a61a896d244838435aacf6e2213 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Oct 2021 20:21:40 +0200 Subject: [PATCH 209/566] tcp: Make initial sequence number random. --- src/socket/tcp.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index d4adfbe85..e9885da8f 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -56,11 +56,6 @@ impl fmt::Display for State { } } -/// Initial sequence number. This used to be 0, but some servers don't behave correctly -/// with that, so we use a non-zero starting sequence number. TODO: randomize instead. -/// https://github.com/smoltcp-rs/smoltcp/issues/489 -const INITIAL_SEQ_NO: TcpSeqNumber = TcpSeqNumber(42); - // Conservative initial RTT estimate. const RTTE_INITIAL_RTT: u32 = 300; const RTTE_INITIAL_DEV: u32 = 100; @@ -430,7 +425,7 @@ impl<'a> TcpSocket<'a> { listen_address: IpAddress::default(), local_endpoint: IpEndpoint::default(), remote_endpoint: IpEndpoint::default(), - local_seq_no: INITIAL_SEQ_NO, + local_seq_no: TcpSeqNumber::default(), remote_seq_no: TcpSeqNumber::default(), remote_last_seq: TcpSeqNumber::default(), remote_last_ack: None, @@ -658,7 +653,7 @@ impl<'a> TcpSocket<'a> { self.listen_address = IpAddress::default(); self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); - self.local_seq_no = INITIAL_SEQ_NO; + self.local_seq_no = TcpSeqNumber::default(); self.remote_seq_no = TcpSeqNumber::default(); self.remote_last_seq = TcpSeqNumber::default(); self.remote_last_ack = None; @@ -751,18 +746,24 @@ impl<'a> TcpSocket<'a> { ..local_endpoint }; - // Carry over the local sequence number. - let local_seq_no = self.local_seq_no; - self.reset(); self.local_endpoint = local_endpoint; self.remote_endpoint = remote_endpoint; - self.local_seq_no = local_seq_no; - self.remote_last_seq = local_seq_no; self.set_state(State::SynSent); + + let seq = Self::random_seq_no(); + self.local_seq_no = seq; + self.remote_last_seq = seq; Ok(()) } + fn random_seq_no() -> TcpSeqNumber { + #[cfg(test)] + return TcpSeqNumber(10000); + #[cfg(not(test))] + return TcpSeqNumber(crate::rand::rand_u32() as i32); + } + /// Close the transmit half of the full-duplex connection. /// /// Note that there is no corresponding function for the receive half of the full-duplex @@ -1575,8 +1576,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); - // FIXME: use something more secure here - self.local_seq_no = TcpSeqNumber(!repr.seq_number.0); + self.local_seq_no = Self::random_seq_no(); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; self.remote_has_sack = repr.sack_permitted; From 22e7233bec8c4425b130aae190177e4df35d63ea Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 13:59:16 +0200 Subject: [PATCH 210/566] wire/dhcp: BROADCAST flag is MSB (0x8000), not LSB (0x0001). This fixes DHCP on Linksys WRT1900AC. With 0x0001, it would not reply to DISCOVERs at all, probably because seeing an unknown reserved flag being set. With 0x8000, it works fine. --- src/wire/dhcpv4.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 8ac8e5923..de281b31a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -455,7 +455,7 @@ impl> Packet { /// Returns true if the broadcast flag is set. pub fn broadcast_flag(&self) -> bool { let field = &self.buffer.as_ref()[field::FLAGS]; - NetworkEndian::read_u16(field) & 0b1 == 0b1 + NetworkEndian::read_u16(field) & 0x8000 == 0x8000 } } @@ -578,7 +578,7 @@ impl + AsMut<[u8]>> Packet { /// Sets the broadcast flag to the specified value. pub fn set_broadcast_flag(&mut self, value: bool) { let field = &mut self.buffer.as_mut()[field::FLAGS]; - NetworkEndian::write_u16(field, if value { 1 } else { 0 }); + NetworkEndian::write_u16(field, if value { 0x8000 } else { 0 }); } } From 1a3c305b69251b4afc4a639199028026b074da86 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 14:02:27 +0200 Subject: [PATCH 211/566] wire/dhcp: use bitflags for the FLAGS field. --- src/wire/dhcpv4.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index de281b31a..bddf1448c 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -1,5 +1,6 @@ // See https://tools.ietf.org/html/rfc2131 for the DHCP specification. +use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use crate::wire::arp::Hardware; @@ -34,6 +35,12 @@ enum_with_unknown! { } } +bitflags! { + pub struct Flags: u16 { + const BROADCAST = 0b1000_0000_0000_0000; + } +} + impl MessageType { fn opcode(&self) -> OpCode { match *self { @@ -452,10 +459,9 @@ impl> Packet { Ipv4Address::from_bytes(field) } - /// Returns true if the broadcast flag is set. - pub fn broadcast_flag(&self) -> bool { + pub fn flags(&self) -> Flags { let field = &self.buffer.as_ref()[field::FLAGS]; - NetworkEndian::read_u16(field) & 0x8000 == 0x8000 + Flags::from_bits_truncate(NetworkEndian::read_u16(field)) } } @@ -575,10 +581,10 @@ impl + AsMut<[u8]>> Packet { field.copy_from_slice(value.as_bytes()); } - /// Sets the broadcast flag to the specified value. - pub fn set_broadcast_flag(&mut self, value: bool) { + /// Sets the flags to the specified value. + pub fn set_flags(&mut self, val: Flags) { let field = &mut self.buffer.as_mut()[field::FLAGS]; - NetworkEndian::write_u16(field, if value { 0x8000 } else { 0 }); + NetworkEndian::write_u16(field, val.bits()); } } @@ -828,7 +834,7 @@ impl<'a> Repr<'a> { options = next_options; } - let broadcast = packet.broadcast_flag(); + let broadcast = packet.flags().contains(Flags::BROADCAST); Ok(Repr { transaction_id, @@ -870,7 +876,12 @@ impl<'a> Repr<'a> { packet.set_your_ip(self.your_ip); packet.set_server_ip(self.server_ip); packet.set_relay_agent_ip(self.relay_agent_ip); - packet.set_broadcast_flag(self.broadcast); + + let mut flags = Flags::empty(); + if self.broadcast { + flags |= Flags::BROADCAST; + } + packet.set_flags(flags); { let mut options = packet.options_mut()?; @@ -1072,7 +1083,7 @@ mod test { packet.set_hops(0); packet.set_transaction_id(0x3d1d); packet.set_secs(0); - packet.set_broadcast_flag(false); + packet.set_flags(Flags::empty()); packet.set_client_ip(IP_NULL); packet.set_your_ip(IP_NULL); packet.set_server_ip(IP_NULL); From dc5cee7b1a84be3dfdf81dbf98736ea2bb2ca5d0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 14:03:50 +0200 Subject: [PATCH 212/566] socket/dhcp: do not set BROADCAST flag. Reasons: 1. We were already accidentally not setting the BROADCAST flag due to it being the wrong bit (see previous commit). 2. Major OSes don't set it. 3. rfc1542 section 3.1.1 states it's discouraged, and the issue it's supposed to workaround doesn't apply to smoltcp. Unfortunately, some client implementations are unable to receive such unicast IP datagrams until they know their own IP address (..) This addition to the protocol is a workaround for old host implementations. Such implementations SHOULD be modified so that they may receive unicast BOOTREPLY messages, thus making use of this workaround unnecessary. In general, the use of this mechanism is discouraged. --- src/socket/dhcpv4.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d93ae4f79..ee061a4d1 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -354,7 +354,7 @@ impl Dhcpv4Socket { router: None, subnet_mask: None, relay_agent_ip: Ipv4Address::UNSPECIFIED, - broadcast: true, + broadcast: false, requested_ip: None, client_identifier: Some(ethernet_addr), server_identifier: None, @@ -410,7 +410,6 @@ impl Dhcpv4Socket { } dhcp_repr.message_type = DhcpMessageType::Request; - dhcp_repr.broadcast = false; dhcp_repr.requested_ip = Some(state.requested_ip); dhcp_repr.server_identifier = Some(state.server.identifier); @@ -445,7 +444,6 @@ impl Dhcpv4Socket { ipv4_repr.dst_addr = state.server.address; dhcp_repr.message_type = DhcpMessageType::Request; dhcp_repr.client_ip = state.config.address.address(); - dhcp_repr.broadcast = false; net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); From 52e174f2e2c54bf697455401c938996bcaff0009 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 14:10:08 +0200 Subject: [PATCH 213/566] socket/dhcp: Use random transaction_id instead of sequential. This is a minor security improvement against blind packet spoofing, since it adds more entropy to the packets. --- src/socket/dhcpv4.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index ee061a4d1..19ee093be 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -340,9 +340,9 @@ impl Dhcpv4Socket { // 0x0f * 4 = 60 bytes. const MAX_IPV4_HEADER_LEN: usize = 60; - // We don't directly increment transaction_id because sending the packet + // We don't directly modify self.transaction_id because sending the packet // may fail. We only want to update state after succesfully sending. - let next_transaction_id = self.transaction_id + 1; + let next_transaction_id = crate::rand::rand_u32(); let mut dhcp_repr = DhcpRepr { message_type: DhcpMessageType::Discover, From 76d2fc139c0cabd210ffe44b008195805ac57657 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 19:41:10 +0200 Subject: [PATCH 214/566] socket/dhcp: log incoming reprs as well as outgoing. --- src/socket/dhcpv4.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 19ee093be..29f644fcc 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -203,10 +203,10 @@ impl Dhcpv4Socket { }; net_debug!( - "DHCP recv {:?} from {} ({})", + "DHCP recv {:?} from {}: {:?}", dhcp_repr.message_type, src_ip, - server_identifier + dhcp_repr ); match (&mut self.state, dhcp_repr.message_type) { From 28024a1249d7fe94641a401c426e848659a2ced7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 20:05:25 +0200 Subject: [PATCH 215/566] socket/dhcp: add basic test --- src/socket/dhcpv4.rs | 299 ++++++++++++++++++++++++++++++++++++++++++- src/socket/mod.rs | 4 +- 2 files changed, 301 insertions(+), 2 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 29f644fcc..1af3ab538 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -100,6 +100,8 @@ enum ClientState { } /// Return value for the `Dhcpv4Socket::poll` function +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Event<'a> { /// Configuration has been lost (for example, the lease has expired) Deconfigured, @@ -328,6 +330,16 @@ impl Dhcpv4Socket { Some((config, renew_at, expires_at)) } + #[cfg(not(test))] + fn random_transaction_id() -> u32 { + crate::rand::rand_u32() + } + + #[cfg(test)] + fn random_transaction_id() -> u32 { + 0x12345678 + } + pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>, @@ -342,7 +354,7 @@ impl Dhcpv4Socket { // We don't directly modify self.transaction_id because sending the packet // may fail. We only want to update state after succesfully sending. - let next_transaction_id = crate::rand::rand_u32(); + let next_transaction_id = Self::random_transaction_id(); let mut dhcp_repr = DhcpRepr { message_type: DhcpMessageType::Discover, @@ -504,3 +516,288 @@ impl<'a> From for Socket<'a> { Socket::Dhcpv4(val) } } + +#[cfg(test)] +mod test { + + use super::*; + use crate::wire::EthernetAddress; + + // =========================================================================================// + // Helper functions + + fn send( + socket: &mut Dhcpv4Socket, + timestamp: Instant, + (ip_repr, udp_repr, dhcp_repr): (Ipv4Repr, UdpRepr, DhcpRepr), + ) -> Result<()> { + net_trace!("send: {:?}", ip_repr); + net_trace!(" {:?}", udp_repr); + net_trace!(" {:?}", dhcp_repr); + + let mut payload = vec![0; dhcp_repr.buffer_len()]; + dhcp_repr + .emit(&mut DhcpPacket::new_unchecked(&mut payload)) + .unwrap(); + + let mut cx = Context::DUMMY.clone(); + cx.now = timestamp; + socket.process(&cx, &ip_repr, &udp_repr, &payload) + } + + fn recv(socket: &mut Dhcpv4Socket, timestamp: Instant, mut f: F) + where + F: FnMut(Result<(Ipv4Repr, UdpRepr, DhcpRepr)>), + { + let mut cx = Context::DUMMY.clone(); + cx.now = timestamp; + let result = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| { + assert_eq!(ip_repr.protocol, IpProtocol::Udp); + assert_eq!( + ip_repr.payload_len, + udp_repr.header_len() + dhcp_repr.buffer_len() + ); + + // We validated the payload len, change it to 0 to make equality testing easier + ip_repr.payload_len = 0; + + net_trace!("recv: {:?}", ip_repr); + net_trace!(" {:?}", udp_repr); + net_trace!(" {:?}", dhcp_repr); + Ok(f(Ok((ip_repr, udp_repr, dhcp_repr)))) + }); + match result { + Ok(()) => (), + Err(e) => f(Err(e)), + } + } + + macro_rules! send { + ($socket:ident, $repr:expr) => + (send!($socket, time 0, $repr)); + ($socket:ident, $repr:expr, $result:expr) => + (send!($socket, time 0, $repr, $result)); + ($socket:ident, time $time:expr, $repr:expr) => + (send!($socket, time $time, $repr, Ok(( )))); + ($socket:ident, time $time:expr, $repr:expr, $result:expr) => + (assert_eq!(send(&mut $socket, Instant::from_millis($time), $repr), $result)); + } + + macro_rules! recv { + ($socket:ident, [$( $repr:expr ),*]) => ({ + $( recv!($socket, Ok($repr)); )* + recv!($socket, Err(Error::Exhausted)) + }); + ($socket:ident, time $time:expr, [$( $repr:expr ),*]) => ({ + $( recv!($socket, time $time, Ok($repr)); )* + recv!($socket, time $time, Err(Error::Exhausted)) + }); + ($socket:ident, $result:expr) => + (recv!($socket, time 0, $result)); + ($socket:ident, time $time:expr, $result:expr) => + (recv(&mut $socket, Instant::from_millis($time), |result| { + assert_eq!(result, $result) + })); + } + + #[cfg(feature = "log")] + fn init_logger() { + struct Logger; + static LOGGER: Logger = Logger; + + impl log::Log for Logger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + println!("{}", record.args()); + } + + fn flush(&self) {} + } + + // If it fails, that just means we've already set it to the same value. + let _ = log::set_logger(&LOGGER); + log::set_max_level(log::LevelFilter::Trace); + + println!(); + } + + // =========================================================================================// + // Constants + + const TXID: u32 = 0x12345678; + + const MY_IP: Ipv4Address = Ipv4Address([192, 168, 1, 42]); + const SERVER_IP: Ipv4Address = Ipv4Address([192, 168, 1, 1]); + const DNS_IP_1: Ipv4Address = Ipv4Address([1, 1, 1, 1]); + const DNS_IP_2: Ipv4Address = Ipv4Address([1, 1, 1, 2]); + const DNS_IP_3: Ipv4Address = Ipv4Address([1, 1, 1, 3]); + const DNS_IPS: [Option; DHCP_MAX_DNS_SERVER_COUNT] = + [Some(DNS_IP_1), Some(DNS_IP_2), Some(DNS_IP_3)]; + const MASK_24: Ipv4Address = Ipv4Address([255, 255, 255, 0]); + + const MY_MAC: EthernetAddress = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); + + const IP_BROADCAST: Ipv4Repr = Ipv4Repr { + src_addr: Ipv4Address::UNSPECIFIED, + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Udp, + payload_len: 0, + hop_limit: 64, + }; + + const IP_RECV: Ipv4Repr = Ipv4Repr { + src_addr: SERVER_IP, + dst_addr: MY_IP, + protocol: IpProtocol::Udp, + payload_len: 0, + hop_limit: 64, + }; + + const IP_SEND: Ipv4Repr = Ipv4Repr { + src_addr: MY_IP, + dst_addr: SERVER_IP, + protocol: IpProtocol::Udp, + payload_len: 0, + hop_limit: 64, + }; + + const UDP_SEND: UdpRepr = UdpRepr { + src_port: 68, + dst_port: 67, + }; + const UDP_RECV: UdpRepr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + + const DHCP_DEFAULT: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Unknown(99), + transaction_id: TXID, + client_hardware_address: MY_MAC, + client_ip: Ipv4Address::UNSPECIFIED, + your_ip: Ipv4Address::UNSPECIFIED, + server_ip: Ipv4Address::UNSPECIFIED, + router: None, + subnet_mask: None, + relay_agent_ip: Ipv4Address::UNSPECIFIED, + broadcast: false, + requested_ip: None, + client_identifier: None, + server_identifier: None, + parameter_request_list: None, + dns_servers: None, + max_size: None, + lease_duration: None, + }; + + const DHCP_DISCOVER: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Discover, + client_identifier: Some(MY_MAC), + parameter_request_list: Some(&[1, 3, 6]), + max_size: Some(1432), + ..DHCP_DEFAULT + }; + + const DHCP_OFFER: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Offer, + server_ip: SERVER_IP, + server_identifier: Some(SERVER_IP), + + your_ip: MY_IP, + router: Some(SERVER_IP), + subnet_mask: Some(MASK_24), + dns_servers: Some(DNS_IPS), + lease_duration: Some(60), + + ..DHCP_DEFAULT + }; + + const DHCP_REQUEST: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Request, + client_identifier: Some(MY_MAC), + server_identifier: Some(SERVER_IP), + max_size: Some(1432), + + requested_ip: Some(MY_IP), + parameter_request_list: Some(&[1, 3, 6]), + ..DHCP_DEFAULT + }; + + const DHCP_ACK: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Ack, + server_ip: SERVER_IP, + server_identifier: Some(SERVER_IP), + + your_ip: MY_IP, + router: Some(SERVER_IP), + subnet_mask: Some(MASK_24), + dns_servers: Some(DNS_IPS), + lease_duration: Some(60), + + ..DHCP_DEFAULT + }; + + // =========================================================================================// + // Tests + + fn socket() -> Dhcpv4Socket { + #[cfg(feature = "log")] + init_logger(); + + let mut s = Dhcpv4Socket::new(); + assert_eq!(s.poll(), Some(Event::Deconfigured)); + s + } + + fn socket_bound() -> Dhcpv4Socket { + let mut s = socket(); + s.state = ClientState::Renewing(RenewState { + config: Config { + address: Ipv4Cidr::new(MY_IP, 24), + dns_servers: DNS_IPS, + router: Some(SERVER_IP), + }, + server: ServerInfo { + address: SERVER_IP, + identifier: SERVER_IP, + }, + renew_at: Instant::from_secs(30), + expires_at: Instant::from_secs(60), + }); + + s + } + + #[test] + fn test_bind() { + let mut s = socket(); + + recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + assert_eq!(s.poll(), None); + send!(s, (IP_RECV, UDP_RECV, DHCP_OFFER)); + assert_eq!(s.poll(), None); + recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + assert_eq!(s.poll(), None); + send!(s, (IP_RECV, UDP_RECV, DHCP_ACK)); + + assert_eq!( + s.poll(), + Some(Event::Configured(&Config { + address: Ipv4Cidr::new(MY_IP, 24), + dns_servers: DNS_IPS, + router: Some(SERVER_IP), + })) + ); + + match s.state { + ClientState::Renewing(r) => { + assert_eq!(r.renew_at, Instant::from_secs(30)); + assert_eq!(r.expires_at, Instant::from_secs(60)); + } + _ => panic!("Invalid state"), + } + } +} diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d1de47da7..d28bb2dcd 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -216,7 +216,9 @@ impl Context { max_transmission_unit: 1500, }, #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] - ethernet_address: None, + ethernet_address: Some(crate::wire::EthernetAddress([ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + ])), now: Instant::from_millis_const(0), }; } From 4e77831f8ee7193813e043a4cff442b878d46ddb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Oct 2021 23:33:25 +0200 Subject: [PATCH 216/566] socket/dhcp: add renew test --- src/socket/dhcpv4.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 1af3ab538..a1fe7178f 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -740,6 +740,18 @@ mod test { ..DHCP_DEFAULT }; + const DHCP_RENEW: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Request, + client_identifier: Some(MY_MAC), + // NO server_identifier in renew requests, only in first one! + client_ip: MY_IP, + max_size: Some(1432), + + requested_ip: None, + parameter_request_list: Some(&[1, 3, 6]), + ..DHCP_DEFAULT + }; + // =========================================================================================// // Tests @@ -800,4 +812,35 @@ mod test { _ => panic!("Invalid state"), } } + + #[test] + fn test_renew() { + let mut s = socket_bound(); + + recv!(s, []); + assert_eq!(s.poll(), None); + recv!(s, time 40_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + assert_eq!(s.poll(), None); + + match &s.state { + ClientState::Renewing(r) => { + // the expiration still hasn't been bumped, because + // we haven't received the ACK yet + assert_eq!(r.expires_at, Instant::from_secs(60)); + } + _ => panic!("Invalid state"), + } + + send!(s, time 40_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + assert_eq!(s.poll(), None); + + match &s.state { + ClientState::Renewing(r) => { + // NOW the expiration gets bumped + assert_eq!(r.renew_at, Instant::from_secs(40 + 30)); + assert_eq!(r.expires_at, Instant::from_secs(40 + 60)); + } + _ => panic!("Invalid state"), + } + } } From 9478327803260f668254ccb684affc54533fae2a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 15 Oct 2021 00:38:42 +0200 Subject: [PATCH 217/566] socket/dhcp: add retransmission/timeout tests --- src/socket/dhcpv4.rs | 196 +++++++++++++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 46 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index a1fe7178f..b5bd824bd 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -545,30 +545,43 @@ mod test { socket.process(&cx, &ip_repr, &udp_repr, &payload) } - fn recv(socket: &mut Dhcpv4Socket, timestamp: Instant, mut f: F) - where - F: FnMut(Result<(Ipv4Repr, UdpRepr, DhcpRepr)>), - { + fn recv( + socket: &mut Dhcpv4Socket, + timestamp: Instant, + reprs: &[(Ipv4Repr, UdpRepr, DhcpRepr)], + ) { let mut cx = Context::DUMMY.clone(); cx.now = timestamp; - let result = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| { - assert_eq!(ip_repr.protocol, IpProtocol::Udp); - assert_eq!( - ip_repr.payload_len, - udp_repr.header_len() + dhcp_repr.buffer_len() - ); - - // We validated the payload len, change it to 0 to make equality testing easier - ip_repr.payload_len = 0; - - net_trace!("recv: {:?}", ip_repr); - net_trace!(" {:?}", udp_repr); - net_trace!(" {:?}", dhcp_repr); - Ok(f(Ok((ip_repr, udp_repr, dhcp_repr)))) - }); - match result { - Ok(()) => (), - Err(e) => f(Err(e)), + + let mut i = 0; + + while socket.poll_at(&cx) <= PollAt::Time(timestamp) { + let _ = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| { + assert_eq!(ip_repr.protocol, IpProtocol::Udp); + assert_eq!( + ip_repr.payload_len, + udp_repr.header_len() + dhcp_repr.buffer_len() + ); + + // We validated the payload len, change it to 0 to make equality testing easier + ip_repr.payload_len = 0; + + net_trace!("recv: {:?}", ip_repr); + net_trace!(" {:?}", udp_repr); + net_trace!(" {:?}", dhcp_repr); + + let got_repr = (ip_repr, udp_repr, dhcp_repr); + match reprs.get(i) { + Some(want_repr) => assert_eq!(want_repr, &got_repr), + None => panic!("Too many reprs emitted"), + } + i += 1; + Ok(()) + }); + } + + if i != reprs.len() { + panic!("Too few reprs emitted. Wanted {}, got {}", reprs.len(), i); } } @@ -584,20 +597,12 @@ mod test { } macro_rules! recv { - ($socket:ident, [$( $repr:expr ),*]) => ({ - $( recv!($socket, Ok($repr)); )* - recv!($socket, Err(Error::Exhausted)) + ($socket:ident, $reprs:expr) => ({ + recv!($socket, time 0, $reprs); }); - ($socket:ident, time $time:expr, [$( $repr:expr ),*]) => ({ - $( recv!($socket, time $time, Ok($repr)); )* - recv!($socket, time $time, Err(Error::Exhausted)) + ($socket:ident, time $time:expr, $reprs:expr) => ({ + recv(&mut $socket, Instant::from_millis($time), &$reprs); }); - ($socket:ident, $result:expr) => - (recv!($socket, time 0, $result)); - ($socket:ident, time $time:expr, $result:expr) => - (recv(&mut $socket, Instant::from_millis($time), |result| { - assert_eq!(result, $result) - })); } #[cfg(feature = "log")] @@ -710,7 +715,7 @@ mod test { router: Some(SERVER_IP), subnet_mask: Some(MASK_24), dns_servers: Some(DNS_IPS), - lease_duration: Some(60), + lease_duration: Some(1000), ..DHCP_DEFAULT }; @@ -735,7 +740,7 @@ mod test { router: Some(SERVER_IP), subnet_mask: Some(MASK_24), dns_servers: Some(DNS_IPS), - lease_duration: Some(60), + lease_duration: Some(1000), ..DHCP_DEFAULT }; @@ -776,8 +781,8 @@ mod test { address: SERVER_IP, identifier: SERVER_IP, }, - renew_at: Instant::from_secs(30), - expires_at: Instant::from_secs(60), + renew_at: Instant::from_secs(500), + expires_at: Instant::from_secs(1000), }); s @@ -804,43 +809,142 @@ mod test { })) ); - match s.state { + match &s.state { + ClientState::Renewing(r) => { + assert_eq!(r.renew_at, Instant::from_secs(500)); + assert_eq!(r.expires_at, Instant::from_secs(1000)); + } + _ => panic!("Invalid state"), + } + } + + #[test] + fn test_discover_retransmit() { + let mut s = socket(); + + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + recv!(s, time 1_000, []); + recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + recv!(s, time 11_000, []); + recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + + // check after retransmits it still works + send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_OFFER)); + recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + } + + #[test] + fn test_request_retransmit() { + let mut s = socket(); + + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 1_000, []); + recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 6_000, []); + recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 15_000, []); + recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + + // check after retransmits it still works + send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + + match &s.state { ClientState::Renewing(r) => { - assert_eq!(r.renew_at, Instant::from_secs(30)); - assert_eq!(r.expires_at, Instant::from_secs(60)); + assert_eq!(r.renew_at, Instant::from_secs(20 + 500)); + assert_eq!(r.expires_at, Instant::from_secs(20 + 1000)); } _ => panic!("Invalid state"), } } + #[test] + fn test_request_timeout() { + let mut s = socket(); + + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + recv!(s, time 30_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + + // After 5 tries and 70 seconds, it gives up. + // 5 + 5 + 10 + 10 + 20 = 70 + recv!(s, time 70_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + + // check it still works + send!(s, time 60_000, (IP_RECV, UDP_RECV, DHCP_OFFER)); + recv!(s, time 60_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + } + #[test] fn test_renew() { let mut s = socket_bound(); recv!(s, []); assert_eq!(s.poll(), None); - recv!(s, time 40_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); assert_eq!(s.poll(), None); match &s.state { ClientState::Renewing(r) => { // the expiration still hasn't been bumped, because // we haven't received the ACK yet - assert_eq!(r.expires_at, Instant::from_secs(60)); + assert_eq!(r.expires_at, Instant::from_secs(1000)); } _ => panic!("Invalid state"), } - send!(s, time 40_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + send!(s, time 500_000, (IP_RECV, UDP_RECV, DHCP_ACK)); assert_eq!(s.poll(), None); match &s.state { ClientState::Renewing(r) => { // NOW the expiration gets bumped - assert_eq!(r.renew_at, Instant::from_secs(40 + 30)); - assert_eq!(r.expires_at, Instant::from_secs(40 + 60)); + assert_eq!(r.renew_at, Instant::from_secs(500 + 500)); + assert_eq!(r.expires_at, Instant::from_secs(500 + 1000)); + } + _ => panic!("Invalid state"), + } + } + + #[test] + fn test_renew_retransmit() { + let mut s = socket_bound(); + + recv!(s, []); + recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + recv!(s, time 749_000, []); + recv!(s, time 750_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + recv!(s, time 874_000, []); + recv!(s, time 875_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + + // check it still works + send!(s, time 875_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + match &s.state { + ClientState::Renewing(r) => { + // NOW the expiration gets bumped + assert_eq!(r.renew_at, Instant::from_secs(875 + 500)); + assert_eq!(r.expires_at, Instant::from_secs(875 + 1000)); } _ => panic!("Invalid state"), } } + + #[test] + fn test_renew_timeout() { + let mut s = socket_bound(); + + recv!(s, []); + recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + recv!(s, time 999_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + recv!(s, time 1_000_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + match &s.state { + ClientState::Discovering(_) => {} + _ => panic!("Invalid state"), + } + } } From 21e254fe82c610159c00d3fbe335264de5c219a9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 15 Oct 2021 01:02:31 +0200 Subject: [PATCH 218/566] socket/dhcp: add nak tests --- src/socket/dhcpv4.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index b5bd824bd..fc3f9251b 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -653,6 +653,14 @@ mod test { hop_limit: 64, }; + const IP_SERVER_BROADCAST: Ipv4Repr = Ipv4Repr { + src_addr: SERVER_IP, + dst_addr: Ipv4Address::BROADCAST, + protocol: IpProtocol::Udp, + payload_len: 0, + hop_limit: 64, + }; + const IP_RECV: Ipv4Repr = Ipv4Repr { src_addr: SERVER_IP, dst_addr: MY_IP, @@ -745,6 +753,13 @@ mod test { ..DHCP_DEFAULT }; + const DHCP_NAK: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Nak, + server_ip: SERVER_IP, + server_identifier: Some(SERVER_IP), + ..DHCP_DEFAULT + }; + const DHCP_RENEW: DhcpRepr = DhcpRepr { message_type: DhcpMessageType::Request, client_identifier: Some(MY_MAC), @@ -880,6 +895,17 @@ mod test { recv!(s, time 60_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); } + #[test] + fn test_request_nak() { + let mut s = socket(); + + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); + send!(s, time 0, (IP_SERVER_BROADCAST, UDP_RECV, DHCP_NAK)); + recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + } + #[test] fn test_renew() { let mut s = socket_bound(); @@ -947,4 +973,13 @@ mod test { _ => panic!("Invalid state"), } } + + #[test] + fn test_renew_nak() { + let mut s = socket_bound(); + + recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + send!(s, time 500_000, (IP_SERVER_BROADCAST, UDP_RECV, DHCP_NAK)); + recv!(s, time 500_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); + } } From 62df0c13b399ccc9fc78f0b9fce34f0314307f8c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Oct 2021 01:17:28 +0200 Subject: [PATCH 219/566] dhcp: add "ignore NAKs" option. This workarounds a known broken Vodafone Fiber router which replies with both NAK and ACK: ![screenshot-2021-10-19_01-18-41](https://user-images.githubusercontent.com/1247578/137819133-a8f9ab28-8bc5-4cca-9c91-2eac15d88070.png) --- src/socket/dhcpv4.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index fc3f9251b..267372006 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -122,6 +122,9 @@ pub struct Dhcpv4Socket { /// Max lease duration. If set, it sets a maximum cap to the server-provided lease duration. /// Useful to react faster to IP configuration changes and to test whether renews work correctly. max_lease_duration: Option, + + /// Ignore NAKs. + ignore_naks: bool, } /// DHCP client socket. @@ -141,17 +144,45 @@ impl Dhcpv4Socket { config_changed: true, transaction_id: 1, max_lease_duration: None, + ignore_naks: false, } } + /// Get the configured max lease duration. + /// + /// See also [`Self::set_max_lease_duration()`] pub fn max_lease_duration(&self) -> Option { self.max_lease_duration } + /// Set the max lease duration. + /// + /// When set, the lease duration will be capped at the configured duration if the + /// DHCP server gives us a longer lease. This is generally not recommended, but + /// can be useful for debugging or reacting faster to network configuration changes. + /// + /// If None, no max is applied (the lease duration from the DHCP server is used.) pub fn set_max_lease_duration(&mut self, max_lease_duration: Option) { self.max_lease_duration = max_lease_duration; } + /// Get whether to ignore NAKs. + /// + /// See also [`Self::set_ignore_naks()`] + pub fn ignore_naks(&self) -> bool { + self.ignore_naks + } + + /// Set whether to ignore NAKs. + /// + /// This is not compliant with the DHCP RFCs, since theoretically + /// we must stop using the assigned IP when receiving a NAK. This + /// can increase reliability on broken networks with buggy routers + /// or rogue DHCP servers, however. + pub fn set_ignore_naks(&mut self, ignore_naks: bool) { + self.ignore_naks = ignore_naks; + } + pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { let t = match &self.state { ClientState::Discovering(state) => state.retry_at, @@ -242,7 +273,9 @@ impl Dhcpv4Socket { } } (ClientState::Requesting(_), DhcpMessageType::Nak) => { - self.reset(); + if !self.ignore_naks { + self.reset(); + } } (ClientState::Renewing(state), DhcpMessageType::Ack) => { if let Some((config, renew_at, expires_at)) = @@ -257,7 +290,9 @@ impl Dhcpv4Socket { } } (ClientState::Renewing(_), DhcpMessageType::Nak) => { - self.reset(); + if !self.ignore_naks { + self.reset(); + } } _ => { net_debug!( From 908763637ea5a15a4f58230965b2511d2e2a909b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 May 2019 16:34:32 +0200 Subject: [PATCH 220/566] wire: add From
for ::std::net::IpAddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- src/wire/ip.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 3fc85ea6c..16c9de616 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -214,6 +214,19 @@ impl From<::std::net::IpAddr> for Address { } } +#[cfg(feature = "std")] +impl From
for ::std::net::IpAddr { + fn from(x: Address) -> ::std::net::IpAddr { + match x { + #[cfg(feature = "proto-ipv4")] + Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()), + #[cfg(feature = "proto-ipv6")] + Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()), + _ => unreachable!(), + } + } +} + #[cfg(all(feature = "std", feature = "proto-ipv4"))] impl From<::std::net::Ipv4Addr> for Address { fn from(ipv4: ::std::net::Ipv4Addr) -> Address { From 522fc45a32b3a6df5aa603c00ee7dc1c39d25b7b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Oct 2021 03:06:37 +0200 Subject: [PATCH 221/566] readme: add instructions on how to setup a bridged tap. --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index 35d556428..2f4b8ed87 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,45 @@ sudo iptables -t nat -A POSTROUTING -s 192.168.69.0/24 -j MASQUERADE sudo sysctl net.ipv4.ip_forward=1 sudo ip6tables -t nat -A POSTROUTING -s fdaa::/64 -j MASQUERADE sudo sysctl -w net.ipv6.conf.all.forwarding=1 + +# Some distros have a default policy of DROP. This allows the traffic. +sudo iptables -A FORWARD -i tap0 -s 192.168.69.0/24 -j ACCEPT +sudo iptables -A FORWARD -o tap0 -d 192.168.69.0/24 -j ACCEPT +``` + +### Bridged connection + +Instead of the routed connection above, you may also set up a bridged (switched) +connection. This will make smoltcp speak directly to your LAN, with real ARP, etc. +It is needed to run the DHCP example. + +NOTE: In this case, the examples' IP configuration must match your LAN's! + +NOTE: this ONLY works with actual wired Ethernet connections. It +will NOT work on a WiFi connection. + +```sh +# Replace with your wired Ethernet interface name +ETH=enp0s20f0u1u1 + +sudo modprobe bridge +sudo modprobe br_netfilter + +sudo sysctl -w net.bridge.bridge-nf-call-arptables=0 +sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0 +sudo sysctl -w net.bridge.bridge-nf-call-iptables=0 + +sudo ip tuntap add name tap0 mode tap user $USER +sudo brctl addbr br0 +sudo brctl addif br0 tap0 +sudo brctl addif br0 $ETH +sudo ip link set tap0 up +sudo ip link set $ETH up +sudo ip link set br0 up + +# This connects your host system to the internet, so you can use it +# at the same time you run the examples. +sudo dhcpcd br0 ``` ### Fault injection From 2c306b50a3a272d7f1dcece8d1fabf545c3ceaaa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Oct 2021 03:35:57 +0200 Subject: [PATCH 222/566] tcp: Reply with RST to ACKs with invalid ackno in SYN_SENT. Should fix aramperes/onetun#17 --- src/socket/tcp.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index e9885da8f..e2d404da3 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1354,6 +1354,30 @@ impl<'a> TcpSocket<'a> { return Ok(Some(Self::rst_reply(ip_repr, repr))); } } + // ACKs in the SYN-SENT state are invalid. + (State::SynSent, TcpControl::None, Some(ack_number)) => { + // If the sequence number matches, ignore it instead of RSTing. + // I'm not sure why, I think it may be a workaround for broken TCP + // servers, or a defense against reordering. Either way, if Linux + // does it, we do too. + if ack_number == self.local_seq_no + 1 { + net_debug!( + "{}:{}:{}: expecting a SYN|ACK, received an ACK with the right ack_number, ignoring.", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Err(Error::Dropped); + } + + net_debug!( + "{}:{}:{}: expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST.", + self.meta.handle, + self.local_endpoint, + self.remote_endpoint + ); + return Ok(Some(Self::rst_reply(ip_repr, repr))); + } // Anything else in the SYN-SENT state is invalid. (State::SynSent, _, _) => { net_debug!( @@ -3475,15 +3499,103 @@ mod test { #[test] fn test_syn_sent_bad_ack() { let mut s = socket_syn_sent(); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); send!( s, TcpRepr { - control: TcpControl::None, - ack_number: Some(TcpSeqNumber(1)), + control: TcpControl::None, // Unexpected + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 1), // Correct ..SEND_TEMPL }, Err(Error::Dropped) ); + + // It should trigger no response and change no state + recv!(s, []); + assert_eq!(s.state, State::SynSent); + } + + #[test] + fn test_syn_sent_bad_ack_seq_1() { + let mut s = socket_syn_sent(); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::None, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ), // WRONG + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + control: TcpControl::Rst, + seq_number: LOCAL_SEQ, // matching the ack_number of the unexpected ack + ack_number: None, + window_len: 0, + ..RECV_TEMPL + })) + ); + + // It should trigger a RST, and change no state + assert_eq!(s.state, State::SynSent); + } + + #[test] + fn test_syn_sent_bad_ack_seq_2() { + let mut s = socket_syn_sent(); + recv!( + s, + [TcpRepr { + control: TcpControl::Syn, + seq_number: LOCAL_SEQ, + ack_number: None, + max_seg_size: Some(BASE_MSS), + window_scale: Some(0), + sack_permitted: true, + ..RECV_TEMPL + }] + ); + send!( + s, + TcpRepr { + control: TcpControl::None, + seq_number: REMOTE_SEQ, + ack_number: Some(LOCAL_SEQ + 123456), // WRONG + ..SEND_TEMPL + }, + Ok(Some(TcpRepr { + control: TcpControl::Rst, + seq_number: LOCAL_SEQ + 123456, // matching the ack_number of the unexpected ack + ack_number: None, + window_len: 0, + ..RECV_TEMPL + })) + ); + + // It should trigger a RST, and change no state assert_eq!(s.state, State::SynSent); } From 23df785e5c92b2062def6181b2d41e77bc254db3 Mon Sep 17 00:00:00 2001 From: mkb2091 Date: Tue, 19 Oct 2021 22:45:26 +0100 Subject: [PATCH 223/566] Fixed typo --- src/time.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time.rs b/src/time.rs index 267fb280d..e0fb05f50 100644 --- a/src/time.rs +++ b/src/time.rs @@ -90,12 +90,12 @@ impl Instant { } /// The total number of milliseconds that have passed since - /// the biginning of time. + /// the beginning of time. pub const fn total_millis(&self) -> i64 { self.micros / 1000 } /// The total number of milliseconds that have passed since - /// the biginning of time. + /// the beginning of time. pub const fn total_micros(&self) -> i64 { self.micros } From 2c41c80f3b50037a2f418f2a99e6070ef6d3c8a1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Oct 2021 15:25:04 +0200 Subject: [PATCH 224/566] Add matrix bot --- .github/matrix-bot.yml | 44 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/matrix-bot.yml diff --git a/.github/matrix-bot.yml b/.github/matrix-bot.yml new file mode 100644 index 000000000..ca51045ba --- /dev/null +++ b/.github/matrix-bot.yml @@ -0,0 +1,44 @@ +name: Matrix bot +on: + pull_request_target: + types: [opened, closed] + +jobs: + new-pr: + if: github.event.action == 'opened' && github.repository == 'smoltcp-rs/smoltcp' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "New PR: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" + + merged-pr: + if: github.event.action == 'closed' && github.event.pull_request.merged == true && github.repository == 'smoltcp-rs/smoltcp' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "PR merged: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" + + abandoned-pr: + if: github.event.action == 'closed' && github.event.pull_request.merged == false && github.repository == 'smoltcp-rs/smoltcp' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "PR closed without merging: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" From 5dbfc42aab8cb08aa4bafb79886ed6e11c3b7a00 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Oct 2021 15:35:32 +0200 Subject: [PATCH 225/566] Fix wrong matrix bot path --- .github/{ => workflows}/matrix-bot.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/matrix-bot.yml (100%) diff --git a/.github/matrix-bot.yml b/.github/workflows/matrix-bot.yml similarity index 100% rename from .github/matrix-bot.yml rename to .github/workflows/matrix-bot.yml From 3d782f19cd51651da46d1dd8c1bc777d0a88bd33 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 29 Apr 2021 12:04:46 +0200 Subject: [PATCH 226/566] Add support for 802.15.4 and 6LoWPAN --- Cargo.toml | 16 +- examples/benchmark.rs | 2 +- examples/client.rs | 2 +- examples/dhcp_client.rs | 2 +- examples/httpclient.rs | 2 +- examples/loopback.rs | 2 +- examples/multicast.rs | 2 +- examples/ping.rs | 24 +- examples/server.rs | 2 +- examples/sixlowpan.rs | 115 +++ examples/tcpdump.rs | 2 +- examples/utils.rs | 6 - src/iface/interface.rs | 740 +++++++++++--- src/iface/mod.rs | 20 +- src/iface/neighbor.rs | 20 +- src/lib.rs | 21 +- src/phy/mod.rs | 23 +- src/phy/pcap_writer.rs | 4 + src/phy/raw_socket.rs | 13 +- src/phy/sys/linux.rs | 1 + src/phy/sys/raw_socket.rs | 18 +- src/phy/sys/tuntap_interface.rs | 4 + src/phy/tracer.rs | 2 + src/socket/dhcpv4.rs | 15 +- src/socket/icmp.rs | 17 +- src/socket/mod.rs | 27 +- src/socket/set.rs | 4 +- src/wire/arp.rs | 20 +- src/wire/icmpv6.rs | 31 +- src/wire/ieee802154.rs | 924 +++++++++++++++++ src/wire/igmp.rs | 24 +- src/wire/ip.rs | 25 + src/wire/ipv6routing.rs | 35 +- src/wire/mld.rs | 4 + src/wire/mod.rs | 113 +- src/wire/ndisc.rs | 93 +- src/wire/ndiscoption.rs | 212 ++-- src/wire/sixlowpan.rs | 1704 +++++++++++++++++++++++++++++++ 38 files changed, 3952 insertions(+), 339 deletions(-) create mode 100644 examples/sixlowpan.rs create mode 100644 src/wire/ieee802154.rs create mode 100644 src/wire/sixlowpan.rs diff --git a/Cargo.toml b/Cargo.toml index 170760a7f..cab4bf334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,18 +37,24 @@ verbose = [] rand-custom-impl = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] -"phy-raw_socket" = ["std", "libc", "medium-ethernet"] +"medium-ieee802154" = ["socket"] + +"phy-raw_socket" = ["std", "libc"] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] + "proto-ipv4" = [] "proto-igmp" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] +"proto-sixlowpan" = ["proto-ipv6"] + "socket" = [] "socket-raw" = ["socket"] "socket-udp" = ["socket"] "socket-tcp" = ["socket"] "socket-icmp" = ["socket"] "socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"] + "async" = [] defmt-trace = [] @@ -59,9 +65,9 @@ defmt-error = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ - "medium-ethernet", "medium-ip", + "medium-ethernet", "medium-ip", "medium-ieee802154", "phy-raw_socket", "phy-tuntap_interface", - "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", + "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "async" ] @@ -110,5 +116,9 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac name = "dhcp_client" required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"] +[[example]] +name = "sixlowpan" +required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] + [profile.release] debug = 2 diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 3bb23e925..72d0dc775 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -100,7 +100,7 @@ fn main() { let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); diff --git a/examples/client.rs b/examples/client.rs index 19e1417e4..c07e2f4ee 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -47,7 +47,7 @@ fn main() { .routes(routes); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 57d9211a9..663fd46f7 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -39,7 +39,7 @@ fn main() { .routes(routes); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index dafde6b5a..c23680030 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -53,7 +53,7 @@ fn main() { .routes(routes); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); diff --git a/examples/loopback.rs b/examples/loopback.rs index dfbb77346..e8df98899 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -95,7 +95,7 @@ fn main() { let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(EthernetAddress::default()) + .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) .finalize(); diff --git a/examples/multicast.rs b/examples/multicast.rs index 30a368eee..ba782a5fa 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -38,7 +38,7 @@ fn main() { let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) diff --git a/examples/ping.rs b/examples/ping.rs index 45e327bc0..45bd63de1 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -22,7 +22,7 @@ use smoltcp::{ }; macro_rules! send_icmp_ping { - ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, + (v4, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{ let icmp_repr = $repr_type::EchoRequest { ident: $ident, @@ -35,6 +35,22 @@ macro_rules! send_icmp_ping { let icmp_packet = $packet_type::new_unchecked(icmp_payload); (icmp_repr, icmp_packet) }}; + + (v6, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, + $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{ + let icmp_repr = $repr_type::EchoRequest { + ident: $ident, + seq_no: $seq_no, + data: &$echo_payload, + }; + + let icmp_payload = $socket + .send(icmp_repr.buffer_len(&Medium::Ethernet), $remote_addr) + .unwrap(); + + let icmp_packet = $packet_type::new_unchecked(icmp_payload); + (icmp_repr, icmp_packet) + }}; } macro_rules! get_icmp_pong { @@ -132,7 +148,7 @@ fn main() { .routes(routes); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); @@ -170,6 +186,7 @@ fn main() { match remote_addr { IpAddress::Ipv4(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( + v4, Icmpv4Repr, Icmpv4Packet, ident, @@ -182,6 +199,7 @@ fn main() { } IpAddress::Ipv6(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( + v6, Icmpv6Repr, Icmpv6Packet, ident, @@ -195,6 +213,7 @@ fn main() { &remote_addr, &mut icmp_packet, &device_caps.checksum, + &device_caps.medium, ); } _ => unimplemented!(), @@ -230,6 +249,7 @@ fn main() { &src_ipv6, &icmp_packet, &device_caps.checksum, + &device_caps.medium, ) .unwrap(); get_icmp_pong!( diff --git a/examples/server.rs b/examples/server.rs index 1db276a36..be13f5c34 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -59,7 +59,7 @@ fn main() { let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder - .ethernet_addr(ethernet_addr) + .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } let mut iface = builder.finalize(); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs new file mode 100644 index 000000000..b936224df --- /dev/null +++ b/examples/sixlowpan.rs @@ -0,0 +1,115 @@ +/* + +# Setup + + modprobe mac802154_hwsim + + ip link set wpan0 down + iwpan dev wpan0 set pan_id 0xbeef + ip link add link wpan0 name lowpan0 type lowpan + ip link set wpan0 up + ip link set lowpan0 up + + iwpan dev wpan1 del + iwpan phy phy1 interface add monitor%d type monitor + +# Running + + sudo ./target/debug/examples/sixlowpan + +# Teardown + + rmmod mac802154_hwsim + + */ + +mod utils; + +use log::debug; +use std::collections::BTreeMap; +use std::os::unix::io::AsRawFd; +use std::str; + +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; +use smoltcp::socket::SocketSet; +use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; +use smoltcp::time::Instant; +use smoltcp::wire::{IpAddress, IpCidr}; + +fn main() { + utils::setup_logging(""); + + let (mut opts, mut free) = utils::create_options(); + utils::add_middleware_options(&mut opts, &mut free); + + let mut matches = utils::parse_options(&opts, free); + + let device = RawSocket::new("monitor0", Medium::Ieee802154).unwrap(); + + let fd = device.as_raw_fd(); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + + let neighbor_cache = NeighborCache::new(BTreeMap::new()); + + let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 64]); + let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 128]); + let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + + let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ + 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + ]); + let ip_addrs = [IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )]; + + let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + builder = builder + .hardware_addr(ieee802154_addr.into()) + .neighbor_cache(neighbor_cache); + let mut iface = builder.finalize(); + + let mut sockets = SocketSet::new(vec![]); + let udp_handle = sockets.add(udp_socket); + + loop { + let timestamp = Instant::now(); + match iface.poll(&mut sockets, timestamp) { + Ok(_) => {} + Err(e) => { + debug!("poll error: {}", e); + } + } + + // udp:6969: respond "hello" + { + let mut socket = sockets.get::(udp_handle); + if !socket.is_open() { + socket.bind(6969).unwrap() + } + + let client = match socket.recv() { + Ok((data, endpoint)) => { + debug!( + "udp:6969 recv data: {:?} from {}", + str::from_utf8(data).unwrap(), + endpoint + ); + Some(endpoint) + } + Err(_) => None, + }; + if let Some(endpoint) = client { + let data = b"hello\n"; + debug!( + "udp:6969 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap() + ); + socket.send_slice(data, endpoint).unwrap(); + } + } + + phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + } +} diff --git a/examples/tcpdump.rs b/examples/tcpdump.rs index 6bd0e8a0e..6bdf35975 100644 --- a/examples/tcpdump.rs +++ b/examples/tcpdump.rs @@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd; fn main() { let ifname = env::args().nth(1).unwrap(); - let mut socket = RawSocket::new(ifname.as_ref()).unwrap(); + let mut socket = RawSocket::new(ifname.as_ref(), smoltcp::phy::Medium::Ethernet).unwrap(); loop { phy_wait(socket.as_raw_fd(), None).unwrap(); let (rx_token, _) = socket.receive().unwrap(); diff --git a/examples/utils.rs b/examples/utils.rs index 2a3518ba6..f82dd9a08 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -12,7 +12,6 @@ use std::process; use std::str::{self, FromStr}; use std::time::{SystemTime, UNIX_EPOCH}; -use smoltcp::phy::RawSocket; #[cfg(feature = "phy-tuntap_interface")] use smoltcp::phy::TunTapInterface; use smoltcp::phy::{Device, FaultInjector, Medium, Tracer}; @@ -112,11 +111,6 @@ pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { } } -pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket { - let interface = matches.free.remove(0); - RawSocket::new(&interface).unwrap() -} - pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { opts.optopt("", "pcap", "Write a packet capture file", "FILE"); opts.optopt( diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 0440cc6b8..12427e3b5 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -6,7 +6,7 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; use crate::iface::Routes; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::iface::{NeighborAnswer, NeighborCache}; use crate::phy::{Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::socket::*; @@ -32,10 +32,16 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. struct InterfaceInner<'a> { - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option>, - #[cfg(feature = "medium-ethernet")] - ethernet_addr: Option, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Option, + #[cfg(feature = "medium-ieee802154")] + sequence_no: u8, + #[cfg(feature = "medium-ieee802154")] + src_pan_id: Option, + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -50,10 +56,16 @@ struct InterfaceInner<'a> { /// A builder structure used for creating a network interface. pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, - #[cfg(feature = "medium-ethernet")] - ethernet_addr: Option, - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Option, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option>, + #[cfg(feature = "medium-ieee802154")] + sequence_no: u8, + #[cfg(feature = "medium-ieee802154")] + src_pan_id: Option, + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -89,7 +101,7 @@ let neighbor_cache = // ... let ip_addrs = // ... # []; let iface = InterfaceBuilder::new(device) - .ethernet_addr(hw_addr) + .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) .finalize(); @@ -99,10 +111,18 @@ let iface = InterfaceBuilder::new(device) pub fn new(device: DeviceT) -> Self { InterfaceBuilder { device: device, - #[cfg(feature = "medium-ethernet")] - ethernet_addr: None, - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: None, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: None, + + #[cfg(feature = "medium-ieee802154")] + sequence_no: 1, + #[cfg(feature = "medium-ieee802154")] + src_pan_id: None, + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: None, + ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] any_ip: false, @@ -112,17 +132,40 @@ let iface = InterfaceBuilder::new(device) } } - /// Set the Ethernet address the interface will use. See also + /// Set the Hardware address the interface will use. See also /// [ethernet_addr]. /// /// # Panics /// This function panics if the address is not unicast. /// /// [ethernet_addr]: struct.Interface.html#method.ethernet_addr - #[cfg(feature = "medium-ethernet")] - pub fn ethernet_addr(mut self, addr: EthernetAddress) -> Self { - InterfaceInner::check_ethernet_addr(&addr); - self.ethernet_addr = Some(addr); + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn hardware_addr(mut self, addr: HardwareAddress) -> Self { + InterfaceInner::check_hardware_addr(&addr); + self.hardware_addr = Some(addr); + self + } + + /// Set the initial IEEE802.15.4 sequence number the interface will use. + /// + /// **NOTE**: this needs to be initailized randomly and not equal to 0. + #[cfg(feature = "medium-ieee802154")] + pub fn sequence_no(mut self, sequence_no: u8) -> Self { + self.sequence_no = sequence_no; + self + } + + /// Set the IEEE802.15.4 source PAN ID the interface will use. + #[cfg(feature = "medium-ieee802154")] + pub fn src_pan_id(mut self, pan_id: Ieee802154Pan) -> Self { + self.src_pan_id = Some(pan_id); + self + } + + /// Set the IEEE802.15.4 destination PAN ID the interface will use. + #[cfg(feature = "medium-ieee802154")] + pub fn dst_pan_id(mut self, pan_id: Ieee802154Pan) -> Self { + self.dst_pan_id = Some(pan_id); self } @@ -194,7 +237,7 @@ let iface = InterfaceBuilder::new(device) } /// Set the Neighbor Cache the interface will use. - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { self.neighbor_cache = Some(neighbor_cache); self @@ -214,12 +257,12 @@ let iface = InterfaceBuilder::new(device) pub fn finalize(self) -> Interface<'a, DeviceT> { let device_capabilities = self.device.capabilities(); - #[cfg(feature = "medium-ethernet")] - let (ethernet_addr, neighbor_cache) = match device_capabilities.medium { + let (hardware_addr, neighbor_cache) = match device_capabilities.medium { + #[cfg(feature = "medium-ethernet")] Medium::Ethernet => ( Some( - self.ethernet_addr - .expect("ethernet_addr required option was not set"), + self.hardware_addr + .expect("hardware_addr required option was not set"), ), Some( self.neighbor_cache @@ -229,8 +272,8 @@ let iface = InterfaceBuilder::new(device) #[cfg(feature = "medium-ip")] Medium::Ip => { assert!( - self.ethernet_addr.is_none(), - "ethernet_addr is set, but device medium is IP" + self.hardware_addr.is_none(), + "hardware_addr is set, but device medium is IP" ); assert!( self.neighbor_cache.is_none(), @@ -238,23 +281,40 @@ let iface = InterfaceBuilder::new(device) ); (None, None) } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => ( + Some( + self.hardware_addr + .expect("hardware_addr required option was not set"), + ), + Some( + self.neighbor_cache + .expect("neighbor_cache required option was not set"), + ), + ), }; Interface { device: self.device, inner: InterfaceInner { - #[cfg(feature = "medium-ethernet")] - ethernet_addr, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr, ip_addrs: self.ip_addrs, #[cfg(feature = "proto-ipv4")] any_ip: self.any_ip, routes: self.routes, - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache, #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: self.ipv4_multicast_groups, #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, + #[cfg(feature = "medium-ieee802154")] + sequence_no: self.sequence_no, + #[cfg(feature = "medium-ieee802154")] + src_pan_id: self.src_pan_id, + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: self.dst_pan_id, }, } } @@ -329,6 +389,7 @@ impl<'a> IpPacket<'a> { &_ip_repr.dst_addr(), &mut Icmpv6Packet::new_unchecked(payload), &caps.checksum, + &caps.medium, ), #[cfg(feature = "socket-raw")] IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), @@ -414,26 +475,46 @@ impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d>, { - /// Get the Ethernet address of the interface. + /// Get the HardwareAddress address of the interface. /// /// # Panics - /// This function panics if if the interface's medium is not Ethernet. + /// This function panics if the medium is not Ethernet or Ieee802154. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn hardware_addr(&self) -> HardwareAddress { + #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] + assert!(self.device().capabilities().medium == Medium::Ethernet); + #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] + assert!(self.device().capabilities().medium == Medium::Ieee802154); + + #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] + assert!( + self.device().capabilities().medium == Medium::Ethernet + || self.device().capabilities().medium == Medium::Ethernet + ); - #[cfg(feature = "medium-ethernet")] - pub fn ethernet_addr(&self) -> EthernetAddress { - self.inner.ethernet_addr.unwrap() + self.inner.hardware_addr.unwrap() } - /// Set the Ethernet address of the interface. + /// Set the HardwareAddress address of the interface. /// /// # Panics - /// This function panics if the address is not unicast, or if the - /// interface's medium is not Ethernet. - #[cfg(feature = "medium-ethernet")] - pub fn set_ethernet_addr(&mut self, addr: EthernetAddress) { - assert!(self.device.capabilities().medium == Medium::Ethernet); - InterfaceInner::check_ethernet_addr(&addr); - self.inner.ethernet_addr = Some(addr); + /// This function panics if the address is not unicast, and if the medium is not Ethernet or + /// Ieee802154. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn set_hardware_addr(&mut self, addr: HardwareAddress) { + #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] + assert!(self.device().capabilities().medium == Medium::Ethernet); + #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] + assert!(self.device().capabilities().medium == Medium::Ieee802154); + + #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] + assert!( + self.device().capabilities().medium == Medium::Ethernet + || self.device().capabilities().medium == Medium::Ethernet + ); + + InterfaceInner::check_hardware_addr(&addr); + self.inner.hardware_addr = Some(addr); } /// Get a reference to the inner device. @@ -654,42 +735,60 @@ where ref mut inner, } = self; while let Some((rx_token, tx_token)) = device.receive() { - if let Err(err) = rx_token.consume(cx.now, |frame| { - match cx.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => match inner.process_ethernet(cx, sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { - if let Err(err) = inner.dispatch(cx, tx_token, packet) { - net_debug!("Failed to send response: {}", err); - } + if let Err(err) = rx_token.consume(cx.now, |frame| match cx.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => match inner.process_ethernet(cx, sockets, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + if let Err(err) = inner.dispatch(cx, tx_token, packet) { + net_debug!("Failed to send response: {}", err); } } - Err(err) => { - net_debug!("cannot process ingress packet: {}", err); - #[cfg(not(feature = "defmt"))] - net_debug!( - "packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame) - ); + Ok(()) + } + Err(err) => { + net_debug!("cannot process ingress packet: {}", err); + #[cfg(not(feature = "defmt"))] + net_debug!( + "packet dump follows:\n{}", + PrettyPrinter::>::new("", &frame) + ); + Err(err) + } + }, + #[cfg(feature = "medium-ip")] + Medium::Ip => match inner.process_ip(cx, sockets, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) { + net_debug!("Failed to send response: {}", err); + } } - }, - #[cfg(feature = "medium-ip")] - Medium::Ip => match inner.process_ip(cx, sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { - if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) { - net_debug!("Failed to send response: {}", err); - } + Ok(()) + } + Err(err) => { + net_debug!("cannot process ingress packet: {}", err); + Err(err) + } + }, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => match inner.process_ieee802154(cx, sockets, &frame) { + Ok(response) => { + processed_any = true; + if let Some(packet) = response { + if let Err(err) = inner.dispatch_ieee802154(cx, tx_token, packet) { + net_debug!("Failed to send response: {}", err); } } - Err(err) => net_debug!("cannot process ingress packet: {}", err), - }, - } - - Ok(()) + Ok(()) + } + Err(err) => { + net_debug!("cannot process ingress packet: {}", err); + Err(err) + } + }, }) { net_debug!("Failed to consume RX token: {}", err); } @@ -854,16 +953,23 @@ where Context { now, caps: self.device.capabilities(), - #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] - ethernet_address: self.inner.ethernet_addr, + #[cfg(all( + any(feature = "medium-ethernet", feature = "medium-ieee802154"), + feature = "socket-dhcpv4" + ))] + hardware_addr: self.inner.hardware_addr, + #[cfg(feature = "medium-ieee802154")] + src_pan_id: self.inner.src_pan_id, + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: self.inner.dst_pan_id, } } } impl<'a> InterfaceInner<'a> { - #[cfg(feature = "medium-ethernet")] - fn check_ethernet_addr(addr: &EthernetAddress) { - if addr.is_multicast() { + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + fn check_hardware_addr(addr: &HardwareAddress) { + if !addr.is_unicast() { panic!("Ethernet address {} is not unicast", addr) } } @@ -876,6 +982,13 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "medium-ieee802154")] + fn get_sequence_number(&mut self) -> u8 { + let no = self.sequence_no; + self.sequence_no = self.sequence_no.wrapping_add(1); + no + } + /// Determine if the given `Ipv6Address` is the solicited node /// multicast address for a IPv6 addresses assigned to the interface. /// See [RFC 4291 § 2.7.1] for more details. @@ -941,7 +1054,7 @@ impl<'a> InterfaceInner<'a> { // Ignore any packets not directed to our hardware address or any of the multicast groups. if !eth_frame.dst_addr().is_broadcast() && !eth_frame.dst_addr().is_multicast() - && eth_frame.dst_addr() != self.ethernet_addr.unwrap() + && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr.unwrap() { return Ok(None); } @@ -989,6 +1102,120 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "medium-ieee802154")] + fn process_ieee802154<'frame, T: AsRef<[u8]> + ?Sized>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + sixlowpan_payload: &'frame T, + ) -> Result>> { + let ieee802154_frame = Ieee802154Frame::new_checked(sixlowpan_payload)?; + let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame)?; + + match ieee802154_frame.payload() { + Some(payload) => self.process_sixlowpan(cx, sockets, &ieee802154_repr, payload), + None => Ok(None), + } + } + + #[cfg(feature = "proto-sixlowpan")] + fn process_sixlowpan<'frame, T: AsRef<[u8]> + ?Sized>( + &mut self, + cx: &Context, + sockets: &mut SocketSet, + ieee802154_repr: &Ieee802154Repr, + payload: &'frame T, + ) -> Result>> { + // The first header needs to be an IPHC header. + let iphc_packet = SixlowpanIphcPacket::new_checked(payload)?; + let iphc_repr = SixlowpanIphcRepr::parse( + &iphc_packet, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + )?; + + let payload = iphc_packet.payload(); + let ip_repr = IpRepr::Sixlowpan(iphc_repr); + + // Fill the neighbor cache from IP header of unicast frames. + let ip_addr = ip_repr.src_addr(); + if self.in_same_network(&ip_addr) + && self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&ip_addr, cx.now) + .found() + { + self.neighbor_cache.as_mut().unwrap().fill( + ip_addr, + ieee802154_repr.src_addr.unwrap().into(), // TODO(thvdveld): Add checks before calling unwrap + cx.now, + ); + } + + // Currently we assume the next header is a UDP, so we mark all the rest with todo. + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match SixlowpanNhcPacket::dispatch(payload)? { + SixlowpanNhcPacket::ExtensionHeader(ext_header) => { + todo!("{:?}", ext_header) + } + SixlowpanNhcPacket::UdpHeader(udp_packet) => { + // Handle the UDP + let udp_repr = SixlowpanUdpRepr::parse( + &udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + udp_packet.checksum(), + )?; + + // Look for UDP sockets that will accept the UDP packet. + // If it does not accept the packet, then send an ICMP message. + 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(cx, &ip_repr, &udp_repr, udp_packet.payload()) + { + Ok(()) => return Ok(None), + Err(e) => return Err(e), + } + } + + // The packet wasn't handled by a socket, send an ICMP port unreachable packet. + match ip_repr { + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(ipv6_repr) => { + let payload_len = icmp_reply_payload_len( + payload.len(), + IPV6_MIN_MTU, + ipv6_repr.buffer_len(), + ); + let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ipv6_repr, + data: &payload[0..payload_len], + }; + Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) + } + IpRepr::Unspecified { .. } => Err(Error::Unaddressable), + IpRepr::Sixlowpan(_) => Err(Error::Malformed), // XXX(thvdveld): this is just wrong; + r => todo!("{:?}", r), + } + } + } + } + SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { + IpProtocol::Icmpv6 => { + self.process_icmpv6(cx, sockets, ip_repr, iphc_packet.payload()) + } + hdr => todo!("{:?}", hdr), + }, + } + } + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn process_arp<'frame, T: AsRef<[u8]>>( &mut self, @@ -1034,14 +1261,19 @@ impl<'a> InterfaceInner<'a> { // when we later reply to them. self.neighbor_cache.as_mut().unwrap().fill( source_protocol_addr.into(), - source_hardware_addr, + source_hardware_addr.into(), timestamp, ); if operation == ArpOperation::Request { + let src_hardware_addr = match self.hardware_addr { + Some(HardwareAddress::Ethernet(addr)) => addr, + _ => unreachable!(), + }; + Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, - source_hardware_addr: self.ethernet_addr.unwrap(), + source_hardware_addr: src_hardware_addr, source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr, @@ -1162,7 +1394,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) + Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) } } } @@ -1192,7 +1424,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dhcpv4")] { - if ipv4_repr.protocol == IpProtocol::Udp && self.ethernet_addr.is_some() { + if ipv4_repr.protocol == IpProtocol::Udp && self.hardware_addr.is_some() { // First check for source and dest ports, then do `UdpRepr::parse` if they match. // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) let udp_packet = UdpPacket::new_checked(ip_payload)?; @@ -1372,6 +1604,7 @@ impl<'a> InterfaceInner<'a> { &ip_repr.dst_addr(), &icmp_packet, &cx.caps.checksum, + &cx.caps.medium, )?; #[cfg(feature = "socket-icmp")] @@ -1406,7 +1639,26 @@ impl<'a> InterfaceInner<'a> { seq_no, data, }; - Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) + Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) + } + #[cfg(feature = "medium-ieee802154")] + IpRepr::Sixlowpan(sixlowpan_repr) => { + let icmp_reply_repr = Icmpv6Repr::EchoReply { + ident, + seq_no, + data, + }; + Ok(self.icmpv6_reply( + cx, + Ipv6Repr { + src_addr: sixlowpan_repr.src_addr, + dst_addr: sixlowpan_repr.dst_addr, + next_header: IpProtocol::Unknown(0), + payload_len: data.len(), + hop_limit: 64, + }, + icmp_reply_repr, + )) } _ => Err(Error::Unrecognized), }, @@ -1415,9 +1667,23 @@ impl<'a> InterfaceInner<'a> { Icmpv6Repr::EchoReply { .. } => Ok(None), // Forward any NDISC packets to the ndisc packet handler - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { - IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx.now, ipv6_repr, repr), + IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx, ipv6_repr, repr), + #[cfg(feature = "medium-ieee802154")] + IpRepr::Sixlowpan(sixlowpan_repr) => { + self.process_ndisc( + cx, + Ipv6Repr { + src_addr: sixlowpan_repr.src_addr, + dst_addr: sixlowpan_repr.dst_addr, + next_header: IpProtocol::Unknown(0), + payload_len: 10, // 2 + 8 + hop_limit: sixlowpan_repr.hop_limit, + }, + repr, + ) + } _ => Ok(None), }, @@ -1431,10 +1697,13 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] + #[cfg(all( + any(feature = "medium-ethernet", feature = "medium-ieee802154"), + feature = "proto-ipv6" + ))] fn process_ndisc<'frame>( &mut self, - timestamp: Instant, + cx: &Context, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>, ) -> Result>> { @@ -1452,13 +1721,13 @@ impl<'a> InterfaceInner<'a> { .neighbor_cache .as_mut() .unwrap() - .lookup(&ip_addr, timestamp) + .lookup(&ip_addr, cx.now) .found() { self.neighbor_cache .as_mut() .unwrap() - .fill(ip_addr, lladdr, timestamp) + .fill(ip_addr, lladdr, cx.now) } } _ => (), @@ -1475,21 +1744,22 @@ impl<'a> InterfaceInner<'a> { .neighbor_cache .as_mut() .unwrap() - .fill(ip_repr.src_addr.into(), lladdr, timestamp), + .fill(ip_repr.src_addr.into(), lladdr, cx.now), _ => (), } if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, - target_addr: target_addr, - lladdr: Some(self.ethernet_addr.unwrap()), + target_addr, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + lladdr: Some(self.hardware_addr.unwrap()), }); let ip_repr = Ipv6Repr { src_addr: target_addr, dst_addr: ip_repr.src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: advert.buffer_len(), + payload_len: advert.buffer_len(&cx.caps.medium), }; Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) } else { @@ -1647,6 +1917,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn icmpv6_reply<'frame, 'icmp: 'frame>( &self, + cx: &Context, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>, ) -> Option> { @@ -1655,7 +1926,7 @@ impl<'a> InterfaceInner<'a> { src_addr: ipv6_repr.dst_addr, dst_addr: ipv6_repr.src_addr, next_header: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(), + payload_len: icmp_repr.buffer_len(&cx.caps.medium), hop_limit: 64, }; Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) @@ -1718,7 +1989,29 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) + Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) + } + #[cfg(feature = "proto-sixlowpan")] + IpRepr::Sixlowpan(sixlowpan_repr) => { + let ipv6_repr = Ipv6Repr { + src_addr: sixlowpan_repr.src_addr, + dst_addr: sixlowpan_repr.dst_addr, + next_header: IpProtocol::Udp, // XXX + payload_len: ip_payload.len(), + hop_limit: sixlowpan_repr.hop_limit, + }; + + let payload_len = icmp_reply_payload_len( + ip_payload.len(), + IPV6_MIN_MTU, + sixlowpan_repr.buffer_len(), + ); + let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ipv6_repr, + data: &ip_payload[0..payload_len], + }; + Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) } IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } @@ -1804,7 +2097,14 @@ impl<'a> InterfaceInner<'a> { 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()); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); f(frame); @@ -1839,6 +2139,13 @@ impl<'a> InterfaceInner<'a> { .unwrap() .lookup(&_routed_addr, cx.now) .found(), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => self + .neighbor_cache + .as_ref() + .unwrap() + .lookup(&_routed_addr, cx.now) + .found(), #[cfg(feature = "medium-ip")] Medium::Ip => true, }, @@ -1846,14 +2153,14 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] fn lookup_hardware_addr( &mut self, cx: &Context, tx_token: Tx, src_addr: &IpAddress, dst_addr: &IpAddress, - ) -> Result<(EthernetAddress, Tx)> + ) -> Result<(HardwareAddress, Tx)> where Tx: TxToken, { @@ -1862,19 +2169,34 @@ impl<'a> InterfaceInner<'a> { let hardware_addr = match *dst_addr { IpAddress::Unspecified => None, #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(_addr) => Some(EthernetAddress::from_bytes(&[ - 0x01, - 0x00, - 0x5e, - b[1] & 0x7F, - b[2], - b[3], - ])), + IpAddress::Ipv4(_addr) => { + Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0x01, + 0x00, + 0x5e, + b[1] & 0x7F, + b[2], + b[3], + ]))) + } #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(_addr) => Some(EthernetAddress::from_bytes(&[ - 0x33, 0x33, b[12], b[13], b[14], b[15], - ])), + IpAddress::Ipv6(_addr) => match cx.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0x33, 0x33, b[12], b[13], b[14], b[15], + ]))) + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + // Not sure if this is correct + Some(HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)) + } + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + }, }; + if let Some(hardware_addr) = hardware_addr { return Ok((hardware_addr, tx_token)); } @@ -1890,7 +2212,7 @@ impl<'a> InterfaceInner<'a> { { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), NeighborAnswer::RateLimited => return Err(Error::Unaddressable), - NeighborAnswer::NotFound => (), + _ => (), // XXX } match (src_addr, dst_addr) { @@ -1900,10 +2222,16 @@ impl<'a> InterfaceInner<'a> { "address {} not in neighbor cache, sending ARP request", dst_addr ); + let src_hardware_addr = + if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, - source_hardware_addr: self.ethernet_addr.unwrap(), + source_hardware_addr: src_hardware_addr, source_protocol_addr: src_addr, target_hardware_addr: EthernetAddress::BROADCAST, target_protocol_addr: dst_addr, @@ -1926,15 +2254,15 @@ impl<'a> InterfaceInner<'a> { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, - lladdr: Some(self.ethernet_addr.unwrap()), + lladdr: self.hardware_addr, }); let packet = IpPacket::Icmpv6(( Ipv6Repr { - src_addr: src_addr, + src_addr, dst_addr: dst_addr.solicited_node(), next_header: IpProtocol::Icmpv6, - payload_len: solicit.buffer_len(), + payload_len: solicit.buffer_len(&cx.caps.medium), hop_limit: 0xff, }, solicit, @@ -1961,12 +2289,15 @@ impl<'a> InterfaceInner<'a> { match cx.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - let (dst_hardware_addr, tx_token) = self.lookup_hardware_addr( + let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( cx, tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr(), - )?; + )? { + (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), + _ => unreachable!(), + }; self.dispatch_ethernet(cx, tx_token, ip_repr.total_len(), |mut frame| { frame.set_dst_addr(dst_hardware_addr); @@ -1998,6 +2329,157 @@ impl<'a> InterfaceInner<'a> { Ok(()) }) } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => self.dispatch_ieee802154(cx, tx_token, packet), + } + } + + #[cfg(feature = "medium-ieee802154")] + fn dispatch_ieee802154( + &mut self, + cx: &Context, + tx_token: Tx, + packet: IpPacket, + ) -> Result<()> { + let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; + + match cx.caps.medium { + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( + cx, + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )? { + (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), + _ => unreachable!(), + }; + + let ack_request = dst_hardware_addr.is_unicast(); + + let ack_request = match packet { + IpPacket::Icmpv6(_) => false, + _ => ack_request, + }; + + let mut tx_len = 0; + + let ll_src_addr = + if let Some(HardwareAddress::Ieee802154(addr)) = self.hardware_addr { + Some(addr) + } else { + return Err(Error::Malformed); + }; + + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: cx.dst_pan_id, + dst_addr: Some(dst_hardware_addr), + src_pan_id: cx.src_pan_id, + src_addr: ll_src_addr, + }; + + let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { + (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), + _ => return Err(Error::Unaddressable), + }; + + let next_header = match &packet { + IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, + IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), + _ => return Err(Error::Unrecognized), + }; + + let hop_limit = match packet { + IpPacket::Icmpv6((_, Icmpv6Repr::Ndisc(_))) => 255, + IpPacket::Icmpv6((_, Icmpv6Repr::EchoReply { .. })) => 64, + IpPacket::Udp(..) => 64, + _ => return Err(Error::Unrecognized), + }; + + let iphc_repr = SixlowpanIphcRepr { + src_addr, + ll_src_addr, + dst_addr, + ll_dst_addr: Some(dst_hardware_addr), + next_header, + hop_limit, + }; + + tx_len += ieee_repr.buffer_len(); + tx_len += iphc_repr.buffer_len(); + + match &packet { + IpPacket::Udp((_, udp_repr, payload)) => { + let udp_repr = SixlowpanUdpRepr(*udp_repr); + tx_len += udp_repr.header_len() + payload.len(); + } + IpPacket::Icmpv6((_, icmp)) => { + tx_len += icmp.buffer_len(&cx.caps.medium); + } + _ => return Err(Error::Unrecognized), + } + + //tx_len += 2; // XXX: FCS calculation not needed when doing it in hardware + + tx_token.consume(cx.now, tx_len, |mut tx_buffer| { + // 1. Create the header of 802.15.4 + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buffer); + ieee_repr.emit(&mut ieee_packet); + + let mut start = ieee_repr.buffer_len(); + + // 2. Create the header for 6LoWPAN IPHC + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut tx_buffer[start..tx_len]); + iphc_repr.emit(&mut iphc_packet); + start += iphc_repr.buffer_len(); + + match packet { + IpPacket::Udp((_, udp_repr, payload)) => { + // 3. Create the header for 6LoWPAN UDP + let mut udp_packet = + SixlowpanUdpPacket::new_unchecked(&mut tx_buffer[start..tx_len]); + + SixlowpanUdpRepr(udp_repr).emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + IpPacket::Icmpv6((_, icmp_repr)) => { + // 3. Create the header for ICMPv6 + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut tx_buffer[start..tx_len]); + + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &cx.caps.checksum, + &cx.caps.medium, + ); + } + _ => return Err(Error::Unrecognized), + } + + //let fcs = crate::wire::ieee802154::calculate_crc(&tx_buffer[..tx_len-2]); + //tx_buffer[tx_len-1] = ((fcs >> 8) & 0xff) as u8; + //tx_buffer[tx_len-2] = (fcs & 0xff) as u8; + + Ok(()) + }) + } + _ => Err(Error::NotSupported), } } @@ -2113,7 +2595,7 @@ mod test { ]; let iface_builder = InterfaceBuilder::new(device) - .ethernet_addr(EthernetAddress::default()) + .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] @@ -2150,7 +2632,7 @@ mod test { } #[test] - #[should_panic(expected = "ethernet_addr required option was not set")] + #[should_panic(expected = "hardware_addr required option was not set")] #[cfg(all(feature = "medium-ethernet"))] fn test_builder_initialization_panic() { InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); @@ -2653,7 +3135,7 @@ mod test { dst_addr: src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(), + payload_len: expected_icmp_repr.buffer_len(&Medium::Ethernet), }; #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let expected_icmp_repr = Icmpv4Repr::DstUnreachable { @@ -2673,6 +3155,13 @@ mod test { let cx = iface.context(Instant::from_secs(0)); // The expected packet does not exceed the IPV4_MIN_MTU + #[cfg(feature = "proto-ipv6")] + assert_eq!( + expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(&Medium::Ethernet), + MIN_MTU + ); + // The expected packet does not exceed the IPV4_MIN_MTU + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] assert_eq!( expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), MIN_MTU @@ -2753,7 +3242,7 @@ mod test { &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr) ), - Ok((remote_hw_addr, MockTxToken)) + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); } @@ -2771,14 +3260,14 @@ mod test { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: local_ip_addr, - lladdr: Some(remote_hw_addr), + lladdr: Some(remote_hw_addr.into()), }); let ip_repr = IpRepr::Ipv6(Ipv6Repr { src_addr: remote_ip_addr, dst_addr: local_ip_addr.solicited_node(), next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: solicit.buffer_len(), + payload_len: solicit.buffer_len(&Medium::Ethernet), }); let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); @@ -2792,13 +3281,14 @@ mod test { &local_ip_addr.solicited_node().into(), &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), &ChecksumCapabilities::default(), + &iface.device().capabilities().medium, ); } let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr: local_ip_addr, - lladdr: Some(local_hw_addr), + lladdr: Some(local_hw_addr.into()), }); let ipv6_expected = Ipv6Repr { @@ -2806,7 +3296,7 @@ mod test { dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: icmpv6_expected.buffer_len(), + payload_len: icmpv6_expected.buffer_len(&Medium::Ethernet), }; let cx = iface.context(Instant::from_secs(0)); @@ -2830,7 +3320,7 @@ mod test { &IpAddress::Ipv6(local_ip_addr), &IpAddress::Ipv6(remote_ip_addr) ), - Ok((remote_hw_addr, MockTxToken)) + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); } @@ -3040,7 +3530,7 @@ mod test { src_addr: Ipv6Address::LOOPBACK, dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, - payload_len: reply_icmp_repr.buffer_len(), + payload_len: reply_icmp_repr.buffer_len(&Medium::Ethernet), hop_limit: 0x40, }; @@ -3074,6 +3564,8 @@ mod test { } #[cfg(feature = "medium-ip")] Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => todo!(), }; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 18f0b1227..f1c6313cd 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,19 +4,27 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ -#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] +#[cfg(any( + feature = "medium-ethernet", + feature = "medium-ip", + feature = "medium-ieee802154" +))] mod interface; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] mod neighbor; mod route; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub(crate) use self::neighbor::Answer as NeighborAnswer; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub use self::neighbor::Cache as NeighborCache; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] +#[cfg(any( + feature = "medium-ethernet", + feature = "medium-ip", + feature = "medium-ieee802154" +))] pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 7fff0c74c..0b6b1469b 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -4,7 +4,7 @@ use managed::ManagedMap; use crate::time::{Duration, Instant}; -use crate::wire::{EthernetAddress, IpAddress}; +use crate::wire::{HardwareAddress, IpAddress}; /// A cached neighbor. /// @@ -13,7 +13,7 @@ use crate::wire::{EthernetAddress, IpAddress}; #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Neighbor { - hardware_addr: EthernetAddress, + hardware_addr: HardwareAddress, expires_at: Instant, } @@ -22,7 +22,7 @@ pub struct Neighbor { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum Answer { /// The neighbor address is in the cache and not expired. - Found(EthernetAddress), + Found(HardwareAddress), /// The neighbor address is not in the cache, or has expired. NotFound, /// The neighbor address is not in the cache, or has expired, @@ -115,7 +115,7 @@ impl<'a> Cache<'a> { pub fn fill( &mut self, protocol_addr: IpAddress, - hardware_addr: EthernetAddress, + hardware_addr: HardwareAddress, timestamp: Instant, ) { debug_assert!(protocol_addr.is_unicast()); @@ -197,7 +197,7 @@ impl<'a> Cache<'a> { pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { if protocol_addr.is_broadcast() { - return Answer::Found(EthernetAddress::BROADCAST); + return Answer::Found(HardwareAddress::BROADCAST); } if let Some(&Neighbor { @@ -228,10 +228,12 @@ mod test { use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4}; use std::collections::BTreeMap; - const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]); - const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]); - const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]); - const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]); + use crate::wire::EthernetAddress; + + const HADDR_A: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 1])); + const HADDR_B: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 2])); + const HADDR_C: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 3])); + const HADDR_D: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 4])); #[test] fn test_fill() { diff --git a/src/lib.rs b/src/lib.rs index 0d0d1ce71..b8653cf0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,8 +97,12 @@ compile_error!("at least one socket needs to be enabled"); */ #[cfg(any(feature = "std", feature = "alloc"))] extern crate alloc; -#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))] -compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6"); +#[cfg(not(any( + feature = "proto-ipv4", + feature = "proto-ipv6", + feature = "proto-sixlowpan" +)))] +compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6, proto-sixlowpan"); #[cfg(all( feature = "socket", @@ -113,9 +117,13 @@ compile_error!("If you enable the socket feature, you must enable at least one o #[cfg(all( feature = "socket", - not(any(feature = "medium-ethernet", feature = "medium-ip",)) + not(any( + feature = "medium-ethernet", + feature = "medium-ip", + feature = "medium-ieee802154", + )) ))] -compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet"); +compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet, medium-ieee802154"); #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You must enable at most one of the following features: defmt, log"); @@ -174,6 +182,10 @@ pub enum Error { /// An incoming packet was recognized but contradicted internal state. /// E.g. a TCP packet addressed to a socket that doesn't exist. Dropped, + + /// An incoming packet was recognized but some parts are not supported by smoltcp. + /// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC. + NotSupported, } #[cfg(feature = "std")] @@ -195,6 +207,7 @@ impl fmt::Display for Error { Error::Fragmented => write!(f, "fragmented packet"), Error::Malformed => write!(f, "malformed packet"), Error::Dropped => write!(f, "dropped by socket"), + Error::NotSupported => write!(f, "not supported by smoltcp"), } } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index c156d58b9..ae4a11819 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -254,6 +254,8 @@ impl DeviceCapabilities { } #[cfg(feature = "medium-ip")] Medium::Ip => self.max_transmission_unit, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802 } } } @@ -275,15 +277,32 @@ pub enum Medium { /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. #[cfg(feature = "medium-ip")] Ip, + + #[cfg(feature = "medium-ieee802154")] + Ieee802154, } impl Default for Medium { fn default() -> Medium { #[cfg(feature = "medium-ethernet")] return Medium::Ethernet; - #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))] + #[cfg(all( + feature = "medium-ip", + not(feature = "medium-ethernet"), + not(feature = "medium-ieee802154") + ))] return Medium::Ip; - #[cfg(all(not(feature = "medium-ip"), not(feature = "medium-ethernet")))] + #[cfg(all( + feature = "medium-ieee802154", + not(feature = "medium-ip"), + not(feature = "medium-ethernet") + ))] + return Medium::Ieee802154; + #[cfg(all( + not(feature = "medium-ip"), + not(feature = "medium-ethernet"), + not(feature = "medium-ieee802154") + ))] panic!("No medium enabled"); } } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 89b8c8bec..069e55503 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -15,6 +15,8 @@ enum_with_unknown! { Ethernet = 1, /// IPv4 or IPv6 packets (depending on the version field) Ip = 101, + /// IEEE 802.15.4 packets with FCS included. + Ieee802154WithFcs = 195, } } @@ -133,6 +135,8 @@ impl Device<'a>, S: PcapSink> PcapWriter { Medium::Ip => PcapLinkType::Ip, #[cfg(feature = "medium-ethernet")] Medium::Ethernet => PcapLinkType::Ethernet, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => PcapLinkType::Ieee802154WithFcs, }; sink.global_header(link_type); PcapWriter { diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 4c12635f5..95b2dbc92 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -26,10 +26,19 @@ impl RawSocket { /// /// This requires superuser privileges or a corresponding capability bit /// set on the executable. - pub fn new(name: &str) -> io::Result { + pub fn new(name: &str, medium: Medium) -> io::Result { let mut lower = sys::RawSocketDesc::new(name)?; lower.bind_interface()?; - let mtu = lower.interface_mtu()?; + + let mut mtu = lower.interface_mtu()?; + + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + mtu += crate::wire::EthernetFrame::<&[u8]>::header_len() + } + Ok(RawSocket { lower: Rc::new(RefCell::new(lower)), mtu: mtu, diff --git a/src/phy/sys/linux.rs b/src/phy/sys/linux.rs index 58d819c9b..f83ab5866 100644 --- a/src/phy/sys/linux.rs +++ b/src/phy/sys/linux.rs @@ -3,6 +3,7 @@ pub const SIOCGIFMTU: libc::c_ulong = 0x8921; pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; pub const ETH_P_ALL: libc::c_short = 0x0003; +pub const ETH_P_IEEE802154: libc::c_short = 0x00F6; pub const TUNSETIFF: libc::c_ulong = 0x400454CA; pub const IFF_TUN: libc::c_int = 0x0001; diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 8d28b82b4..23c9cdf48 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -18,10 +18,17 @@ impl AsRawFd for RawSocketDesc { impl RawSocketDesc { pub fn new(name: &str) -> io::Result { let lower = unsafe { + // TODO(thvdveld) + //#[cfg(feature = "medium-ieee802154")] + //let protocol = imp::ETH_P_IEEE802154; + + #[cfg(feature = "medium-ethernet")] + let protocol = imp::ETH_P_ALL; + let lower = libc::socket( libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK, - imp::ETH_P_ALL.to_be() as i32, + protocol.to_be() as i32, ); if lower == -1 { return Err(io::Error::last_os_error()); @@ -44,9 +51,16 @@ impl RawSocketDesc { } pub fn bind_interface(&mut self) -> io::Result<()> { + // TODO(thvdveld) + //#[cfg(feature = "medium-ieee802154")] + //let protocol = imp::ETH_P_IEEE802154; + + #[cfg(feature = "medium-ethernet")] + let protocol = imp::ETH_P_ALL; + let sockaddr = libc::sockaddr_ll { sll_family: libc::AF_PACKET as u16, - sll_protocol: imp::ETH_P_ALL.to_be() as u16, + sll_protocol: protocol.to_be() as u16, sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?, sll_hatype: 1, sll_pkttype: 0, diff --git a/src/phy/sys/tuntap_interface.rs b/src/phy/sys/tuntap_interface.rs index 54b9c3647..c0dae80cd 100644 --- a/src/phy/sys/tuntap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -42,6 +42,8 @@ impl TunTapInterfaceDesc { Medium::Ip => imp::IFF_TUN, #[cfg(feature = "medium-ethernet")] Medium::Ethernet => imp::IFF_TAP, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => todo!(), }; self.ifreq.ifr_data = mode | imp::IFF_NO_PI; ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ()) @@ -72,6 +74,8 @@ impl TunTapInterfaceDesc { Medium::Ip => ip_mtu, #[cfg(feature = "medium-ethernet")] Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => todo!(), }; Ok(mtu) diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 40b140b0b..fdf5b1cfc 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -190,6 +190,8 @@ impl<'a> fmt::Display for Packet<'a> { } _ => f.write_str("unrecognized IP version"), }, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => Ok(()), // XXX } } } diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 267372006..4906ebb95 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -2,6 +2,7 @@ use crate::socket::SocketHandle; use crate::socket::{Context, SocketMeta}; use crate::time::{Duration, Instant}; use crate::wire::dhcpv4::field as dhcpv4_field; +use crate::wire::HardwareAddress; use crate::wire::{ DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr, UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, @@ -218,7 +219,13 @@ impl Dhcpv4Socket { return Ok(()); } }; - if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { + let hardware_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + if dhcp_repr.client_hardware_address != hardware_addr { return Ok(()); } if dhcp_repr.transaction_id != self.transaction_id { @@ -381,7 +388,11 @@ impl Dhcpv4Socket { { // note: Dhcpv4Socket is only usable in ethernet mediums, so the // unwrap can never fail. - let ethernet_addr = cx.ethernet_address.unwrap(); + let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; // Worst case biggest IPv4 header length. // 0x0f * 4 = 60 bytes. diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index acdee1204..b2b231d60 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -427,18 +427,19 @@ impl<'a> IcmpSocket<'a> { IcmpRepr::Ipv6(ref icmp_repr) => { let packet_buf = self .rx_buffer - .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + .enqueue(icmp_repr.buffer_len(&_cx.caps.medium), ip_repr.src_addr())?; icmp_repr.emit( &ip_repr.src_addr(), &ip_repr.dst_addr(), &mut Icmpv6Packet::new_unchecked(packet_buf), &ChecksumCapabilities::default(), + &_cx.caps.medium, ); net_trace!( "{}:{}: receiving {} octets", self.meta.handle, - icmp_repr.buffer_len(), + icmp_repr.buffer_len(&_cx.caps.medium), packet_buf.len() ); } @@ -486,12 +487,13 @@ impl<'a> IcmpSocket<'a> { &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored(), + &_cx.caps.medium, )?; let ip_repr = IpRepr::Ipv6(Ipv6Repr { src_addr: src_addr, dst_addr: ipv6_addr, next_header: IpProtocol::Icmpv6, - payload_len: repr.buffer_len(), + payload_len: repr.buffer_len(&_cx.caps.medium), hop_limit: hop_limit, }); emit((ip_repr, IcmpRepr::Ipv6(repr))) @@ -866,6 +868,7 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, + &crate::phy::Medium::Ethernet, ); assert_eq!( @@ -913,6 +916,7 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, + &crate::phy::Medium::Ethernet, ); s.set_hop_limit(Some(0x2a)); @@ -929,7 +933,7 @@ mod test_ipv6 { src_addr: Ipv6Address::UNSPECIFIED, dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, - payload_len: ECHOV6_REPR.buffer_len(), + payload_len: ECHOV6_REPR.buffer_len(&crate::phy::Medium::Ethernet), hop_limit: 0x2a, }) ); @@ -956,6 +960,7 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, + &crate::phy::Medium::Ethernet, ); let data = &packet.into_inner()[..]; @@ -994,6 +999,7 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, + &crate::phy::Medium::Ethernet, ); // Ensure that a packet with an identifier that isn't the bound @@ -1036,7 +1042,7 @@ mod test_ipv6 { src_addr: REMOTE_IPV6.into(), dst_addr: LOCAL_IPV6.into(), protocol: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(), + payload_len: icmp_repr.buffer_len(&crate::phy::Medium::Ethernet), hop_limit: 0x40, }; @@ -1058,6 +1064,7 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, + &crate::phy::Medium::Ethernet, ); assert_eq!( socket.recv(), diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d28bb2dcd..5f29026de 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -187,8 +187,15 @@ from_socket!(Dhcpv4Socket, Dhcpv4); #[derive(Clone, Debug)] pub(crate) struct Context { pub now: Instant, - #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] - pub ethernet_address: Option, + #[cfg(all( + any(feature = "medium-ethernet", feature = "medium-ieee802154"), + feature = "socket-dhcpv4" + ))] + pub hardware_addr: Option, + #[cfg(feature = "medium-ieee802154")] + pub src_pan_id: Option, + #[cfg(feature = "medium-ieee802154")] + pub dst_pan_id: Option, pub caps: DeviceCapabilities, } @@ -215,10 +222,18 @@ impl Context { #[cfg(not(feature = "medium-ethernet"))] max_transmission_unit: 1500, }, - #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))] - ethernet_address: Some(crate::wire::EthernetAddress([ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - ])), + #[cfg(all( + any(feature = "medium-ethernet", feature = "medium-ieee802154"), + feature = "socket-dhcpv4" + ))] + hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( + crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), + )), now: Instant::from_millis_const(0), + + #[cfg(feature = "medium-ieee802154")] + src_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), + #[cfg(feature = "medium-ieee802154")] + dst_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), }; } diff --git a/src/socket/set.rs b/src/socket/set.rs index 482819516..4c37efa64 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -69,9 +69,7 @@ impl<'a> Set<'a> { } match self.sockets { - ManagedSlice::Borrowed(_) => { - panic!("adding a socket to a full SocketSet") - } + ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"), #[cfg(any(feature = "std", feature = "alloc"))] ManagedSlice::Owned(ref mut sockets) => { sockets.push(None); diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 48d64ca69..a56c17dcd 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -357,17 +357,15 @@ impl fmt::Display for Repr { source_protocol_addr, target_hardware_addr, target_protocol_addr, - } => { - write!( - f, - "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", - source_hardware_addr, - source_protocol_addr, - target_hardware_addr, - target_protocol_addr, - operation - ) - } + } => write!( + f, + "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, + operation + ), } } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 333b4900c..0873ea37b 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -2,9 +2,10 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::{cmp, fmt}; use crate::phy::ChecksumCapabilities; +use crate::phy::Medium; use crate::wire::ip::checksum; use crate::wire::MldRepr; -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::wire::NdiscRepr; use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; use crate::{Error, Result}; @@ -532,7 +533,7 @@ pub enum Repr<'a> { seq_no: u16, data: &'a [u8], }, - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Ndisc(NdiscRepr<'a>), Mld(MldRepr<'a>), } @@ -545,6 +546,7 @@ impl<'a> Repr<'a> { dst_addr: &IpAddress, packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities, + medium: &Medium, ) -> Result> where T: AsRef<[u8]> + ?Sized, @@ -617,15 +619,17 @@ impl<'a> Repr<'a> { seq_no: packet.echo_seq_no(), data: packet.payload(), }), - #[cfg(feature = "medium-ethernet")] - (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + (msg_type, 0) if msg_type.is_ndisc() => { + NdiscRepr::parse(packet, medium).map(Repr::Ndisc) + } (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), _ => Err(Error::Unrecognized), } } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub fn buffer_len(&self, medium: &Medium) -> usize { match self { &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } @@ -636,8 +640,8 @@ impl<'a> Repr<'a> { &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() } - #[cfg(feature = "medium-ethernet")] - &Repr::Ndisc(ndisc) => ndisc.buffer_len(), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + &Repr::Ndisc(ndisc) => ndisc.buffer_len(medium), &Repr::Mld(mld) => mld.buffer_len(), } } @@ -650,6 +654,7 @@ impl<'a> Repr<'a> { dst_addr: &IpAddress, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities, + medium: &Medium, ) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { @@ -730,8 +735,8 @@ impl<'a> Repr<'a> { packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len]) } - #[cfg(feature = "medium-ethernet")] - Repr::Ndisc(ndisc) => ndisc.emit(packet), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + Repr::Ndisc(ndisc) => ndisc.emit(packet, medium), Repr::Mld(mld) => mld.emit(packet), } @@ -839,6 +844,7 @@ mod test { &MOCK_IP_ADDR_2, &packet, &ChecksumCapabilities::default(), + &Medium::Ethernet, ) .unwrap(); assert_eq!(repr, echo_packet_repr()); @@ -847,13 +853,14 @@ mod test { #[test] fn test_echo_emit() { let repr = echo_packet_repr(); - let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit( &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), + &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } @@ -892,6 +899,7 @@ mod test { &MOCK_IP_ADDR_2, &packet, &ChecksumCapabilities::default(), + &Medium::Ethernet, ) .unwrap(); assert_eq!(repr, too_big_packet_repr()); @@ -900,13 +908,14 @@ mod test { #[test] fn test_too_big_emit() { let repr = too_big_packet_repr(); - let mut bytes = vec![0xa5; repr.buffer_len()]; + let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit( &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), + &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs new file mode 100644 index 000000000..68ec3a5e0 --- /dev/null +++ b/src/wire/ieee802154.rs @@ -0,0 +1,924 @@ +use core::fmt; + +use byteorder::{ByteOrder, LittleEndian}; + +use crate::wire::ipv6::Address as Ipv6Address; +use crate::Error; +use crate::Result; + +const CRC_TABLE: [u16; 256] = [ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, + 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, + 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, + 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, + 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, + 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, + 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, + 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, + 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, + 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, + 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, + 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, +]; + +pub fn calculate_crc(buffer: &[u8]) -> u16 { + fn crc_byte(crc: u16, c: u8) -> u16 { + (crc >> 8) ^ CRC_TABLE[((crc ^ (c as u16)) & 0xff) as usize] + } + + let mut crc = 0; + + for b in buffer { + crc = crc_byte(crc, *b); + } + + crc +} + +enum_with_unknown! { + /// IEEE 802.15.4 frame type. + pub enum FrameType(u8) { + Beacon = 0b000, + Data = 0b001, + Acknowledgement = 0b010, + MacCommand = 0b011, + Multipurpose = 0b101, + FragmentOrFrak = 0b110, + Extended = 0b111, + } +} + +impl fmt::Display for FrameType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + FrameType::Beacon => write!(f, "Beacon"), + FrameType::Data => write!(f, "Data"), + FrameType::Acknowledgement => write!(f, "Ack"), + FrameType::MacCommand => write!(f, "MAC command"), + FrameType::Multipurpose => write!(f, "Multipurpose"), + FrameType::FragmentOrFrak => write!(f, "FragmentOrFrak"), + FrameType::Extended => write!(f, "Extended"), + FrameType::Unknown(id) => write!(f, "0b{:04b}", id), + } + } +} +enum_with_unknown! { + /// IEEE 802.15.4 addressing mode for destination and source addresses. + pub enum AddressingMode(u8) { + Absent = 0b00, + Short = 0b10, + Extended = 0b11, + } +} + +impl AddressingMode { + /// Return the size in octets of the address. + fn size(&self) -> usize { + match self { + AddressingMode::Absent => 0, + AddressingMode::Short => 2, + AddressingMode::Extended => 8, + AddressingMode::Unknown(_) => 0, // TODO(thvdveld): what do we need to here? + } + } +} + +impl fmt::Display for AddressingMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AddressingMode::Absent => write!(f, "Absent"), + AddressingMode::Short => write!(f, "Short"), + AddressingMode::Extended => write!(f, "Extended"), + AddressingMode::Unknown(id) => write!(f, "0b{:04b}", id), + } + } +} + +/// A IEEE 802.15.4 PAN. +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub struct Pan(pub u16); + +impl Pan { + /// Return the PAN ID as bytes. + pub fn as_bytes(&self) -> [u8; 2] { + let mut pan = [0u8; 2]; + LittleEndian::write_u16(&mut pan, self.0); + pan + } +} + +/// A IEEE 802.15.4 address. +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub enum Address { + Absent, + Short([u8; 2]), + Extended([u8; 8]), +} + +impl Address { + /// The broadcast address. + pub const BROADCAST: Address = Address::Short([0xff; 2]); + + /// Query whether the address is an unicast address. + pub fn is_unicast(&self) -> bool { + !self.is_broadcast() + } + + /// Query whether this address is the broadcast address. + pub fn is_broadcast(&self) -> bool { + *self == Self::BROADCAST + } + + fn short_from_bytes(a: [u8; 2]) -> Self { + Self::Short(a) + } + + fn extended_from_bytes(a: [u8; 8]) -> Self { + Self::Extended(a) + } + + pub fn from_bytes(a: &[u8]) -> Self { + if a.len() == 2 { + let mut b = [0u8; 2]; + b.copy_from_slice(a); + Address::Short(b) + } else if a.len() == 8 { + let mut b = [0u8; 8]; + b.copy_from_slice(a); + Address::Extended(b) + } else { + panic!("Not an IEEE802.15.4 address"); + } + } + + pub fn as_bytes(&self) -> &[u8] { + match self { + Address::Absent => &[], + Address::Short(value) => value, + Address::Extended(value) => value, + } + } + + /// Convert the extended address to an Extended Unique Identifier (EUI-64) + pub fn as_eui_64(&self) -> Option<[u8; 8]> { + match self { + Address::Absent | Address::Short(_) => None, + Address::Extended(value) => { + let mut bytes = [0; 8]; + bytes.copy_from_slice(&value[..]); + + bytes[0] ^= 1 << 1; + + Some(bytes) + } + } + } + + /// Convert an extended address to a link-local IPv6 address using the EUI-64 format from + /// RFC2464. + pub fn as_link_local_address(&self) -> Option { + let mut bytes = [0; 16]; + bytes[0] = 0xfe; + bytes[1] = 0x80; + bytes[8..].copy_from_slice(&self.as_eui_64()?); + + Some(Ipv6Address::from_bytes(&bytes)) + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Absent => write!(f, "not-present"), + Self::Short(bytes) => write!(f, "{:02x}-{:02x}", bytes[0], bytes[1]), + Self::Extended(bytes) => write!( + f, + "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] + ), + } + } +} + +enum_with_unknown! { + /// IEEE 802.15.4 addressing mode for destination and source addresses. + pub enum FrameVersion(u8) { + Ieee802154_2003 = 0b00, + Ieee802154_2006 = 0b01, + Ieee802154 = 0b10, + } +} + +/// A read/write wrapper around an IEEE 802.15.4 frame buffer. +#[derive(Debug, Clone)] +pub struct Frame> { + buffer: T, +} + +mod field { + use crate::wire::field::*; + + pub const FRAMECONTROL: Field = 0..2; + pub const SEQUENCE_NUMBER: usize = 2; + pub const ADDRESSING: Rest = 3..; +} + +macro_rules! fc_bit_field { + ($field:ident, $bit:literal) => { + #[inline] + pub fn $field(&self) -> bool { + let data = self.buffer.as_ref(); + let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]); + + ((raw >> $bit) & 0b1) == 0b1 + } + }; +} + +macro_rules! set_fc_bit_field { + ($field:ident, $bit:literal) => { + #[inline] + pub fn $field(&mut self, val: bool) { + let data = &mut self.buffer.as_mut()[field::FRAMECONTROL]; + let mut raw = LittleEndian::read_u16(data); + raw |= ((val as u16) << $bit); + + data.copy_from_slice(&raw.to_le_bytes()); + } + }; +} + +impl> Frame { + /// Input a raw octet buffer with Ethernet frame structure. + pub fn new_unchecked(buffer: T) -> Frame { + Frame { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result> { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + + if matches!(packet.dst_addressing_mode(), AddressingMode::Unknown(_)) { + return Err(Error::Malformed); + } + + if matches!(packet.src_addressing_mode(), AddressingMode::Unknown(_)) { + return Err(Error::Malformed); + } + + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Truncated)` if the buffer is too short. + pub fn check_len(&self) -> Result<()> { + if self.buffer.as_ref().is_empty() { + Err(Error::Truncated) + } else { + Ok(()) + } + } + + /// Consumes the frame, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + /// Return the FrameType field. + #[inline] + pub fn frame_type(&self) -> FrameType { + let data = self.buffer.as_ref(); + let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]); + let ft = (raw & 0b11) as u8; + FrameType::from(ft) + } + + fc_bit_field!(security_enabled, 3); + fc_bit_field!(frame_pending, 4); + fc_bit_field!(ack_request, 5); + fc_bit_field!(pan_id_compression, 6); + + fc_bit_field!(sequence_number_suppression, 8); + fc_bit_field!(ie_present, 9); + + /// Return the destination addressing mode. + #[inline] + pub fn dst_addressing_mode(&self) -> AddressingMode { + let data = self.buffer.as_ref(); + let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]); + let am = ((raw >> 10) & 0b11) as u8; + AddressingMode::from(am) + } + + /// Return the frame version. + #[inline] + pub fn frame_version(&self) -> FrameVersion { + let data = self.buffer.as_ref(); + let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]); + let fv = ((raw >> 12) & 0b11) as u8; + FrameVersion::from(fv) + } + + /// Return the source addressing mode. + #[inline] + pub fn src_addressing_mode(&self) -> AddressingMode { + let data = self.buffer.as_ref(); + let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]); + let am = ((raw >> 14) & 0b11) as u8; + AddressingMode::from(am) + } + + /// Return the sequence number of the frame. + #[inline] + pub fn sequence_number(&self) -> Option { + match self.frame_type() { + FrameType::Beacon + | FrameType::Data + | FrameType::Acknowledgement + | FrameType::MacCommand + | FrameType::Multipurpose => { + let data = self.buffer.as_ref(); + let raw = data[field::SEQUENCE_NUMBER]; + Some(raw) + } + FrameType::Extended | FrameType::FragmentOrFrak | FrameType::Unknown(_) => None, + } + } + + /// Return the addressing fields. + #[inline] + fn addressing_fields(&self) -> Option<&[u8]> { + match self.frame_type() { + FrameType::Beacon + | FrameType::Data + | FrameType::MacCommand + | FrameType::Multipurpose => (), + FrameType::Acknowledgement if self.frame_version() == FrameVersion::Ieee802154 => (), + FrameType::Acknowledgement + | FrameType::Extended + | FrameType::FragmentOrFrak + | FrameType::Unknown(_) => return None, + } + + let mut offset = 2; + + // Calculate the size of the addressing field. + offset += self.dst_addressing_mode().size(); + offset += self.src_addressing_mode().size(); + + if !self.pan_id_compression() { + offset += 2; + } + + Some(&self.buffer.as_ref()[field::ADDRESSING][..offset]) + } + + /// Return the destination PAN field. + #[inline] + pub fn dst_pan_id(&self) -> Option { + let addressing_fields = self.addressing_fields()?; + match self.dst_addressing_mode() { + AddressingMode::Absent => None, + AddressingMode::Short | AddressingMode::Extended => { + Some(Pan(LittleEndian::read_u16(&addressing_fields[0..2]))) + } + AddressingMode::Unknown(_) => None, + } + } + + /// Return the destination address field. + #[inline] + pub fn dst_addr(&self) -> Option
{ + let addressing_fields = self.addressing_fields()?; + match self.dst_addressing_mode() { + AddressingMode::Absent => Some(Address::Absent), + AddressingMode::Short => { + let mut raw = [0u8; 2]; + raw.clone_from_slice(&addressing_fields[2..4]); + raw.reverse(); + Some(Address::short_from_bytes(raw)) + } + AddressingMode::Extended => { + let mut raw = [0u8; 8]; + raw.clone_from_slice(&addressing_fields[2..10]); + raw.reverse(); + Some(Address::extended_from_bytes(raw)) + } + AddressingMode::Unknown(_) => None, + } + } + + /// Return the destination PAN field. + #[inline] + pub fn src_pan_id(&self) -> Option { + if self.pan_id_compression() { + return None; + } + + let addressing_fields = self.addressing_fields()?; + let offset = self.dst_addressing_mode().size() + 2; + + match self.src_addressing_mode() { + AddressingMode::Absent => None, + AddressingMode::Short | AddressingMode::Extended => Some(Pan(LittleEndian::read_u16( + &addressing_fields[offset..offset + 2], + ))), + AddressingMode::Unknown(_) => None, + } + } + + /// Return the source address field. + #[inline] + pub fn src_addr(&self) -> Option
{ + let addressing_fields = self.addressing_fields()?; + let mut offset = match self.dst_addressing_mode() { + AddressingMode::Absent => 0, + AddressingMode::Short => 2, + AddressingMode::Extended => 8, + _ => return None, // TODO(thvdveld): what do we do here? + } + 2; + + if !self.pan_id_compression() { + offset += 2; + } + + match self.src_addressing_mode() { + AddressingMode::Absent => Some(Address::Absent), + AddressingMode::Short => { + let mut raw = [0u8; 2]; + raw.clone_from_slice(&addressing_fields[offset..offset + 2]); + raw.reverse(); + Some(Address::short_from_bytes(raw)) + } + AddressingMode::Extended => { + let mut raw = [0u8; 8]; + raw.clone_from_slice(&addressing_fields[offset..offset + 8]); + raw.reverse(); + Some(Address::extended_from_bytes(raw)) + } + AddressingMode::Unknown(_) => None, + } + } + + /// Return the Auxilliary Security Header Field + #[inline] + pub fn aux_security_header(&self) -> Option<&[u8]> { + match self.frame_type() { + FrameType::Beacon + | FrameType::Data + | FrameType::MacCommand + | FrameType::Multipurpose => (), + FrameType::Acknowledgement if self.frame_version() == FrameVersion::Ieee802154 => (), + FrameType::Acknowledgement + | FrameType::Extended + | FrameType::FragmentOrFrak + | FrameType::Unknown(_) => return None, + } + + if !self.security_enabled() { + return None; + } + + todo!(); + } + + /// Return the IE field (the header as well as the payload IE) + #[inline] + pub fn ie_field(&self) -> Option<&[u8]> { + match self.frame_type() { + FrameType::Data | FrameType::MacCommand | FrameType::Multipurpose => (), + FrameType::Beacon | FrameType::Acknowledgement + if self.frame_version() == FrameVersion::Ieee802154 => {} + FrameType::Beacon + | FrameType::Acknowledgement + | FrameType::Extended + | FrameType::FragmentOrFrak + | FrameType::Unknown(_) => return None, + } + + if !self.ie_present() { + return None; + } + + todo!(); + } + + /// Return the FCS fields + #[inline] + pub fn fcs(&self) -> &[u8] { + &self.buffer.as_ref()[self.buffer.as_ref().len() - 2..] + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> { + /// Return a pointer to the payload. + #[inline] + pub fn payload(&self) -> Option<&'a [u8]> { + match self.frame_type() { + FrameType::Data => { + let data = &self.buffer.as_ref()[field::ADDRESSING]; + let offset = self.addressing_fields().unwrap().len(); + + Some(&data[offset..data.len() - 2]) // Remove the FCS field of the IEEE80.15.4 frame. + } + _ => None, + } + } +} + +impl + AsMut<[u8]>> Frame { + /// Set the frame type. + #[inline] + pub fn set_frame_type(&mut self, frame_type: FrameType) { + let data = &mut self.buffer.as_mut()[field::FRAMECONTROL]; + let mut raw = LittleEndian::read_u16(data); + + raw = (raw & !(0b111)) | (u8::from(frame_type) as u16 & 0b111); + data.copy_from_slice(&raw.to_le_bytes()); + } + + set_fc_bit_field!(set_security_enabled, 3); + set_fc_bit_field!(set_frame_pending, 4); + set_fc_bit_field!(set_ack_request, 5); + set_fc_bit_field!(set_pan_id_compression, 6); + + /// Set the frame version. + #[inline] + pub fn set_frame_version(&mut self, version: FrameVersion) { + let data = &mut self.buffer.as_mut()[field::FRAMECONTROL]; + let mut raw = LittleEndian::read_u16(data); + + raw = (raw & !(0b11 << 12)) | ((u8::from(version) as u16 & 0b11) << 12); + data.copy_from_slice(&raw.to_le_bytes()); + } + + /// Set the frame sequence number. + #[inline] + pub fn set_sequence_number(&mut self, value: u8) { + let data = self.buffer.as_mut(); + data[field::SEQUENCE_NUMBER] = value; + } + + /// Set the destination PAN ID. + #[inline] + pub fn set_dst_pan_id(&mut self, value: Pan) { + // NOTE the destination addressing mode must be different than Absent. + // This is the reason why we set it to Extended. + self.set_dst_addressing_mode(AddressingMode::Extended); + + let data = self.buffer.as_mut(); + data[field::ADDRESSING][..2].copy_from_slice(&value.as_bytes()); + } + + /// Set the destination address. + #[inline] + pub fn set_dst_addr(&mut self, mut value: Address) { + match value { + Address::Absent => self.set_dst_addressing_mode(AddressingMode::Absent), + Address::Short(ref mut value) => { + value.reverse(); + self.set_dst_addressing_mode(AddressingMode::Short); + let data = self.buffer.as_mut(); + data[field::ADDRESSING][2..2 + 2].copy_from_slice(value); + value.reverse(); + } + Address::Extended(ref mut value) => { + value.reverse(); + self.set_dst_addressing_mode(AddressingMode::Extended); + let data = &mut self.buffer.as_mut()[field::ADDRESSING]; + data[2..2 + 8].copy_from_slice(value); + value.reverse(); + } + } + } + + /// Set the destination addressing mode. + #[inline] + fn set_dst_addressing_mode(&mut self, value: AddressingMode) { + let data = &mut self.buffer.as_mut()[field::FRAMECONTROL]; + let mut raw = LittleEndian::read_u16(data); + + raw = (raw & !(0b11 << 10)) | ((u8::from(value) as u16 & 0b11) << 10); + data.copy_from_slice(&raw.to_le_bytes()); + } + + /// Set the source PAN ID. + #[inline] + pub fn set_src_pan_id(&mut self, value: Pan) { + let offset = match self.dst_addressing_mode() { + AddressingMode::Absent => todo!("{}", self.dst_addressing_mode()), + AddressingMode::Short => 2, + AddressingMode::Extended => 8, + _ => unreachable!(), + } + 2; + + let data = &mut self.buffer.as_mut()[field::ADDRESSING]; + data[offset..offset + 2].copy_from_slice(&value.as_bytes()); + } + + /// Set the source address. + #[inline] + pub fn set_src_addr(&mut self, mut value: Address) { + let offset = match self.dst_addressing_mode() { + AddressingMode::Absent => todo!("{}", self.dst_addressing_mode()), + AddressingMode::Short => 2, + AddressingMode::Extended => 8, + _ => unreachable!(), + } + 2; + + let offset = offset + if self.pan_id_compression() { 0 } else { 2 }; + + match value { + Address::Absent => self.set_src_addressing_mode(AddressingMode::Absent), + Address::Short(ref mut value) => { + value.reverse(); + self.set_src_addressing_mode(AddressingMode::Short); + let data = &mut self.buffer.as_mut()[field::ADDRESSING]; + data[offset..offset + 2].copy_from_slice(value); + value.reverse(); + } + Address::Extended(ref mut value) => { + value.reverse(); + self.set_src_addressing_mode(AddressingMode::Extended); + let data = &mut self.buffer.as_mut()[field::ADDRESSING]; + data[offset..offset + 8].copy_from_slice(value); + value.reverse(); + } + } + } + + /// Set the source addressing mode. + #[inline] + fn set_src_addressing_mode(&mut self, value: AddressingMode) { + let data = &mut self.buffer.as_mut()[field::FRAMECONTROL]; + let mut raw = LittleEndian::read_u16(data); + + raw = (raw & !(0b11 << 14)) | ((u8::from(value) as u16 & 0b11) << 14); + data.copy_from_slice(&raw.to_le_bytes()); + } + + /// Return a mutable pointer to the payload. + #[inline] + pub fn payload_mut(&mut self) -> Option<&mut [u8]> { + match self.frame_type() { + FrameType::Data => { + let mut start_offset = 3; + start_offset += self.addressing_fields().unwrap().len(); + + let data = self.buffer.as_mut(); + let end_offset = start_offset + data.len() - 2; + Some(&mut data[start_offset..end_offset]) + } + _ => None, + } + } +} + +impl> fmt::Display for Frame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "IEEE802.15.4 frame type={} seq={:2x?} dst_pan={:x?} dest={:x?} src_pan={:?} src={:x?} fcs={:x?}", + self.frame_type(), + self.sequence_number(), + self.dst_pan_id(), + self.dst_addr(), + self.src_pan_id(), + self.src_addr(), + self.fcs(), + ) + } +} + +/// A high-level representation of an IEEE802.15.4 frame. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Repr { + pub frame_type: FrameType, + pub security_enabled: bool, + pub frame_pending: bool, + pub ack_request: bool, + pub sequence_number: Option, + pub pan_id_compression: bool, + pub frame_version: FrameVersion, + pub dst_pan_id: Option, + pub dst_addr: Option
, + pub src_pan_id: Option, + pub src_addr: Option
, +} + +impl Repr { + /// Parse an IEEE 802.15.4 frame and return a high-level representation. + pub fn parse + ?Sized>(packet: &Frame<&T>) -> Result { + // Ensure the basic accessors will work. + packet.check_len()?; + + Ok(Repr { + frame_type: packet.frame_type(), + security_enabled: packet.security_enabled(), + frame_pending: packet.frame_pending(), + ack_request: packet.ack_request(), + sequence_number: packet.sequence_number(), + pan_id_compression: packet.pan_id_compression(), + frame_version: packet.frame_version(), + dst_pan_id: packet.dst_pan_id(), + dst_addr: packet.dst_addr(), + src_pan_id: packet.src_pan_id(), + src_addr: packet.src_addr(), + }) + } + + /// Return the length of a buffer required to hold a packet with the payload of a given length. + #[inline] + pub fn buffer_len(&self) -> usize { + 3 + 2 + + match self.dst_addr { + Some(Address::Absent) | None => 0, + Some(Address::Short(_)) => 2, + Some(Address::Extended(_)) => 8, + } + + if !self.pan_id_compression { 2 } else { 0 } + + match self.src_addr { + Some(Address::Absent) | None => 0, + Some(Address::Short(_)) => 2, + Some(Address::Extended(_)) => 8, + } + } + + /// Emit a high-level representation into an IEEE802.15.4 frame. + pub fn emit + AsMut<[u8]>>(&self, frame: &mut Frame) { + frame.set_frame_type(self.frame_type); + frame.set_security_enabled(self.security_enabled); + frame.set_frame_pending(self.frame_pending); + frame.set_ack_request(self.ack_request); + frame.set_pan_id_compression(self.pan_id_compression); + frame.set_frame_version(self.frame_version); + + if let Some(sequence_number) = self.sequence_number { + frame.set_sequence_number(sequence_number); + } + + if let Some(dst_pan_id) = self.dst_pan_id { + frame.set_dst_pan_id(dst_pan_id); + } + if let Some(dst_addr) = self.dst_addr { + frame.set_dst_addr(dst_addr); + } + + if !self.pan_id_compression && self.src_pan_id.is_some() { + frame.set_src_pan_id(self.src_pan_id.unwrap()); + } + + if let Some(src_addr) = self.src_addr { + frame.set_src_addr(src_addr); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Result; + + #[test] + fn test_broadcast() { + assert!(Address::BROADCAST.is_broadcast()); + assert!(!Address::BROADCAST.is_unicast()); + } + + #[test] + fn prepare_frame() { + let mut buffer = [0u8; 128]; + + let repr = Repr { + frame_type: FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: true, + pan_id_compression: true, + frame_version: FrameVersion::Ieee802154, + sequence_number: Some(1), + dst_pan_id: Some(Pan(0xabcd)), + dst_addr: Some(Address::BROADCAST), + src_pan_id: None, + src_addr: Some(Address::Extended([ + 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, + ])), + }; + + let buffer_len = repr.buffer_len(); + + let mut frame = Frame::new_unchecked(&mut buffer[..buffer_len]); + repr.emit(&mut frame); + + println!("{:2x?}", frame); + + assert_eq!(frame.frame_type(), FrameType::Data); + assert!(!frame.security_enabled()); + assert!(!frame.frame_pending()); + assert!(frame.ack_request()); + assert!(frame.pan_id_compression()); + assert_eq!(frame.frame_version(), FrameVersion::Ieee802154); + assert_eq!(frame.sequence_number(), Some(1)); + assert_eq!(frame.dst_pan_id(), Some(Pan(0xabcd))); + assert_eq!(frame.dst_addr(), Some(Address::BROADCAST)); + assert_eq!(frame.src_pan_id(), None); + assert_eq!( + frame.src_addr(), + Some(Address::Extended([ + 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00 + ])) + ); + } + + macro_rules! vector_test { + ($name:ident $bytes:expr ; $($test_method:ident -> $expected:expr,)*) => { + #[test] + #[allow(clippy::bool_assert_comparison)] + fn $name() -> Result<()> { + let frame = &$bytes; + let frame = Frame::new_checked(frame)?; + + $( + assert_eq!(frame.$test_method(), $expected, stringify!($test_method)); + )* + + Ok(()) + } + } + } + + vector_test! { + extended_addr + [ + 0b0000_0001, 0b1100_1100, // frame control + 0b0, // seq + 0xcd, 0xab, // pan id + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, // dst addr + 0x03, 0x04, // pan id + 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, // src addr + ]; + frame_type -> FrameType::Data, + dst_addr -> Some(Address::Extended([0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00])), + src_addr -> Some(Address::Extended([0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00])), + dst_pan_id -> Some(Pan(0xabcd)), + } + + vector_test! { + short_addr + [ + 0x01, 0x98, // frame control + 0x00, // sequence number + 0x34, 0x12, 0x78, 0x56, // PAN identifier and address of destination + 0x34, 0x12, 0xbc, 0x9a, // PAN identifier and address of source + ]; + frame_type -> FrameType::Data, + security_enabled -> false, + frame_pending -> false, + ack_request -> false, + pan_id_compression -> false, + dst_addressing_mode -> AddressingMode::Short, + frame_version -> FrameVersion::Ieee802154_2006, + src_addressing_mode -> AddressingMode::Short, + dst_pan_id -> Some(Pan(0x1234)), + dst_addr -> Some(Address::Short([0x56, 0x78])), + src_pan_id -> Some(Pan(0x1234)), + src_addr -> Some(Address::Short([0x9a, 0xbc])), + } + + vector_test! { + zolertia_remote + [ + 0x41, 0xd8, // frame control + 0x01, // sequence number + 0xcd, 0xab, // Destination PAN id + 0xff, 0xff, // Short destination address + 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, // Extended source address + 0x2b, 0x00, 0x00, 0x00, // payload + 0xb3, 0x0d // FSM + ]; + frame_type -> FrameType::Data, + security_enabled -> false, + frame_pending -> false, + ack_request -> false, + pan_id_compression -> true, + dst_addressing_mode -> AddressingMode::Short, + frame_version -> FrameVersion::Ieee802154_2006, + src_addressing_mode -> AddressingMode::Extended, + //payload -> Some(&[0x2b, 0x00, 0x00, 0x00]), + fcs -> [0xb3, 0x0d], + } +} diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index c2bfd0c90..6d5dd2baf 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -337,23 +337,19 @@ impl<'a> fmt::Display for Repr { max_resp_time, group_addr, version, - } => { - write!( - f, - "IGMP membership query max_resp_time={} group_addr={} version={:?}", - max_resp_time, group_addr, version - ) - } + } => write!( + f, + "IGMP membership query max_resp_time={} group_addr={} version={:?}", + max_resp_time, group_addr, version + ), Repr::MembershipReport { group_addr, version, - } => { - write!( - f, - "IGMP membership report group_addr={} version={:?}", - group_addr, version - ) - } + } => write!( + f, + "IGMP membership report group_addr={} version={:?}", + group_addr, version + ), Repr::LeaveGroup { group_addr } => { write!(f, "IGMP leave group group_addr={})", group_addr) } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 16c9de616..a084025e1 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -6,6 +6,8 @@ use crate::phy::ChecksumCapabilities; use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; +#[cfg(feature = "proto-sixlowpan")] +use crate::wire::{SixlowpanIphcPacket, SixlowpanIphcRepr}; use crate::{Error, Result}; /// Internet protocol version. @@ -513,6 +515,8 @@ pub enum Repr { Ipv4(Ipv4Repr), #[cfg(feature = "proto-ipv6")] Ipv6(Ipv6Repr), + #[cfg(feature = "proto-sixlowpan")] + Sixlowpan(SixlowpanIphcRepr), } #[cfg(feature = "proto-ipv4")] @@ -538,6 +542,8 @@ impl Repr { Repr::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(_) => Version::Ipv6, + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(_) => Version::Ipv6, } } @@ -549,6 +555,8 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr), + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => Address::Ipv6(repr.src_addr), } } @@ -560,6 +568,8 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr), + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => Address::Ipv6(repr.dst_addr), } } @@ -571,6 +581,8 @@ impl Repr { Repr::Ipv4(repr) => repr.protocol, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.next_header, + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => todo!("{:?}", repr), } } @@ -582,6 +594,8 @@ impl Repr { Repr::Ipv4(repr) => repr.payload_len, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.payload_len, + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => todo!("{:?}", repr), } } @@ -602,6 +616,8 @@ impl Repr { ref mut payload_len, .. }) => *payload_len = length, + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(_) => todo!(), } } @@ -613,6 +629,8 @@ impl Repr { Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit, + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(SixlowpanIphcRepr { hop_limit, .. }) => hop_limit, } } @@ -753,6 +771,9 @@ impl Repr { resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs) } + #[cfg(feature = "proto-sixlowpan")] + &Repr::Sixlowpan(_) => todo!(), // TODO(thvdveld): what do we need to do here? + &Repr::Unspecified { .. } => { panic!("source and destination IP address families do not match") } @@ -770,6 +791,8 @@ impl Repr { Repr::Ipv4(repr) => repr.buffer_len(), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.buffer_len(), + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => repr.buffer_len(), } } @@ -788,6 +811,8 @@ impl Repr { Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), + #[cfg(feature = "proto-sixlowpan")] + Repr::Sixlowpan(repr) => repr.emit(&mut SixlowpanIphcPacket::new_unchecked(buffer)), } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index fdb155ada..84e55408a 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -503,17 +503,15 @@ impl<'a> fmt::Display for Repr<'a> { length, segments_left, home_address, - } => { - write!( - f, - "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", - next_header, - length, - Type::Type2, - segments_left, - home_address - ) - } + } => write!( + f, + "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", + next_header, + length, + Type::Type2, + segments_left, + home_address + ), Repr::Rpl { next_header, length, @@ -522,10 +520,17 @@ impl<'a> fmt::Display for Repr<'a> { cmpr_e, pad, .. - } => { - write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", - next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) - } + } => write!( + f, + "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", + next_header, + length, + Type::Rpl, + segments_left, + cmpr_i, + cmpr_e, + pad + ), } } } diff --git a/src/wire/mld.rs b/src/wire/mld.rs index c77e1dae7..cf3b5f485 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -532,6 +532,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &packet, &ChecksumCapabilities::default(), + &crate::phy::Medium::Ethernet, ); assert_eq!(repr, Ok(create_repr(Message::MldQuery))); } @@ -544,6 +545,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &packet, &ChecksumCapabilities::default(), + &crate::phy::Medium::Ethernet, ); assert_eq!(repr, Ok(create_repr(Message::MldReport))); } @@ -558,6 +560,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &mut packet, &ChecksumCapabilities::default(), + &crate::phy::Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); } @@ -572,6 +575,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &mut packet, &ChecksumCapabilities::default(), + &crate::phy::Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index e1dc18d32..321a67fc8 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -89,6 +89,8 @@ mod icmp; mod icmpv4; #[cfg(feature = "proto-ipv6")] mod icmpv6; +#[cfg(feature = "medium-ieee802154")] +pub mod ieee802154; #[cfg(feature = "proto-igmp")] mod igmp; pub(crate) mod ip; @@ -106,10 +108,18 @@ mod ipv6option; mod ipv6routing; #[cfg(feature = "proto-ipv6")] mod mld; -#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] +#[cfg(all( + feature = "proto-ipv6", + any(feature = "medium-ethernet", feature = "medium-ieee802154") +))] mod ndisc; -#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] +#[cfg(all( + feature = "proto-ipv6", + any(feature = "medium-ethernet", feature = "medium-ieee802154") +))] mod ndiscoption; +#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] +mod sixlowpan; mod tcp; mod udp; @@ -126,6 +136,24 @@ pub use self::arp::{ Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr, }; +#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] +pub use self::sixlowpan::{ + iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr}, + nhc::{ + ExtensionHeaderPacket as SixlowpanExtHeaderPacket, + ExtensionHeaderRepr as SixlowpanExtHeaderRepr, Packet as SixlowpanNhcPacket, + UdpNhcRepr as SixlowpanUdpRepr, UdpPacket as SixlowpanUdpPacket, + }, + NextHeader as SixlowpanNextHeader, +}; + +#[cfg(feature = "medium-ieee802154")] +pub use self::ieee802154::{ + Address as Ieee802154Address, AddressingMode as Ieee802154AddressingMode, + Frame as Ieee802154Frame, FrameType as Ieee802154FrameType, + FrameVersion as Ieee802154FrameVersion, Pan as Ieee802154Pan, Repr as Ieee802154Repr, +}; + pub use self::ip::{ Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol, Repr as IpRepr, Version as IpVersion, @@ -177,12 +205,18 @@ pub use self::icmpv6::{ #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] pub use self::icmp::Repr as IcmpRepr; -#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] +#[cfg(all( + feature = "proto-ipv6", + any(feature = "medium-ethernet", feature = "medium-ieee802154") +))] pub use self::ndisc::{ NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags, }; -#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] +#[cfg(all( + feature = "proto-ipv6", + any(feature = "medium-ethernet", feature = "medium-ieee802154") +))] pub use self::ndiscoption::{ NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags, PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader, @@ -205,3 +239,74 @@ pub use self::dhcpv4::{ CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT, }; + +/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum HardwareAddress { + BROADCAST, + #[cfg(feature = "medium-ethernet")] + Ethernet(EthernetAddress), + #[cfg(feature = "medium-ieee802154")] + Ieee802154(Ieee802154Address), +} + +impl HardwareAddress { + pub fn as_bytes(&self) -> &[u8] { + match self { + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(addr) => addr.as_bytes(), + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(addr) => addr.as_bytes(), + _ => todo!(), + } + } + + /// Query wether the address is an unicast address. + pub fn is_unicast(&self) -> bool { + match self { + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(addr) => addr.is_unicast(), + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(addr) => addr.is_unicast(), + _ => todo!(), + } + } + + /// Query wether the address is a broadcast address. + pub fn is_broadcast(&self) -> bool { + match self { + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(addr) => addr.is_broadcast(), + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(addr) => addr.is_broadcast(), + _ => todo!(), + } + } +} + +impl core::fmt::Display for HardwareAddress { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + HardwareAddress::BROADCAST => write!(f, "BROADCAST"), + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(addr) => write!(f, "{}", addr), + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(addr) => write!(f, "{}", addr), + } + } +} + +#[cfg(feature = "medium-ethernet")] +impl From for HardwareAddress { + fn from(addr: EthernetAddress) -> Self { + HardwareAddress::Ethernet(addr) + } +} + +#[cfg(feature = "medium-ieee802154")] +impl From for HardwareAddress { + fn from(addr: Ieee802154Address) -> Self { + HardwareAddress::Ieee802154(addr) + } +} diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 89c8c711b..1d9690948 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -1,10 +1,12 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; +use crate::phy::Medium; use crate::time::Duration; use crate::wire::icmpv6::{field, Message, Packet}; +use crate::wire::HardwareAddress; use crate::wire::Ipv6Address; -use crate::wire::{EthernetAddress, Ipv6Packet, Ipv6Repr}; +use crate::wire::{Ipv6Packet, Ipv6Repr}; use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; use crate::{Error, Result}; @@ -193,7 +195,7 @@ impl + AsMut<[u8]>> Packet { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { RouterSolicit { - lladdr: Option, + lladdr: Option, }, RouterAdvert { hop_limit: u8, @@ -201,23 +203,23 @@ pub enum Repr<'a> { router_lifetime: Duration, reachable_time: Duration, retrans_time: Duration, - lladdr: Option, + lladdr: Option, mtu: Option, prefix_info: Option, }, NeighborSolicit { target_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, }, NeighborAdvert { flags: NeighborFlags, target_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, }, Redirect { target_addr: Ipv6Address, dest_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, redirected_hdr: Option>, }, } @@ -225,7 +227,7 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC packet and return a high-level representation of the /// packet. - pub fn parse(packet: &Packet<&'a T>) -> Result> + pub fn parse(packet: &Packet<&'a T>, medium: &Medium) -> Result> where T: AsRef<[u8]> + ?Sized, { @@ -234,7 +236,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), + NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), _ => { return Err(Error::Unrecognized); } @@ -249,7 +251,7 @@ impl<'a> Repr<'a> { let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); while packet.payload().len() - offset > 0 { let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?; - let opt = NdiscOptionRepr::parse(&pkt)?; + let opt = NdiscOptionRepr::parse(&pkt, medium)?; match opt { NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::Mtu(val) => mtu = Some(val), @@ -258,7 +260,7 @@ impl<'a> Repr<'a> { return Err(Error::Unrecognized); } } - offset += opt.buffer_len(); + offset += opt.buffer_len(medium); } Ok(Repr::RouterAdvert { hop_limit: packet.current_hop_limit(), @@ -275,7 +277,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), + NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), _ => { return Err(Error::Unrecognized); } @@ -292,7 +294,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), + NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr(medium)), _ => { return Err(Error::Unrecognized); } @@ -313,7 +315,7 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(&packet.payload()[offset..])?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => { - lladdr = Some(opt.link_layer_addr()); + lladdr = Some(opt.link_layer_addr(medium)); offset += 8; } NdiscOptionType::RedirectedHeader => { @@ -347,7 +349,7 @@ impl<'a> Repr<'a> { } } - pub fn buffer_len(&self) -> usize { + pub fn buffer_len(&self, medium: &Medium) -> usize { match self { &Repr::RouterSolicit { lladdr } => match lladdr { Some(_) => field::UNUSED.end + 8, @@ -373,7 +375,31 @@ impl<'a> Repr<'a> { } &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => { match lladdr { - Some(_) => field::TARGET_ADDR.end + 8, + Some(_addr) => { + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => field::TARGET_ADDR.end + 8, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + // XXX: This is for 6LoWPAN + let mut len = field::TARGET_ADDR.end; + len += 2; + len += _addr.as_bytes().len(); + + // A packet of len == 30 is a packet with an Ethernet address + if len > 30 { + // TODO(thvdveld): find out why this padding is +4 and not +6 + // WireShark wants a padding of +6, however, then the packet is not accepted when using ping + // When a padding of +4 is used, then WireShark complains and says that the packet is malformed, + // however, ping accepts this packet. + len += 4; // Padding + } + len + } + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + } + } None => field::TARGET_ADDR.end, } } @@ -394,7 +420,7 @@ impl<'a> Repr<'a> { } } - pub fn emit(&self, packet: &mut Packet<&mut T>) + pub fn emit(&self, packet: &mut Packet<&mut T>, medium: &Medium) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { @@ -405,7 +431,7 @@ impl<'a> Repr<'a> { packet.clear_reserved(); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); + NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); } } @@ -429,19 +455,26 @@ impl<'a> Repr<'a> { let mut offset = 0; if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); - offset += 8; + NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => offset += 6, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => offset += 8, + #[cfg(feature = "medium-ip")] + _ => unreachable!(), + } } if let Some(mtu) = mtu { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt); + NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt, medium); offset += 8; } if let Some(prefix_info) = prefix_info { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt) + NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt, medium) } } @@ -455,7 +488,7 @@ impl<'a> Repr<'a> { packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); + NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); } } @@ -471,7 +504,7 @@ impl<'a> Repr<'a> { packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); + NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); } } @@ -489,7 +522,7 @@ impl<'a> Repr<'a> { let offset = match lladdr { Some(lladdr) => { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); + NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); 8 } None => 0, @@ -497,7 +530,7 @@ impl<'a> Repr<'a> { if let Some(redirected_hdr) = redirected_hdr { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt); + NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt, medium); } } } @@ -509,6 +542,8 @@ mod test { use super::*; use crate::phy::ChecksumCapabilities; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; + use crate::wire::EthernetAddress; + use crate::wire::HardwareAddress; use crate::wire::Icmpv6Repr; static ROUTER_ADVERT_BYTES: [u8; 24] = [ @@ -524,7 +559,9 @@ mod test { router_lifetime: Duration::from_secs(900), reachable_time: Duration::from_millis(900), retrans_time: Duration::from_millis(900), - lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])), + lladdr: Some(HardwareAddress::Ethernet(EthernetAddress([ + 0x52, 0x54, 0x00, 0x12, 0x34, 0x56, + ]))), mtu: None, prefix_info: None, }) @@ -569,7 +606,8 @@ mod test { &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &packet, - &ChecksumCapabilities::default() + &ChecksumCapabilities::default(), + &Medium::Ethernet, ) .unwrap(), create_repr() @@ -585,6 +623,7 @@ mod test { &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), + &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 6c79b5ff2..59e212b84 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -2,10 +2,18 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use crate::phy::Medium; use crate::time::Duration; -use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; +use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr}; use crate::{Error, Result}; +#[cfg(feature = "medium-ethernet")] +use crate::wire::EthernetAddress; +#[cfg(feature = "medium-ieee802154")] +use crate::wire::Ieee802154Address; + +use crate::wire::HardwareAddress; + enum_with_unknown! { /// NDISC Option Type pub enum Type(u8) { @@ -83,7 +91,10 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Link-Layer Address - pub const LL_ADDR: Field = 2..8; + #[cfg(feature = "medium-ethernet")] + pub const LL_ADDR_ETHERNET: Field = 2..8; + #[cfg(feature = "medium-ieee802154")] + pub const LL_ADDR_IEEE802154: Field = 2..10; // Prefix Information Option fields. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -214,9 +225,23 @@ impl> NdiscOption { impl> NdiscOption { /// Return the Source/Target Link-layer Address. #[inline] - pub fn link_layer_addr(&self) -> EthernetAddress { + pub fn link_layer_addr(&self, medium: &Medium) -> HardwareAddress { let data = self.buffer.as_ref(); - EthernetAddress::from_bytes(&data[field::LL_ADDR]) + + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + let addr = &data[field::LL_ADDR_ETHERNET]; + HardwareAddress::Ethernet(EthernetAddress::from_bytes(addr)) + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + let addr = &data[field::LL_ADDR_IEEE802154]; + HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(addr)) + } + #[cfg(feature = "medium-ip")] + _ => todo!(), // TODO(thvdveld) + } } } @@ -297,9 +322,21 @@ impl + AsMut<[u8]>> NdiscOption { impl + AsMut<[u8]>> NdiscOption { /// Set the Source/Target Link-layer Address. #[inline] - pub fn set_link_layer_addr(&mut self, addr: EthernetAddress) { + pub fn set_link_layer_addr(&mut self, addr: HardwareAddress) { let data = self.buffer.as_mut(); - data[field::LL_ADDR].copy_from_slice(addr.as_bytes()) + match addr { + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(addr) => { + let data = &mut data[field::LL_ADDR_ETHERNET]; + data.copy_from_slice(addr.as_bytes()); + } + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(addr) => { + let data = &mut data[field::LL_ADDR_IEEE802154]; + data.copy_from_slice(addr.as_bytes()); + } + _ => todo!(), + } } } @@ -376,17 +413,18 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> { } } -impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), - Err(err) => { - write!(f, "NDISC Option ({})", err)?; - Ok(()) - } - } - } -} +//impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { +//fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//// XXX(thvdveld): how do we pass the medium? +//match Repr::parse(self, &Medium::Ethernet) { +//Ok(repr) => write!(f, "{}", repr), +//Err(err) => { +//write!(f, "NDISC Option ({})", err)?; +//Ok(()) +//} +//} +//} +//} #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -409,8 +447,8 @@ pub struct RedirectedHeader<'a> { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { - SourceLinkLayerAddr(EthernetAddress), - TargetLinkLayerAddr(EthernetAddress), + SourceLinkLayerAddr(HardwareAddress), + TargetLinkLayerAddr(HardwareAddress), PrefixInformation(PrefixInformation), RedirectedHeader(RedirectedHeader<'a>), Mtu(u32), @@ -423,21 +461,21 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC Option and return a high-level representation. - pub fn parse(opt: &'a NdiscOption<&'a T>) -> Result> + pub fn parse(opt: &'a NdiscOption<&'a T>, medium: &Medium) -> Result> where T: AsRef<[u8]> + ?Sized, { match opt.option_type() { Type::SourceLinkLayerAddr => { if opt.data_len() == 1 { - Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr())) + Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr(medium))) } else { Err(Error::Malformed) } } Type::TargetLinkLayerAddr => { if opt.data_len() == 1 { - Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) + Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr(medium))) } else { Err(Error::Malformed) } @@ -486,32 +524,57 @@ impl<'a> Repr<'a> { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { - match self { - &Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end, - &Repr::PrefixInformation(_) => field::PREFIX.end, - &Repr::RedirectedHeader(RedirectedHeader { header, data }) => { + pub fn buffer_len(&self, medium: &Medium) -> usize { + match (self, medium) { + #[cfg(feature = "medium-ethernet")] + (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ethernet) => { + field::LL_ADDR_ETHERNET.end + } + #[cfg(feature = "medium-ieee802154")] + (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ieee802154) => { + field::LL_ADDR_IEEE802154.end + } + #[cfg(feature = "medium-ip")] + (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), _) => { + unreachable!() + } + (&Repr::PrefixInformation(_), _) => field::PREFIX.end, + (&Repr::RedirectedHeader(RedirectedHeader { header, data }), _) => { field::IP_DATA + header.buffer_len() + data.len() } - &Repr::Mtu(_) => field::MTU.end, - &Repr::Unknown { length, .. } => field::DATA(length).end, + (&Repr::Mtu(_), _) => field::MTU.end, + (&Repr::Unknown { length, .. }, _) => field::DATA(length).end, } } /// Emit a high-level representation into an NDISC Option. - pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>) + pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>, medium: &Medium) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { match *self { Repr::SourceLinkLayerAddr(addr) => { opt.set_option_type(Type::SourceLinkLayerAddr); - opt.set_data_len(1); + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => opt.set_data_len(1), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => opt.set_data_len(2), + #[cfg(feature = "medium-ip")] + _ => unreachable!(), + } opt.set_link_layer_addr(addr); } Repr::TargetLinkLayerAddr(addr) => { opt.set_option_type(Type::TargetLinkLayerAddr); - opt.set_data_len(1); + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => opt.set_data_len(1), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => opt.set_data_len(2), + #[cfg(feature = "medium-ip")] + _ => unreachable!(), + } opt.set_link_layer_addr(addr); } Repr::PrefixInformation(PrefixInformation { @@ -563,51 +626,42 @@ impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "NDISC Option: ")?; match *self { - Repr::SourceLinkLayerAddr(addr) => { - write!(f, "SourceLinkLayer addr={}", addr) - } - Repr::TargetLinkLayerAddr(addr) => { - write!(f, "TargetLinkLayer addr={}", addr) - } + Repr::SourceLinkLayerAddr(addr) => write!(f, "SourceLinkLayer addr={}", addr), + Repr::TargetLinkLayerAddr(addr) => write!(f, "TargetLinkLayer addr={}", addr), Repr::PrefixInformation(PrefixInformation { prefix, prefix_len, .. - }) => { - write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) - } + }) => write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len), Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { write!(f, "RedirectedHeader header={}", header) } - Repr::Mtu(mtu) => { - write!(f, "MTU mtu={}", mtu) - } + Repr::Mtu(mtu) => write!(f, "MTU mtu={}", mtu), Repr::Unknown { type_: id, length, .. - } => { - write!(f, "Unknown({}) length={}", id, length) - } + } => write!(f, "Unknown({}) length={}", id, length), } } } -use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; - -impl> PrettyPrint for NdiscOption { - fn pretty_print( - buffer: &dyn AsRef<[u8]>, - f: &mut fmt::Formatter, - indent: &mut PrettyIndent, - ) -> fmt::Result { - match NdiscOption::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), - Ok(ndisc) => match Repr::parse(&ndisc) { - Err(_) => Ok(()), - Ok(repr) => { - write!(f, "{}{}", indent, repr) - } - }, - } - } -} +//use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; + +//impl> PrettyPrint for NdiscOption { +//fn pretty_print( +//buffer: &dyn AsRef<[u8]>, +//f: &mut fmt::Formatter, +//indent: &mut PrettyIndent, +//) -> fmt::Result { +//// TODO(thvdveld): how do we pass the medium? +//match NdiscOption::new_checked(buffer) { +//Err(err) => return write!(f, "{}({})", indent, err), +//Ok(ndisc) => match Repr::parse(&ndisc, &Medium::Ethernet) { +//Err(_) => Ok(()), +//Ok(repr) => { +//write!(f, "{}{}", indent, repr) +//} +//}, +//} +//} +//} #[cfg(test)] mod test { @@ -667,15 +721,21 @@ mod test { let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); { assert_eq!( - Repr::parse(&NdiscOption::new_unchecked(&bytes)), - Ok(Repr::SourceLinkLayerAddr(addr)) + Repr::parse( + &NdiscOption::new_unchecked(&bytes), + &crate::phy::Medium::Ethernet + ), + Ok(Repr::SourceLinkLayerAddr(addr.into())) ); } bytes[0] = 0x02; { assert_eq!( - Repr::parse(&NdiscOption::new_unchecked(&bytes)), - Ok(Repr::TargetLinkLayerAddr(addr)) + Repr::parse( + &NdiscOption::new_unchecked(&bytes), + &crate::phy::Medium::Ethernet + ), + Ok(Repr::TargetLinkLayerAddr(addr.into())) ); } } @@ -690,7 +750,10 @@ mod test { prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); assert_eq!( - Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), + Repr::parse( + &NdiscOption::new_unchecked(&PREFIX_OPT_BYTES), + &crate::phy::Medium::Ethernet + ), Ok(repr) ); } @@ -706,7 +769,7 @@ mod test { prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); let mut opt = NdiscOption::new_unchecked(&mut bytes); - repr.emit(&mut opt); + repr.emit(&mut opt, &crate::phy::Medium::Ethernet); assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]); } @@ -714,7 +777,10 @@ mod test { fn test_repr_parse_mtu() { let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc]; assert_eq!( - Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Repr::parse( + &NdiscOption::new_unchecked(&bytes), + &crate::phy::Medium::Ethernet + ), Ok(Repr::Mtu(1500)) ); } diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs new file mode 100644 index 000000000..3bef91202 --- /dev/null +++ b/src/wire/sixlowpan.rs @@ -0,0 +1,1704 @@ +use crate::wire::ieee802154::Address as LlAddress; +use crate::wire::ipv6; +use crate::wire::IpProtocol; +use crate::Error; +use crate::Result; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum NextHeader { + Compressed, + Uncompressed(IpProtocol), +} + +/// A wrapper around the address provided in the 6LoWPAN_IPHC header. +/// This requires some context to convert it the an IPv6 address in some cases. +/// For 802.15.4 the context are the short/extended addresses. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Address<'a> { + Complete(ipv6::Address), + WithContext(&'a [u8]), + Elided, + Reserved, +} + +impl<'a> Address<'a> { + /// Resolve the address provided by the IPHC encoding. + pub(crate) fn resolve(self, ll_addr: Option) -> Result { + match self { + Address::Complete(addr) => Ok(addr), + Address::Elided => { + let mut bytes = [0; 16]; + bytes[0] = 0xfe; + bytes[1] = 0x80; + + match ll_addr { + Some(LlAddress::Short(ll)) => { + bytes[11] = 0xff; + bytes[12] = 0xfe; + bytes[14..].copy_from_slice(&ll); + } + Some(LlAddress::Extended(ll)) => { + bytes[8..].copy_from_slice(&LlAddress::Extended(ll).as_eui_64().unwrap()); + } + _ => return Err(Error::Malformed), + } + + Ok(ipv6::Address::from_bytes(&bytes)) + } + Address::WithContext(_) => Err(Error::NotSupported), + Address::Reserved => Err(Error::Malformed), + } + } +} + +pub mod iphc { + use crate::wire::ieee802154::Address as LlAddress; + use crate::wire::ipv6; + use crate::wire::IpProtocol; + use crate::Error; + use crate::Result; + use byteorder::{ByteOrder, NetworkEndian}; + + use super::Address; + use super::NextHeader; + + mod field { + #![allow(non_snake_case)] + + use crate::wire::field::*; + + pub const IPHC_FIELD: Field = 0..2; + } + + const DISPATCH: u8 = 0b011; + + macro_rules! get_field { + ($name:ident, $mask:expr, $shift:expr) => { + fn $name(&self) -> u8 { + let data = self.buffer.as_ref(); + let raw = NetworkEndian::read_u16(&data[field::IPHC_FIELD]); + ((raw >> $shift) & $mask) as u8 + } + }; + } + + macro_rules! set_field { + ($name:ident, $mask:expr, $shift:expr) => { + fn $name(&mut self, val: u8) { + let data = &mut self.buffer.as_mut()[field::IPHC_FIELD]; + let mut raw = NetworkEndian::read_u16(data); + + raw = (raw & !($mask << $shift)) | ((val as u16) << $shift); + NetworkEndian::write_u16(data, raw); + } + }; + } + + /// A read/write wrapper around a LOWPAN_IPHC frame buffer. + #[derive(Debug, Clone)] + pub struct Packet> { + buffer: T, + } + + impl> Packet { + /// Input a raw octet buffer with a 6LoWPAN_IPHC frame structure. + pub fn new_unchecked(buffer: T) -> Packet { + Packet { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result> { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Truncated)` if the buffer is too short. + pub fn check_len(&self) -> Result<()> { + let buffer = self.buffer.as_ref(); + if buffer.len() < 2 { + Err(Error::Truncated) + } else { + Ok(()) + } + } + + /// Consumes the frame, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + /// Parse the next header field. + pub fn next_header(&self) -> NextHeader { + let nh = self.nh_field(); + + if nh == 1 { + // The next header field is compressed. + // It is also encoded using LOWPAN_NHC. + NextHeader::Compressed + } else { + // The full 8 bits for Next Header are carried in-line. + let start = (self.ip_fields_start() + self.traffic_class_size()) as usize; + + let data = self.buffer.as_ref(); + let nh = data[start..start + 1][0]; + NextHeader::Uncompressed(IpProtocol::from(nh)) + } + } + + /// Parse the hop limit field. + pub fn hop_limit(&self) -> u8 { + match self.hlim_field() { + 0b00 => { + let start = (self.ip_fields_start() + + self.traffic_class_size() + + self.next_header_size()) as usize; + + let data = self.buffer.as_ref(); + data[start..start + 1][0] + } + 0b01 => 1, + 0b10 => 64, + 0b11 => 255, + _ => unreachable!(), + } + } + + /// Return the source context identifier. + pub fn src_context_id(&self) -> Option { + if self.cid_field() == 1 { + let data = self.buffer.as_ref(); + Some(data[1] >> 4) + } else { + None + } + } + + /// Return the destination context identifier. + pub fn dst_context_id(&self) -> Option { + if self.cid_field() == 1 { + let data = self.buffer.as_ref(); + Some(data[1] & 0x0f) + } else { + None + } + } + + /// Parse the source address field. + pub fn src_addr(&self) -> Result
{ + let start = (self.ip_fields_start() + + self.traffic_class_size() + + self.next_header_size() + + self.hop_limit_size()) as usize; + + match (self.sac_field(), self.sam_field()) { + (0, 0b00) => { + // The full address is carried in-line. + let data = self.buffer.as_ref(); + Ok(Address::Complete(ipv6::Address::from_bytes( + &data[start..start + 16], + ))) + } + (0, 0b01) => { + // The first 64-bits of the address is elided. + // The value of those bits is the link-local prefix padded with zeros. + // The remaining 64-bits are carried in-line. + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + // Link-local prefix + bytes[0] = 0xfe; + bytes[1] = 0x80; + + bytes[8..].copy_from_slice(&data[start..start + 8]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (0, 0b10) => { + // The first 112 bits of the address are elided. + // The value of the 64 bits is the link-local prefix padded with zeros. + // The following 64 bits are 0000:00ff:fe00:XXXX, + // where XXXX are the bits carried in-line. + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + // Link-local prefix + bytes[0] = 0xfe; + bytes[1] = 0x80; + + bytes[11] = 0xff; + bytes[12] = 0xfe; + + bytes[14..].copy_from_slice(&data[start..start + 2]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (0, 0b11) => { + // The address is fully elided. + // The first 64 bits of the address are the link-local prefix padded with zeros. + // The remaining 64 bits are computed from the encapsulating header. + Ok(Address::Elided) + } + (1, 0b00) => Ok(Address::Complete(ipv6::Address::UNSPECIFIED)), + (1, 0b01) => { + // The address is derived using context information and the 64 bits carried in-line. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + let data = self.buffer.as_ref(); + let bytes = &data[start..start + 8]; + + Ok(Address::WithContext(bytes)) + } + (1, 0b10) => { + // The address is derived using context information and the 16 bits carried in-line. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + let data = self.buffer.as_ref(); + let bytes = &data[start..start + 2]; + + Ok(Address::WithContext(bytes)) + } + (1, 0b11) => { + // The address is fully elided and is derived using context information and the encapsulating header. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + Ok(Address::WithContext(&[])) + } + _ => Err(Error::Malformed), + } + } + + /// Parse the destination address field. + pub fn dst_addr(&self) -> Result
{ + let start = (self.ip_fields_start() + + self.traffic_class_size() + + self.next_header_size() + + self.hop_limit_size() + + self.src_address_size()) as usize; + + match (self.m_field(), self.dac_field(), self.dam_field()) { + (0, 0, 0b00) => { + // The full address is carried in-line. + let data = self.buffer.as_ref(); + Ok(Address::Complete(ipv6::Address::from_bytes( + &data[start..start + 16], + ))) + } + (0, 0, 0b01) => { + // The first 64-bits of the address is elided. + // The value of those bits is the link-local prefix padded with zeros. + // The remaining 64-bits are carried in-line. + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + // Link-local prefix + bytes[0] = 0xfe; + bytes[1] = 0x80; + + bytes[8..].copy_from_slice(&data[start..start + 8]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (0, 0, 0b10) => { + // The first 112 bits of the address are elided. + // The value of the 64 bits is the link-local prefix padded with zeros. + // The following 64 bits are 0000:00ff:fe00:XXXX, + // where XXXX are the bits carried in-line. + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + // Link-local prefix + bytes[0] = 0xfe; + bytes[1] = 0x80; + + bytes[11] = 0xff; + bytes[12] = 0xfe; + + bytes[14..].copy_from_slice(&data[start..start + 2]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (0, 0, 0b11) => { + // The address is fully elided. + // The first 64 bits of the address are the link-local prefix padded with zeros. + // The remaining 64 bits are computed from the encapsulating header. + Ok(Address::Elided) + } + (0, 1, 0b00) => Ok(Address::Reserved), + (0, 1, 0b01) => { + // The address is derived using context information and the 64 bits carried in-line. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + let data = self.buffer.as_ref(); + let bytes = &data[start..start + 8]; + + Ok(Address::WithContext(bytes)) + } + (0, 1, 0b10) => { + // The address is derived using context information and the 16 bits carried in-line. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + let data = self.buffer.as_ref(); + let bytes = &data[start..start + 2]; + Ok(Address::WithContext(bytes)) + } + (0, 1, 0b11) => { + // The address is fully elided and is derived using context information and the encapsulating header. + // Bits covered by context information are always used. + // Any IID bits not covered by context information are always used. + // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. + // Any remaining bits are zero. + Ok(Address::WithContext(&[])) + } + (1, 0, 0b00) => { + // The full address is carried in-line. + let data = self.buffer.as_ref(); + Ok(Address::Complete(ipv6::Address::from_bytes( + &data[start..start + 16], + ))) + } + (1, 0, 0b01) => { + // The address takes the form ffXX::00XX:XXXX:XXXX + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + bytes[0] = 0xff; + bytes[1] = data[start]; + + bytes[11..].copy_from_slice(&data[start + 1..start + 6]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (1, 0, 0b10) => { + // The address takes the form ffXX::00XX:XXXX + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + bytes[0] = 0xff; + bytes[1] = data[start]; + + bytes[13..].copy_from_slice(&data[start + 1..start + 4]); + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (1, 0, 0b11) => { + // The address takes the form ff02::00XX + let data = self.buffer.as_ref(); + let mut bytes = [0u8; 16]; + + bytes[0] = 0xff; + bytes[1] = 0x02; + + bytes[15] = data[start]; + + Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + } + (1, 1, 0b00) => { + // This format is designed to match Unicast-Prefix-based IPv6 Multicast Addresses. + // The multicast takes the form ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX. + // X are octets that are carried in-line, in the order in which they appear. + // P are octets used to encode the prefix itself. + // L are octets used to encode the prefix length. + // The prefix information P and L is taken from the specified context. + Err(Error::NotSupported) + } + (1, 1, 0b01 | 0b10 | 0b11) => Ok(Address::Reserved), + _ => Err(Error::Malformed), + } + } + + get_field!(dispatch_field, 0b111, 13); + get_field!(tf_field, 0b11, 11); + get_field!(nh_field, 0b1, 10); + get_field!(hlim_field, 0b11, 8); + get_field!(cid_field, 0b1, 7); + get_field!(sac_field, 0b1, 6); + get_field!(sam_field, 0b11, 4); + get_field!(m_field, 0b1, 3); + get_field!(dac_field, 0b1, 2); + get_field!(dam_field, 0b11, 0); + + /// Return the start for the IP fields. + fn ip_fields_start(&self) -> u8 { + 2 + self.cid_size() + } + + /// Get the size in octets of the traffic class field. + fn traffic_class_size(&self) -> u8 { + match self.tf_field() { + 0b00 => 4, + 0b01 => 3, + 0b10 => 1, + 0b11 => 0, + _ => unreachable!(), + } + } + + /// Get the size in octets of the next header field. + fn next_header_size(&self) -> u8 { + (self.nh_field() != 1) as u8 + } + + /// Get the size in octets of the hop limit field. + fn hop_limit_size(&self) -> u8 { + (self.hlim_field() == 0b00) as u8 + } + + /// Get the size in octets of the CID field. + fn cid_size(&self) -> u8 { + (self.cid_field() == 1) as u8 + } + + /// Get the size in octets of the source address. + fn src_address_size(&self) -> u8 { + match (self.sac_field(), self.sam_field()) { + (0, 0b00) => 16, // The full address is carried in-line. + (0, 0b01) => 8, // The first 64 bits are elided. + (0, 0b10) => 2, // The first 112 bits are elided. + (0, 0b11) => 0, // The address is fully elided. + (1, 0b00) => 0, // The UNSPECIFIED address. + (1, 0b01) => 8, // Address derived using context information. + (1, 0b10) => 2, // Address derived using context information. + (1, 0b11) => 0, // Address derived using context information. + _ => unreachable!(), + } + } + + /// Get the size in octets of the address address. + fn dst_address_size(&self) -> u8 { + match (self.m_field(), self.dac_field(), self.dam_field()) { + (0, 0, 0b00) => 16, // The full address is carried in-line. + (0, 0, 0b01) => 8, // The first 64 bits are elided. + (0, 0, 0b10) => 2, // The first 112 bits are elided. + (0, 0, 0b11) => 0, // The address is fully elided. + (0, 1, 0b00) => 0, // Reserved. + (0, 1, 0b01) => 8, // Address derived using context information. + (0, 1, 0b10) => 2, // Address derived using context information. + (0, 1, 0b11) => 0, // Address derived using context information. + (1, 0, 0b00) => 16, // The full address is carried in-line. + (1, 0, 0b01) => 6, // The address takes the form ffXX::00XX:XXXX:XXXX. + (1, 0, 0b10) => 4, // The address takes the form ffXX::00XX:XXXX. + (1, 0, 0b11) => 1, // The address takes the form ff02::00XX. + (1, 1, 0b00) => 6, // Match Unicast-Prefix-based IPv6. + (1, 1, 0b01) => 0, // Reserved. + (1, 1, 0b10) => 0, // Reserved. + (1, 1, 0b11) => 0, // Reserved. + _ => unreachable!(), + } + } + } + + impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { + /// Return a pointer to the payload. + pub fn payload(&self) -> &'a [u8] { + let mut len = self.ip_fields_start(); + len += self.traffic_class_size(); + len += self.next_header_size(); + len += self.hop_limit_size(); + len += self.src_address_size(); + len += self.dst_address_size(); + + let len = len as usize; + + let data = self.buffer.as_ref(); + &data[len..] + } + } + + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Packet { + /// Set the dispatch field to `0b011`. + fn set_dispatch_field(&mut self) { + let data = &mut self.buffer.as_mut()[field::IPHC_FIELD]; + let mut raw = NetworkEndian::read_u16(data); + + raw = (raw & !(0b111 << 13)) | (0b11 << 13); + NetworkEndian::write_u16(data, raw); + } + + set_field!(set_tf_field, 0b11, 11); + set_field!(set_nh_field, 0b1, 10); + set_field!(set_hlim_field, 0b11, 8); + //set_field!(set_cid_field, 0b1, 7); + set_field!(set_sac_field, 0b1, 6); + set_field!(set_sam_field, 0b11, 4); + set_field!(set_m_field, 0b1, 3); + //set_field!(set_dac_field, 0b1, 2); + set_field!(set_dam_field, 0b11, 0); + + fn set_field(&mut self, idx: usize, value: &[u8]) { + let raw = self.buffer.as_mut(); + raw[idx..idx + value.len()].copy_from_slice(value); + } + + /// Return a mutable pointer to the payload. + pub fn payload_mut(&mut self) -> &mut [u8] { + let mut len = self.ip_fields_start(); + + len += self.traffic_class_size(); + len += self.next_header_size(); + len += self.hop_limit_size(); + len += self.src_address_size(); + len += self.dst_address_size(); + + let len = len as usize; + + let data = self.buffer.as_mut(); + &mut data[len..] + } + + fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize { + match nh { + NextHeader::Uncompressed(nh) => { + self.set_nh_field(0); + self.set_field(idx, &[nh.into()]); + idx += 1; + } + NextHeader::Compressed => self.set_nh_field(1), + } + + idx + } + + fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize { + match hl { + 255 => self.set_hlim_field(0b11), + 64 => self.set_hlim_field(0b10), + 1 => self.set_hlim_field(0b01), + _ => { + self.set_hlim_field(0b00); + self.set_field(idx, &[hl]); + idx += 1; + } + } + + idx + } + + /// Set the source address based on the IPv6 address and the Link-Local address. + fn set_src_address( + &mut self, + src_addr: ipv6::Address, + ll_src_addr: Option, + mut idx: usize, + ) -> usize { + let src = src_addr.as_bytes(); + if src_addr == ipv6::Address::UNSPECIFIED { + self.set_sac_field(1); + self.set_sam_field(0b00); + } else if src_addr.is_link_local() { + // We have a link local address. + // The remainder of the address can be elided when the context contains + // a 802.15.4 short address or a 802.15.4 extended address which can be + // converted to a eui64 address. + let is_eui_64 = ll_src_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == src[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + let ll = [src[14], src[15]]; + + if ll_src_addr == Some(LlAddress::Short(ll)) { + // We have the context from the 802.15.4 frame. + // The context contains the short address. + // We can elide the source address. + self.set_sam_field(0b11); + } else { + // We don't have the context from the 802.15.4 frame. + // We cannot elide the source address, however we can elide 112 bits. + self.set_sam_field(0b10); + + self.set_field(idx, &src[14..]); + idx += 2; + } + } else if is_eui_64 { + // We have the context from the 802.15.4 frame. + // The context contains the extended address. + // We can elide the source address. + self.set_sam_field(0b11); + } else { + // We cannot elide the source address, however we can elide 64 bits. + self.set_sam_field(0b01); + + self.set_field(idx, &src[8..]); + idx += 8; + } + } else { + // We cannot elide anything. + self.set_field(idx, src); + idx += 16; + } + + idx + } + + fn set_dst_address( + &mut self, + dst_addr: ipv6::Address, + ll_dst_addr: Option, + mut idx: usize, + ) -> usize { + let dst = dst_addr.as_bytes(); + if dst_addr.is_multicast() { + self.set_m_field(1); + + if dst[1] == 0x02 && dst[2..15] == [0; 13] { + self.set_dam_field(0b11); + + self.set_field(idx, &[dst[15]]); + idx += 1; + } else if dst[2..13] == [0; 11] { + self.set_dam_field(0b10); + + self.set_field(idx, &[dst[1]]); + idx += 1; + self.set_field(idx, &dst[13..]); + idx += 3; + } else if dst[2..11] == [0; 9] { + self.set_dam_field(0b01); + + self.set_field(idx, &[dst[1]]); + idx += 1; + self.set_field(idx, &dst[11..]); + idx += 5; + } else { + self.set_dam_field(0b11); + + self.set_field(idx, dst); + idx += 16; + } + } else if dst_addr.is_link_local() { + let is_eui_64 = ll_dst_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == dst[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + let ll = [dst[14], dst[15]]; + + if ll_dst_addr == Some(LlAddress::Short(ll)) { + self.set_dam_field(0b11); + } else { + self.set_dam_field(0b10); + + self.set_field(idx, &dst[14..]); + idx += 2; + } + } else if is_eui_64 { + self.set_dam_field(0b11); + } else { + self.set_dam_field(0b01); + + self.set_field(idx, &dst[8..]); + idx += 8; + } + } else { + self.set_dam_field(0b00); + + self.set_field(idx, dst); + idx += 16; + } + + idx + } + } + + /// A high-level representation of a LOWPAN_IPHC header. + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct Repr { + pub src_addr: ipv6::Address, + pub ll_src_addr: Option, + pub dst_addr: ipv6::Address, + pub ll_dst_addr: Option, + pub next_header: NextHeader, + pub hop_limit: u8, + } + + impl Repr { + /// Parse a LOWPAN_IPHC packet and return a high-level representation. + pub fn parse + ?Sized>( + packet: &Packet<&T>, + ll_src_addr: Option, + ll_dst_addr: Option, + ) -> Result { + // Ensure basic accessors will work. + packet.check_len()?; + + if packet.dispatch_field() != DISPATCH { + // This is not an LOWPAN_IPHC packet. + return Err(Error::Malformed); + } + + let src_addr = packet.src_addr()?.resolve(ll_src_addr)?; + let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr)?; + + Ok(Repr { + src_addr, + ll_src_addr, + dst_addr, + ll_dst_addr, + next_header: packet.next_header(), + hop_limit: packet.hop_limit(), + }) + } + + /// Return the length of a header that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + let mut len = 0; + len += 2; // The minimal header length + + len += if self.next_header == NextHeader::Compressed { + 0 // The next header is compressed (we don't need to inline what the next header is) + } else { + 1 // The next header field is inlined + }; + + // Add the lenght of the source address + len += if self.src_addr == ipv6::Address::UNSPECIFIED { + 0 + } else if self.src_addr.is_link_local() { + let src = self.src_addr.as_bytes(); + let ll = [src[14], src[15]]; + + let is_eui_64 = self + .ll_src_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == src[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + if self.ll_src_addr == Some(LlAddress::Short(ll)) { + 0 + } else { + 2 + } + } else if is_eui_64 { + 0 + } else { + 8 + } + } else { + 16 + }; + + // Add the size of the destination header + let dst = self.dst_addr.as_bytes(); + len += if self.dst_addr.is_multicast() { + if dst[1] == 0x02 && dst[2..15] == [0; 13] { + 1 + } else if dst[2..13] == [0; 11] { + 4 + } else if dst[2..11] == [0; 9] { + 6 + } else { + 16 + } + } else if self.dst_addr.is_link_local() { + let is_eui_64 = self + .ll_dst_addr + .map(|addr| { + addr.as_eui_64() + .map(|addr| addr[..] == dst[8..]) + .unwrap_or(false) + }) + .unwrap_or(false); + + if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] { + let ll = [dst[14], dst[15]]; + + if self.ll_dst_addr == Some(LlAddress::Short(ll)) { + 0 + } else { + 2 + } + } else if is_eui_64 { + 0 + } else { + 8 + } + } else { + 16 + }; + + // Add the size of the traffic flow. + // TODO(thvdveld): implement traffic flow for sixlowpan + len += 0; + + len + } + + /// Emit a high-level representation into a LOWPAN_IPHC packet. + pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { + let idx = 2; + + packet.set_dispatch_field(); + + // SETTING THE TRAFIX FLOW + // TODO(thvdveld): needs more work. + packet.set_tf_field(0b11); + + let idx = packet.set_next_header(self.next_header, idx); + let idx = packet.set_hop_limit(self.hop_limit, idx); + let idx = packet.set_src_address(self.src_addr, self.ll_src_addr, idx); + packet.set_dst_address(self.dst_addr, self.ll_dst_addr, idx); + } + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn iphc_fields() { + let bytes = [ + 0x7a, 0x33, // IPHC + 0x3a, // Next header + ]; + + let packet = Packet::new_unchecked(bytes); + + assert_eq!(packet.dispatch_field(), 0b011); + assert_eq!(packet.tf_field(), 0b11); + assert_eq!(packet.nh_field(), 0b0); + assert_eq!(packet.hlim_field(), 0b10); + assert_eq!(packet.cid_field(), 0b0); + assert_eq!(packet.sac_field(), 0b0); + assert_eq!(packet.sam_field(), 0b11); + assert_eq!(packet.m_field(), 0b0); + assert_eq!(packet.dac_field(), 0b0); + assert_eq!(packet.dam_field(), 0b11); + + assert_eq!( + packet.next_header(), + NextHeader::Uncompressed(IpProtocol::Icmpv6) + ); + + assert_eq!(packet.src_address_size(), 0); + assert_eq!(packet.dst_address_size(), 0); + assert_eq!(packet.hop_limit(), 64); + + assert_eq!(packet.src_addr(), Ok(Address::Elided)); + assert_eq!(packet.dst_addr(), Ok(Address::Elided)); + + let bytes = [ + 0x7e, 0xf7, // IPHC, + 0x00, // CID + ]; + + let packet = Packet::new_unchecked(bytes); + + assert_eq!(packet.dispatch_field(), 0b011); + assert_eq!(packet.tf_field(), 0b11); + assert_eq!(packet.nh_field(), 0b1); + assert_eq!(packet.hlim_field(), 0b10); + assert_eq!(packet.cid_field(), 0b1); + assert_eq!(packet.sac_field(), 0b1); + assert_eq!(packet.sam_field(), 0b11); + assert_eq!(packet.m_field(), 0b0); + assert_eq!(packet.dac_field(), 0b1); + assert_eq!(packet.dam_field(), 0b11); + + assert_eq!(packet.next_header(), NextHeader::Compressed); + + assert_eq!(packet.src_address_size(), 0); + assert_eq!(packet.dst_address_size(), 0); + assert_eq!(packet.hop_limit(), 64); + + assert_eq!(packet.src_addr(), Ok(Address::WithContext(&[]))); + assert_eq!(packet.dst_addr(), Ok(Address::WithContext(&[]))); + } + } +} + +pub mod nhc { + use crate::wire::ip::checksum; + use crate::wire::ip::Address as IpAddress; + use crate::wire::ipv6; + use crate::wire::udp::Repr as UdpRepr; + use crate::wire::IpProtocol; + use crate::Error; + use crate::Result; + use byteorder::{ByteOrder, NetworkEndian}; + use ipv6::Address; + + use super::NextHeader; + + macro_rules! get_field { + ($name:ident, $mask:expr, $shift:expr) => { + fn $name(&self) -> u8 { + let data = self.buffer.as_ref(); + let raw = &data[0]; + ((raw >> $shift) & $mask) as u8 + } + }; + } + + macro_rules! set_field { + ($name:ident, $mask:expr, $shift:expr) => { + fn $name(&mut self, val: u8) { + let data = self.buffer.as_mut(); + let mut raw = data[0]; + raw = (raw & !($mask << $shift)) | (val << $shift); + data[0] = raw; + } + }; + } + + /// A read/write wrapper around a LOWPAN_NHC frame buffer. + #[derive(Debug, Clone)] + pub enum Packet> { + ExtensionHeader(ExtensionHeaderPacket), + UdpHeader(UdpPacket), + } + + impl> Packet { + pub fn dispatch(buffer: T) -> Result> { + let raw = buffer.as_ref(); + + #[cfg(feature = "std")] + println!("{:02x?}", raw[0]); + + if raw[0] >> 4 == 0b1110 { + // We have a compressed IPv6 Extension Header. + Ok(Packet::ExtensionHeader(ExtensionHeaderPacket::new_checked( + buffer, + )?)) + } else if raw[0] >> 3 == 0b11110 { + // We have a compressed UDP header. + Ok(Packet::UdpHeader(UdpPacket::new_checked(buffer)?)) + } else { + Err(Error::Unrecognized) + } + } + } + + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum ExtensionHeaderId { + HopByHopHeader, + RoutingHeader, + FragmentHeader, + DestinationOptionsHeader, + MobilityHeader, + Header, + Reserved, + } + + impl From for IpProtocol { + fn from(val: ExtensionHeaderId) -> Self { + match val { + ExtensionHeaderId::HopByHopHeader => IpProtocol::HopByHop, + ExtensionHeaderId::RoutingHeader => IpProtocol::Ipv6Route, + ExtensionHeaderId::FragmentHeader => IpProtocol::Ipv6Frag, + ExtensionHeaderId::DestinationOptionsHeader => IpProtocol::Ipv6Opts, + ExtensionHeaderId::MobilityHeader => IpProtocol::Unknown(0), + ExtensionHeaderId::Header => IpProtocol::Unknown(0), + ExtensionHeaderId::Reserved => IpProtocol::Unknown(0), + } + } + } + + pub(crate) const EXT_HEADER_DISPATCH: u8 = 0b1110; + + /// A read/write wrapper around a LOWPAN_NHC Next Header frame buffer. + #[derive(Debug, Clone)] + pub struct ExtensionHeaderPacket> { + buffer: T, + } + + impl> ExtensionHeaderPacket { + /// Input a raw octet buffer with a LOWPAN_NHC Extension Header frame structure. + pub fn new_unchecked(buffer: T) -> ExtensionHeaderPacket { + ExtensionHeaderPacket { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result> { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Truncated)` if the buffer is too short. + pub fn check_len(&self) -> Result<()> { + let buffer = self.buffer.as_ref(); + if buffer.is_empty() { + Err(Error::Truncated) + } else { + Ok(()) + } + } + + /// Consumes the frame, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + get_field!(dispatch_field, 0b1111, 4); + get_field!(eid_field, 0b111, 1); + get_field!(nh_field, 0b1, 0); + + /// Return the Extension Header ID. + pub fn extension_header_id(&self) -> ExtensionHeaderId { + match self.eid_field() { + 0 => ExtensionHeaderId::HopByHopHeader, + 1 => ExtensionHeaderId::RoutingHeader, + 2 => ExtensionHeaderId::FragmentHeader, + 3 => ExtensionHeaderId::DestinationOptionsHeader, + 4 => ExtensionHeaderId::MobilityHeader, + 5 | 6 => ExtensionHeaderId::Reserved, + 7 => ExtensionHeaderId::Header, + _ => unreachable!(), + } + } + + /// Return the length field. + pub fn length_field(&self) -> u8 { + let start = 1 + self.next_header_size(); + + let data = self.buffer.as_ref(); + data[start] + } + + /// Parse the next header field. + pub fn next_header(&self) -> NextHeader { + if self.nh_field() == 1 { + NextHeader::Compressed + } else { + // The full 8 bits for Next Header are carried in-line. + let start = 1; + + let data = self.buffer.as_ref(); + let nh = data[start]; + NextHeader::Uncompressed(IpProtocol::from(nh)) + } + } + + /// Return the size of the Next Header field. + fn next_header_size(&self) -> usize { + // If nh is set, then the Next Header is compressed using LOWPAN_NHC + if self.nh_field() == 1 { + 0 + } else { + 1 + } + } + } + + impl<'a, T: AsRef<[u8]> + ?Sized> ExtensionHeaderPacket<&'a T> { + /// Return a pointer to the payload. + pub fn payload(&self) -> &'a [u8] { + let start = 2 + self.next_header_size(); + &self.buffer.as_ref()[start..] + } + } + + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> ExtensionHeaderPacket { + /// Return a mutable pointer to the payload. + pub fn payload_mut(&mut self) -> &mut [u8] { + let start = 2 + self.next_header_size(); + &mut self.buffer.as_mut()[start..] + } + + /// Set the dispatch field to `0b1110`. + fn set_dispatch_field(&mut self) { + let data = self.buffer.as_mut(); + data[0] = (data[0] & !(0b1111 << 4)) | (EXT_HEADER_DISPATCH << 4); + } + + set_field!(set_eid_field, 0b111, 1); + set_field!(set_nh_field, 0b1, 0); + + /// Set the Extension Header ID field. + fn set_extension_header_id(&mut self, ext_header_id: ExtensionHeaderId) { + let id = match ext_header_id { + ExtensionHeaderId::HopByHopHeader => 0, + ExtensionHeaderId::RoutingHeader => 1, + ExtensionHeaderId::FragmentHeader => 2, + ExtensionHeaderId::DestinationOptionsHeader => 3, + ExtensionHeaderId::MobilityHeader => 4, + ExtensionHeaderId::Header => 7, + _ => unreachable!(), + }; + + self.set_eid_field(id); + } + + /// Set the Next Header. + fn set_next_header(&mut self, next_header: NextHeader) { + match next_header { + NextHeader::Compressed => self.set_nh_field(0b1), + NextHeader::Uncompressed(nh) => { + self.set_nh_field(0b0); + + let start = 1; + let data = self.buffer.as_mut(); + data[start] = nh.into(); + } + } + } + + /// Set the length. + fn set_length(&mut self, length: u8) { + let start = 1 + self.next_header_size(); + + let data = self.buffer.as_mut(); + data[start] = length; + } + } + + /// A high-level representation of an LOWPAN_NHC Extension Header header. + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct ExtensionHeaderRepr { + ext_header_id: ExtensionHeaderId, + next_header: NextHeader, + length: u8, + } + + impl ExtensionHeaderRepr { + /// Parse a LOWPAN_NHC Extension Header packet and return a high-level representation. + pub fn parse + ?Sized>( + packet: &ExtensionHeaderPacket<&T>, + ) -> Result { + // Ensure basic accessors will work. + packet.check_len()?; + + if packet.dispatch_field() != EXT_HEADER_DISPATCH { + return Err(Error::Malformed); + } + + Ok(ExtensionHeaderRepr { + ext_header_id: packet.extension_header_id(), + next_header: packet.next_header(), + length: packet.payload().len() as u8, + }) + } + + /// Return the length of a header that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + let mut len = 1; // The minimal header size + + if self.next_header != NextHeader::Compressed { + len += 1; + } + + len += 1; // The length + + len + } + + /// Emit a high-level representaiton into a LOWPAN_NHC Extension Header packet. + pub fn emit + AsMut<[u8]>>(&self, packet: &mut ExtensionHeaderPacket) { + packet.set_dispatch_field(); + packet.set_extension_header_id(self.ext_header_id); + packet.set_next_header(self.next_header); + packet.set_length(self.length); + } + } + + pub(crate) const UDP_DISPATCH: u8 = 0b11110; + + /// A read/write wrapper around a 6LoWPAN_NHC_UDP frame buffer. + #[derive(Debug, Clone)] + pub struct UdpPacket> { + buffer: T, + } + + impl> UdpPacket { + /// Input a raw octet buffer with a LOWPAN_NHC frame structure for UDP. + pub fn new_unchecked(buffer: T) -> UdpPacket { + UdpPacket { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result> { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Truncated)` if the buffer is too short. + pub fn check_len(&self) -> Result<()> { + let buffer = self.buffer.as_ref(); + if buffer.is_empty() { + Err(Error::Truncated) + } else { + Ok(()) + } + } + + /// Consumes the frame, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + get_field!(dispatch_field, 0b11111, 3); + get_field!(checksum_field, 0b1, 2); + get_field!(ports_field, 0b11, 0); + + /// Returns the index of the start of the next header compressed fields. + fn nhc_fields_start(&self) -> usize { + 1 + } + + /// Return the source port number. + pub fn src_port(&self) -> u16 { + match self.ports_field() { + 0b00 | 0b01 => { + // The full 16 bits are carried in-line. + let data = self.buffer.as_ref(); + let start = self.nhc_fields_start(); + + NetworkEndian::read_u16(&data[start..start + 2]) + } + 0b10 => { + // The first 8 bits are elided. + let data = self.buffer.as_ref(); + let start = self.nhc_fields_start(); + + 0xf000 + data[start] as u16 + } + 0b11 => { + // The first 12 bits are elided. + let data = self.buffer.as_ref(); + let start = self.nhc_fields_start(); + + 0xf0b0 + (data[start] >> 4) as u16 + } + _ => unreachable!(), + } + } + + /// Return the destination port number. + pub fn dst_port(&self) -> u16 { + match self.ports_field() { + 0b00 => { + // The full 16 bits are carried in-line. + let data = self.buffer.as_ref(); + let idx = self.nhc_fields_start(); + + NetworkEndian::read_u16(&data[idx + 2..idx + 4]) + } + 0b01 => { + // The first 8 bits are elided. + let data = self.buffer.as_ref(); + let idx = self.nhc_fields_start(); + + 0xf000 + data[idx] as u16 + } + 0b10 => { + // The full 16 bits are carried in-line. + let data = self.buffer.as_ref(); + let idx = self.nhc_fields_start(); + + NetworkEndian::read_u16(&data[idx + 1..idx + 1 + 2]) + } + 0b11 => { + // The first 12 bits are elided. + let data = self.buffer.as_ref(); + let start = self.nhc_fields_start(); + + 0xf0b0 + (NetworkEndian::read_u16(&data[start..start + 1]) & 0xff) + } + _ => unreachable!(), + } + } + + /// Return the checksum. + pub fn checksum(&self) -> Option { + if self.checksum_field() == 0b0 { + // The first 12 bits are elided. + let data = self.buffer.as_ref(); + let start = self.nhc_fields_start() + self.ports_size(); + Some(NetworkEndian::read_u16(&data[start..start + 2])) + } else { + // The checksum is ellided and needs to be recomputed on the 6LoWPAN termination point. + None + } + } + + // Return the size of the checksum field. + fn checksum_size(&self) -> usize { + match self.checksum_field() { + 0b0 => 2, + 0b1 => 0, + _ => unreachable!(), + } + } + + /// Returns the total size of both port numbers. + fn ports_size(&self) -> usize { + match self.ports_field() { + 0b00 => 4, // 16 bits + 16 bits + 0b01 => 3, // 16 bits + 8 bits + 0b10 => 3, // 8 bits + 16 bits + 0b11 => 1, // 4 bits + 4 bits + _ => unreachable!(), + } + } + } + + impl<'a, T: AsRef<[u8]> + ?Sized> UdpPacket<&'a T> { + /// Return a pointer to the payload. + pub fn payload(&self) -> &'a [u8] { + let start = 1 + self.ports_size() + self.checksum_size(); + &self.buffer.as_ref()[start..] + } + } + + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpPacket { + /// Return a mutable pointer to the payload. + pub fn payload_mut(&mut self) -> &mut [u8] { + let start = 1 + self.ports_size() + self.checksum_size(); + &mut self.buffer.as_mut()[start..] + } + + /// Set the dispatch field to `0b11110`. + fn set_dispatch_field(&mut self) { + let data = self.buffer.as_mut(); + data[0] = (data[0] & !(0b11111 << 3)) | (UDP_DISPATCH << 3); + } + + set_field!(set_checksum_field, 0b1, 2); + set_field!(set_ports_field, 0b11, 0); + + fn set_ports(&mut self, src_port: u16, dst_port: u16) { + let mut idx = 1; + + match (src_port, dst_port) { + (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => { + // We can compress both the source and destination ports. + self.set_ports_field(0b11); + let data = self.buffer.as_mut(); + data[idx] = (((src_port - 0xf0b0) as u8) << 4) & ((dst_port - 0xf0b0) as u8); + } + (0xf000..=0xf0ff, _) => { + // We can compress the source port, but not the destination port. + self.set_ports_field(0b10); + let data = self.buffer.as_mut(); + data[idx] = (src_port - 0xf000) as u8; + idx += 1; + + NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port); + } + (_, 0xf000..=0xf0ff) => { + // We can compress the destination port, but not the source port. + self.set_ports_field(0b01); + let data = self.buffer.as_mut(); + NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port); + idx += 2; + data[idx] = (dst_port - 0xf000) as u8; + } + (_, _) => { + // We cannot compress any port. + self.set_ports_field(0b00); + let data = self.buffer.as_mut(); + NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port); + idx += 2; + NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port); + } + }; + } + + fn set_checksum(&mut self, checksum: u16) { + self.set_checksum_field(0b0); + let idx = 1 + self.ports_size(); + let data = self.buffer.as_mut(); + NetworkEndian::write_u16(&mut data[idx..idx + 2], checksum); + } + } + + /// A high-level representation of a LOWPAN_NHC UDP header. + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct UdpNhcRepr(pub UdpRepr); + + impl<'a> UdpNhcRepr { + /// Parse a LOWWPAN_NHC UDP packet and return a high-level representation. + pub fn parse + ?Sized>( + packet: &UdpPacket<&'a T>, + src_addr: &ipv6::Address, + dst_addr: &ipv6::Address, + _checksum: Option, + ) -> Result { + // Ensure basic accessors will work. + packet.check_len()?; + + if packet.dispatch_field() != UDP_DISPATCH { + return Err(Error::Malformed); + } + + let payload_len = packet.payload().len(); + let chk_sum = !checksum::combine(&[ + checksum::pseudo_header( + &IpAddress::Ipv6(*src_addr), + &IpAddress::Ipv6(*dst_addr), + crate::wire::ip::Protocol::Udp, + payload_len as u32 + 8, + ), + packet.src_port(), + packet.dst_port(), + payload_len as u16 + 8, + checksum::data(packet.payload()), + ]); + + // TODO(thvdveld): remove the unwrap + if chk_sum != packet.checksum().unwrap() { + return Err(Error::Checksum); + } + + Ok(UdpNhcRepr(UdpRepr { + src_port: packet.src_port(), + dst_port: packet.dst_port(), + })) + } + + /// Return the length of a packet that will be emitted from this high-level representation. + pub fn header_len(&self) -> usize { + let mut len = 1; // The minimal header size + + len += 2; // XXX We assume we will add the checksum at the end + + // Check if we can compress the source and destination ports + match (self.src_port, self.dst_port) { + (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => len + 1, + (0xf000..=0xf0ff, _) | (_, 0xf000..=0xf0ff) => len + 3, + (_, _) => len + 4, + } + } + + /// Emit a high-level representation into a LOWPAN_NHC UDP header. + pub fn emit + AsMut<[u8]>>( + &self, + packet: &mut UdpPacket, + src_addr: &Address, + dst_addr: &Address, + payload_len: usize, + emit_payload: impl FnOnce(&mut [u8]), + ) { + packet.set_dispatch_field(); + packet.set_ports(self.src_port, self.dst_port); + emit_payload(packet.payload_mut()); + + let chk_sum = !checksum::combine(&[ + checksum::pseudo_header( + &IpAddress::Ipv6(*src_addr), + &IpAddress::Ipv6(*dst_addr), + crate::wire::ip::Protocol::Udp, + payload_len as u32 + 8, + ), + self.src_port, + self.dst_port, + payload_len as u16 + 8, + checksum::data(packet.payload_mut()), + ]); + + packet.set_checksum(chk_sum); + } + } + + impl core::ops::Deref for UdpNhcRepr { + type Target = UdpRepr; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl core::ops::DerefMut for UdpNhcRepr { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn ext_header_nhc_fields() { + let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]; + + let packet = ExtensionHeaderPacket::new_checked(&bytes[..]).unwrap(); + assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH); + assert_eq!(packet.length_field(), 6); + assert_eq!( + packet.extension_header_id(), + ExtensionHeaderId::RoutingHeader + ); + + assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); + } + + #[test] + fn ext_header_emit() { + let ext_header = ExtensionHeaderRepr { + ext_header_id: ExtensionHeaderId::RoutingHeader, + next_header: NextHeader::Compressed, + length: 6, + }; + + let len = ext_header.buffer_len(); + let mut buffer = [0u8; 127]; + let mut packet = ExtensionHeaderPacket::new_unchecked(&mut buffer[..len]); + ext_header.emit(&mut packet); + + assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH); + assert_eq!(packet.next_header(), NextHeader::Compressed); + assert_eq!(packet.length_field(), 6); + assert_eq!( + packet.extension_header_id(), + ExtensionHeaderId::RoutingHeader + ); + } + + #[test] + fn udp_nhc_fields() { + let bytes = [0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4]; + + let packet = UdpPacket::new_checked(&bytes[..]).unwrap(); + assert_eq!(packet.dispatch_field(), UDP_DISPATCH); + assert_eq!(packet.checksum(), Some(0x28c4)); + assert_eq!(packet.src_port(), 5678); + assert_eq!(packet.dst_port(), 8765); + } + + #[test] + fn udp_emit() { + let udp = UdpNhcRepr(UdpRepr { + src_port: 0xf0b1, + dst_port: 0xf001, + }); + + let payload = b"Hello World!"; + + let src_addr = ipv6::Address::default(); + let dst_addr = ipv6::Address::default(); + + let len = udp.header_len() + payload.len(); + let mut buffer = [0u8; 127]; + let mut packet = UdpPacket::new_unchecked(&mut buffer[..len]); + udp.emit(&mut packet, &src_addr, &dst_addr, payload.len(), |buf| { + buf.copy_from_slice(&payload[..]) + }); + + assert_eq!(packet.dispatch_field(), UDP_DISPATCH); + assert_eq!(packet.src_port(), 0xf0b1); + assert_eq!(packet.dst_port(), 0xf001); + assert_eq!(packet.payload_mut(), b"Hello World!"); + } + } +} + +#[cfg(test)] +mod test { + //use super::*; + + //#[test] + //fn ieee802154_udp() { + //use crate::wire::ieee802154::Frame as Ieee802154Frame; + //use crate::wire::ieee802154::Repr as Ieee802154Repr; + //use crate::wire::ipv6routing; + + //// This data is captured using Wireshark from the communication between a RPL 6LoWPAN server + //// and a RPL 6LoWPAN client. + //// The frame is thus an IEEE802.15.4 frame, containing a 6LoWPAN packet, + //// containing a RPL extension header and an UDP header. + //let bytes: &[u8] = &[ + //0x61, 0xdc, 0xdd, 0xcd, 0xab, 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, 0xbf, + //0x9b, 0x15, 0x06, 0x00, 0x4b, 0x12, 0x00, 0x7e, 0xf7, 0x00, 0xe3, 0x06, 0x03, 0x00, + //0xff, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4, 0x68, 0x65, 0x6c, + //0x6c, 0x6f, 0x20, 0x36, 0x35, 0x18, 0xb9, + //]; + + //let ieee802154_frame = Ieee802154Frame::new_checked(bytes).unwrap(); + //let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap(); + + //let iphc_frame = iphc::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap(); + //let iphc_repr = iphc::Repr::parse( + //&iphc_frame, + //ieee802154_repr.src_addr, + //ieee802154_repr.dst_addr, + //) + //.unwrap(); + + //// The next header is compressed. + //assert_eq!(iphc_repr.next_header, NextHeader::Compressed); + + //// We dispatch the NHC packet. + //let nhc_packet = nhc::Packet::dispatch(iphc_frame.payload()).unwrap(); + + //let udp_payload = match nhc_packet { + //nhc::Packet::ExtensionHeader(ext_packet) => { + //// The next header is compressed (it is the UDP NHC compressed header). + //assert_eq!(ext_packet.next_header(), NextHeader::Compressed); + //assert_eq!(ext_packet.length_field(), 6); + //let payload = ext_packet.payload(); + + //let length = ext_packet.length_field() as usize; + //let ext_packet_payload = &payload[..length]; + + //match ext_packet.extension_header_id() { + //nhc::ExtensionHeaderId::RoutingHeader => { + //// We are not intersted in the Next Header protocol. + //let proto = ipv6::Protocol::Unknown(0); + //let mut new_payload = [0; 8]; + + //new_payload[0] = proto.into(); + //new_payload[1] = (2 + length - 8) as u8; + //new_payload[2..].copy_from_slice(ext_packet_payload); + + //let routing = ipv6routing::Header::new_checked(new_payload).unwrap(); + + //assert_eq!(routing.routing_type(), ipv6routing::Type::Rpl); + //assert_eq!(routing.segments_left(), 0); + //assert_eq!(routing.cmpr_e(), 0xf); + //assert_eq!(routing.cmpr_i(), 0xf); + //} + //_ => unreachable!(), + //} + + //&payload[length..] + //} + //_ => unreachable!(), + //}; + + //let udp_nhc_frame = nhc::UdpPacket::new_checked(udp_payload).unwrap(); + //let udp_repr = nhc::UdpNhcRepr::parse( + //&udp_nhc_frame, + //&iphc_repr.src_addr, + //&iphc_repr.dst_addr, + //None, + //) + //.unwrap(); + + //assert_eq!(udp_repr.src_port, 5678); + //assert_eq!(udp_repr.dst_port, 8765); + //assert_eq!(udp_nhc_frame.checksum(), Some(0x28c4)); + //} +} From 68e25a29c30c114cbae191ea9a3c3c66d61aee79 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 06:56:32 +0200 Subject: [PATCH 227/566] Add RawHardwareAddress, use it in wire ndisc. This avoids wire needing to know what medium we're on. --- examples/ping.rs | 22 +---- src/iface/interface.rs | 89 +++++++++-------- src/socket/icmp.rs | 17 +--- src/wire/arp.rs | 20 ++-- src/wire/icmpv6.rs | 21 ++-- src/wire/ipv6routing.rs | 35 +++---- src/wire/mld.rs | 4 - src/wire/mod.rs | 93 +++++++++++++++++ src/wire/ndisc.rs | 97 ++++++------------ src/wire/ndiscoption.rs | 214 +++++++++++++++------------------------- 10 files changed, 283 insertions(+), 329 deletions(-) diff --git a/examples/ping.rs b/examples/ping.rs index 45bd63de1..530b72f75 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -22,7 +22,7 @@ use smoltcp::{ }; macro_rules! send_icmp_ping { - (v4, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, + ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{ let icmp_repr = $repr_type::EchoRequest { ident: $ident, @@ -35,22 +35,6 @@ macro_rules! send_icmp_ping { let icmp_packet = $packet_type::new_unchecked(icmp_payload); (icmp_repr, icmp_packet) }}; - - (v6, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, - $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{ - let icmp_repr = $repr_type::EchoRequest { - ident: $ident, - seq_no: $seq_no, - data: &$echo_payload, - }; - - let icmp_payload = $socket - .send(icmp_repr.buffer_len(&Medium::Ethernet), $remote_addr) - .unwrap(); - - let icmp_packet = $packet_type::new_unchecked(icmp_payload); - (icmp_repr, icmp_packet) - }}; } macro_rules! get_icmp_pong { @@ -186,7 +170,6 @@ fn main() { match remote_addr { IpAddress::Ipv4(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - v4, Icmpv4Repr, Icmpv4Packet, ident, @@ -199,7 +182,6 @@ fn main() { } IpAddress::Ipv6(_) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - v6, Icmpv6Repr, Icmpv6Packet, ident, @@ -213,7 +195,6 @@ fn main() { &remote_addr, &mut icmp_packet, &device_caps.checksum, - &device_caps.medium, ); } _ => unimplemented!(), @@ -249,7 +230,6 @@ fn main() { &src_ipv6, &icmp_packet, &device_caps.checksum, - &device_caps.medium, ) .unwrap(); get_icmp_pong!( diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 12427e3b5..1435edcf8 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -389,7 +389,6 @@ impl<'a> IpPacket<'a> { &_ip_repr.dst_addr(), &mut Icmpv6Packet::new_unchecked(payload), &caps.checksum, - &caps.medium, ), #[cfg(feature = "socket-raw")] IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), @@ -1198,7 +1197,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &payload[0..payload_len], }; - Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) + Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } IpRepr::Unspecified { .. } => Err(Error::Unaddressable), IpRepr::Sixlowpan(_) => Err(Error::Malformed), // XXX(thvdveld): this is just wrong; @@ -1394,7 +1393,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) + Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) } } } @@ -1604,7 +1603,6 @@ impl<'a> InterfaceInner<'a> { &ip_repr.dst_addr(), &icmp_packet, &cx.caps.checksum, - &cx.caps.medium, )?; #[cfg(feature = "socket-icmp")] @@ -1639,7 +1637,7 @@ impl<'a> InterfaceInner<'a> { seq_no, data, }; - Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) + Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) } #[cfg(feature = "medium-ieee802154")] IpRepr::Sixlowpan(sixlowpan_repr) => { @@ -1649,7 +1647,6 @@ impl<'a> InterfaceInner<'a> { data, }; Ok(self.icmpv6_reply( - cx, Ipv6Repr { src_addr: sixlowpan_repr.src_addr, dst_addr: sixlowpan_repr.dst_addr, @@ -1714,23 +1711,24 @@ impl<'a> InterfaceInner<'a> { flags, } => { let ip_addr = ip_repr.src_addr.into(); - match lladdr { - Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - if flags.contains(NdiscNeighborFlags::OVERRIDE) - || !self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&ip_addr, cx.now) - .found() - { - self.neighbor_cache - .as_mut() - .unwrap() - .fill(ip_addr, lladdr, cx.now) - } + if let Some(lladdr) = lladdr { + let lladdr = lladdr.parse(cx.caps.medium)?; + if !lladdr.is_unicast() || !target_addr.is_unicast() { + return Err(Error::Malformed); + } + if flags.contains(NdiscNeighborFlags::OVERRIDE) + || !self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&ip_addr, cx.now) + .found() + { + self.neighbor_cache + .as_mut() + .unwrap() + .fill(ip_addr, lladdr, cx.now) } - _ => (), } Ok(None) } @@ -1739,27 +1737,31 @@ impl<'a> InterfaceInner<'a> { lladdr, .. } => { - match lladdr { - Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => self - .neighbor_cache - .as_mut() - .unwrap() - .fill(ip_repr.src_addr.into(), lladdr, cx.now), - _ => (), + if let Some(lladdr) = lladdr { + let lladdr = lladdr.parse(cx.caps.medium)?; + if !lladdr.is_unicast() || !target_addr.is_unicast() { + return Err(Error::Malformed); + } + self.neighbor_cache.as_mut().unwrap().fill( + ip_repr.src_addr.into(), + lladdr, + cx.now, + ); } + if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - lladdr: Some(self.hardware_addr.unwrap()), + lladdr: Some(self.hardware_addr.unwrap().into()), }); let ip_repr = Ipv6Repr { src_addr: target_addr, dst_addr: ip_repr.src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: advert.buffer_len(&cx.caps.medium), + payload_len: advert.buffer_len(), }; Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) } else { @@ -1917,7 +1919,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn icmpv6_reply<'frame, 'icmp: 'frame>( &self, - cx: &Context, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>, ) -> Option> { @@ -1926,7 +1927,7 @@ impl<'a> InterfaceInner<'a> { src_addr: ipv6_repr.dst_addr, dst_addr: ipv6_repr.src_addr, next_header: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(&cx.caps.medium), + payload_len: icmp_repr.buffer_len(), hop_limit: 64, }; Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) @@ -1989,7 +1990,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) + Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } #[cfg(feature = "proto-sixlowpan")] IpRepr::Sixlowpan(sixlowpan_repr) => { @@ -2011,7 +2012,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) + Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } @@ -2254,7 +2255,7 @@ impl<'a> InterfaceInner<'a> { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, - lladdr: self.hardware_addr, + lladdr: Some(self.hardware_addr.unwrap().into()), }); let packet = IpPacket::Icmpv6(( @@ -2262,7 +2263,7 @@ impl<'a> InterfaceInner<'a> { src_addr, dst_addr: dst_addr.solicited_node(), next_header: IpProtocol::Icmpv6, - payload_len: solicit.buffer_len(&cx.caps.medium), + payload_len: solicit.buffer_len(), hop_limit: 0xff, }, solicit, @@ -2422,7 +2423,7 @@ impl<'a> InterfaceInner<'a> { tx_len += udp_repr.header_len() + payload.len(); } IpPacket::Icmpv6((_, icmp)) => { - tx_len += icmp.buffer_len(&cx.caps.medium); + tx_len += icmp.buffer_len(); } _ => return Err(Error::Unrecognized), } @@ -2466,7 +2467,6 @@ impl<'a> InterfaceInner<'a> { &iphc_repr.dst_addr.into(), &mut icmp_packet, &cx.caps.checksum, - &cx.caps.medium, ); } _ => return Err(Error::Unrecognized), @@ -3135,7 +3135,7 @@ mod test { dst_addr: src_addr, next_header: IpProtocol::Icmpv6, hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(&Medium::Ethernet), + payload_len: expected_icmp_repr.buffer_len(), }; #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let expected_icmp_repr = Icmpv4Repr::DstUnreachable { @@ -3157,7 +3157,7 @@ mod test { // The expected packet does not exceed the IPV4_MIN_MTU #[cfg(feature = "proto-ipv6")] assert_eq!( - expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(&Medium::Ethernet), + expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), MIN_MTU ); // The expected packet does not exceed the IPV4_MIN_MTU @@ -3267,7 +3267,7 @@ mod test { dst_addr: local_ip_addr.solicited_node(), next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: solicit.buffer_len(&Medium::Ethernet), + payload_len: solicit.buffer_len(), }); let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); @@ -3281,7 +3281,6 @@ mod test { &local_ip_addr.solicited_node().into(), &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), &ChecksumCapabilities::default(), - &iface.device().capabilities().medium, ); } @@ -3296,7 +3295,7 @@ mod test { dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, hop_limit: 0xff, - payload_len: icmpv6_expected.buffer_len(&Medium::Ethernet), + payload_len: icmpv6_expected.buffer_len(), }; let cx = iface.context(Instant::from_secs(0)); @@ -3530,7 +3529,7 @@ mod test { src_addr: Ipv6Address::LOOPBACK, dst_addr: remote_ip_addr, next_header: IpProtocol::Icmpv6, - payload_len: reply_icmp_repr.buffer_len(&Medium::Ethernet), + payload_len: reply_icmp_repr.buffer_len(), hop_limit: 0x40, }; diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index b2b231d60..acdee1204 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -427,19 +427,18 @@ impl<'a> IcmpSocket<'a> { IcmpRepr::Ipv6(ref icmp_repr) => { let packet_buf = self .rx_buffer - .enqueue(icmp_repr.buffer_len(&_cx.caps.medium), ip_repr.src_addr())?; + .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; icmp_repr.emit( &ip_repr.src_addr(), &ip_repr.dst_addr(), &mut Icmpv6Packet::new_unchecked(packet_buf), &ChecksumCapabilities::default(), - &_cx.caps.medium, ); net_trace!( "{}:{}: receiving {} octets", self.meta.handle, - icmp_repr.buffer_len(&_cx.caps.medium), + icmp_repr.buffer_len(), packet_buf.len() ); } @@ -487,13 +486,12 @@ impl<'a> IcmpSocket<'a> { &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored(), - &_cx.caps.medium, )?; let ip_repr = IpRepr::Ipv6(Ipv6Repr { src_addr: src_addr, dst_addr: ipv6_addr, next_header: IpProtocol::Icmpv6, - payload_len: repr.buffer_len(&_cx.caps.medium), + payload_len: repr.buffer_len(), hop_limit: hop_limit, }); emit((ip_repr, IcmpRepr::Ipv6(repr))) @@ -868,7 +866,6 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, - &crate::phy::Medium::Ethernet, ); assert_eq!( @@ -916,7 +913,6 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, - &crate::phy::Medium::Ethernet, ); s.set_hop_limit(Some(0x2a)); @@ -933,7 +929,7 @@ mod test_ipv6 { src_addr: Ipv6Address::UNSPECIFIED, dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, - payload_len: ECHOV6_REPR.buffer_len(&crate::phy::Medium::Ethernet), + payload_len: ECHOV6_REPR.buffer_len(), hop_limit: 0x2a, }) ); @@ -960,7 +956,6 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, - &crate::phy::Medium::Ethernet, ); let data = &packet.into_inner()[..]; @@ -999,7 +994,6 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, - &crate::phy::Medium::Ethernet, ); // Ensure that a packet with an identifier that isn't the bound @@ -1042,7 +1036,7 @@ mod test_ipv6 { src_addr: REMOTE_IPV6.into(), dst_addr: LOCAL_IPV6.into(), protocol: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(&crate::phy::Medium::Ethernet), + payload_len: icmp_repr.buffer_len(), hop_limit: 0x40, }; @@ -1064,7 +1058,6 @@ mod test_ipv6 { &REMOTE_IPV6.into(), &mut packet, &checksum, - &crate::phy::Medium::Ethernet, ); assert_eq!( socket.recv(), diff --git a/src/wire/arp.rs b/src/wire/arp.rs index a56c17dcd..48d64ca69 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -357,15 +357,17 @@ impl fmt::Display for Repr { source_protocol_addr, target_hardware_addr, target_protocol_addr, - } => write!( - f, - "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", - source_hardware_addr, - source_protocol_addr, - target_hardware_addr, - target_protocol_addr, - operation - ), + } => { + write!( + f, + "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", + source_hardware_addr, + source_protocol_addr, + target_hardware_addr, + target_protocol_addr, + operation + ) + } } } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 0873ea37b..4afa37db9 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -2,7 +2,6 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::{cmp, fmt}; use crate::phy::ChecksumCapabilities; -use crate::phy::Medium; use crate::wire::ip::checksum; use crate::wire::MldRepr; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -546,7 +545,6 @@ impl<'a> Repr<'a> { dst_addr: &IpAddress, packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities, - medium: &Medium, ) -> Result> where T: AsRef<[u8]> + ?Sized, @@ -620,16 +618,14 @@ impl<'a> Repr<'a> { data: packet.payload(), }), #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - (msg_type, 0) if msg_type.is_ndisc() => { - NdiscRepr::parse(packet, medium).map(Repr::Ndisc) - } + (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), _ => Err(Error::Unrecognized), } } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self, medium: &Medium) -> usize { + pub fn buffer_len(&self) -> usize { match self { &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } @@ -641,7 +637,7 @@ impl<'a> Repr<'a> { field::ECHO_SEQNO.end + data.len() } #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - &Repr::Ndisc(ndisc) => ndisc.buffer_len(medium), + &Repr::Ndisc(ndisc) => ndisc.buffer_len(), &Repr::Mld(mld) => mld.buffer_len(), } } @@ -654,7 +650,6 @@ impl<'a> Repr<'a> { dst_addr: &IpAddress, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities, - medium: &Medium, ) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { @@ -736,7 +731,7 @@ impl<'a> Repr<'a> { } #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - Repr::Ndisc(ndisc) => ndisc.emit(packet, medium), + Repr::Ndisc(ndisc) => ndisc.emit(packet), Repr::Mld(mld) => mld.emit(packet), } @@ -844,7 +839,6 @@ mod test { &MOCK_IP_ADDR_2, &packet, &ChecksumCapabilities::default(), - &Medium::Ethernet, ) .unwrap(); assert_eq!(repr, echo_packet_repr()); @@ -853,14 +847,13 @@ mod test { #[test] fn test_echo_emit() { let repr = echo_packet_repr(); - let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; + let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit( &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), - &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } @@ -899,7 +892,6 @@ mod test { &MOCK_IP_ADDR_2, &packet, &ChecksumCapabilities::default(), - &Medium::Ethernet, ) .unwrap(); assert_eq!(repr, too_big_packet_repr()); @@ -908,14 +900,13 @@ mod test { #[test] fn test_too_big_emit() { let repr = too_big_packet_repr(); - let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; + let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit( &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), - &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 84e55408a..fdb155ada 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -503,15 +503,17 @@ impl<'a> fmt::Display for Repr<'a> { length, segments_left, home_address, - } => write!( - f, - "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", - next_header, - length, - Type::Type2, - segments_left, - home_address - ), + } => { + write!( + f, + "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", + next_header, + length, + Type::Type2, + segments_left, + home_address + ) + } Repr::Rpl { next_header, length, @@ -520,17 +522,10 @@ impl<'a> fmt::Display for Repr<'a> { cmpr_e, pad, .. - } => write!( - f, - "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", - next_header, - length, - Type::Rpl, - segments_left, - cmpr_i, - cmpr_e, - pad - ), + } => { + write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", + next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) + } } } } diff --git a/src/wire/mld.rs b/src/wire/mld.rs index cf3b5f485..c77e1dae7 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -532,7 +532,6 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &packet, &ChecksumCapabilities::default(), - &crate::phy::Medium::Ethernet, ); assert_eq!(repr, Ok(create_repr(Message::MldQuery))); } @@ -545,7 +544,6 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &packet, &ChecksumCapabilities::default(), - &crate::phy::Medium::Ethernet, ); assert_eq!(repr, Ok(create_repr(Message::MldReport))); } @@ -560,7 +558,6 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &mut packet, &ChecksumCapabilities::default(), - &crate::phy::Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); } @@ -575,7 +572,6 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &mut packet, &ChecksumCapabilities::default(), - &crate::phy::Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 321a67fc8..c891a1bb5 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -123,6 +123,8 @@ mod sixlowpan; mod tcp; mod udp; +use crate::{phy::Medium, Error}; + pub use self::pretty_print::PrettyPrinter; #[cfg(feature = "medium-ethernet")] @@ -310,3 +312,94 @@ impl From for HardwareAddress { HardwareAddress::Ieee802154(addr) } } + +#[cfg(not(feature = "medium-ieee802154"))] +pub const MAX_HARDWARE_ADDRESS_LEN: usize = 6; +#[cfg(feature = "medium-ieee802154")] +pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8; + +/// Unparsed hardware address. +/// +/// Used to make NDISC parsing agnostic of the hardware medium in use. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct RawHardwareAddress { + len: u8, + data: [u8; MAX_HARDWARE_ADDRESS_LEN], +} + +impl RawHardwareAddress { + pub fn from_bytes(addr: &[u8]) -> Self { + let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN]; + data[..addr.len()].copy_from_slice(addr); + + Self { + len: addr.len() as u8, + data, + } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.data[..self.len as usize] + } + + pub fn len(&self) -> usize { + self.len as usize + } + + pub fn parse(&self, medium: Medium) -> Result { + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + if self.len() < 6 { + return Err(Error::Malformed); + } + Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes( + self.as_bytes(), + ))) + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + if self.len() < 8 { + return Err(Error::Malformed); + } + Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes( + self.as_bytes(), + ))) + } + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + } + } +} + +impl core::fmt::Display for RawHardwareAddress { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + for (i, &b) in self.as_bytes().iter().enumerate() { + if i != 0 { + write!(f, ":")?; + } + write!(f, "{:02x}", b)?; + } + Ok(()) + } +} + +#[cfg(feature = "medium-ethernet")] +impl From for RawHardwareAddress { + fn from(addr: EthernetAddress) -> Self { + Self::from_bytes(addr.as_bytes()) + } +} + +#[cfg(feature = "medium-ieee802154")] +impl From for RawHardwareAddress { + fn from(addr: Ieee802154Address) -> Self { + Self::from_bytes(addr.as_bytes()) + } +} + +impl From for RawHardwareAddress { + fn from(addr: HardwareAddress) -> Self { + Self::from_bytes(addr.as_bytes()) + } +} diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 1d9690948..32a216396 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -1,11 +1,10 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; -use crate::phy::Medium; use crate::time::Duration; use crate::wire::icmpv6::{field, Message, Packet}; -use crate::wire::HardwareAddress; use crate::wire::Ipv6Address; +use crate::wire::RawHardwareAddress; use crate::wire::{Ipv6Packet, Ipv6Repr}; use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; @@ -195,7 +194,7 @@ impl + AsMut<[u8]>> Packet { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { RouterSolicit { - lladdr: Option, + lladdr: Option, }, RouterAdvert { hop_limit: u8, @@ -203,23 +202,23 @@ pub enum Repr<'a> { router_lifetime: Duration, reachable_time: Duration, retrans_time: Duration, - lladdr: Option, + lladdr: Option, mtu: Option, prefix_info: Option, }, NeighborSolicit { target_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, }, NeighborAdvert { flags: NeighborFlags, target_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, }, Redirect { target_addr: Ipv6Address, dest_addr: Ipv6Address, - lladdr: Option, + lladdr: Option, redirected_hdr: Option>, }, } @@ -227,7 +226,7 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC packet and return a high-level representation of the /// packet. - pub fn parse(packet: &Packet<&'a T>, medium: &Medium) -> Result> + pub fn parse(packet: &Packet<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized, { @@ -236,7 +235,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), + NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), _ => { return Err(Error::Unrecognized); } @@ -251,7 +250,7 @@ impl<'a> Repr<'a> { let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); while packet.payload().len() - offset > 0 { let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?; - let opt = NdiscOptionRepr::parse(&pkt, medium)?; + let opt = NdiscOptionRepr::parse(&pkt)?; match opt { NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::Mtu(val) => mtu = Some(val), @@ -260,7 +259,7 @@ impl<'a> Repr<'a> { return Err(Error::Unrecognized); } } - offset += opt.buffer_len(medium); + offset += opt.buffer_len(); } Ok(Repr::RouterAdvert { hop_limit: packet.current_hop_limit(), @@ -277,7 +276,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), + NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), _ => { return Err(Error::Unrecognized); } @@ -294,7 +293,7 @@ impl<'a> Repr<'a> { let lladdr = if !packet.payload().is_empty() { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { - NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr(medium)), + NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), _ => { return Err(Error::Unrecognized); } @@ -315,7 +314,7 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(&packet.payload()[offset..])?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => { - lladdr = Some(opt.link_layer_addr(medium)); + lladdr = Some(opt.link_layer_addr()); offset += 8; } NdiscOptionType::RedirectedHeader => { @@ -349,7 +348,7 @@ impl<'a> Repr<'a> { } } - pub fn buffer_len(&self, medium: &Medium) -> usize { + pub fn buffer_len(&self) -> usize { match self { &Repr::RouterSolicit { lladdr } => match lladdr { Some(_) => field::UNUSED.end + 8, @@ -374,34 +373,11 @@ impl<'a> Repr<'a> { field::RETRANS_TM.end + offset } &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => { - match lladdr { - Some(_addr) => { - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => field::TARGET_ADDR.end + 8, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - // XXX: This is for 6LoWPAN - let mut len = field::TARGET_ADDR.end; - len += 2; - len += _addr.as_bytes().len(); - - // A packet of len == 30 is a packet with an Ethernet address - if len > 30 { - // TODO(thvdveld): find out why this padding is +4 and not +6 - // WireShark wants a padding of +6, however, then the packet is not accepted when using ping - // When a padding of +4 is used, then WireShark complains and says that the packet is malformed, - // however, ping accepts this packet. - len += 4; // Padding - } - len - } - #[cfg(feature = "medium-ip")] - Medium::Ip => unreachable!(), - } - } - None => field::TARGET_ADDR.end, + let mut offset = field::TARGET_ADDR.end; + if let Some(lladdr) = lladdr { + offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len(); } + offset } &Repr::Redirect { lladdr, @@ -420,7 +396,7 @@ impl<'a> Repr<'a> { } } - pub fn emit(&self, packet: &mut Packet<&mut T>, medium: &Medium) + pub fn emit(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { @@ -431,7 +407,7 @@ impl<'a> Repr<'a> { packet.clear_reserved(); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); + NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); } } @@ -455,26 +431,20 @@ impl<'a> Repr<'a> { let mut offset = 0; if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => offset += 6, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => offset += 8, - #[cfg(feature = "medium-ip")] - _ => unreachable!(), - } + let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr); + opt.emit(&mut opt_pkt); + offset += opt.buffer_len(); } if let Some(mtu) = mtu { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt, medium); + NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt); offset += 8; } if let Some(prefix_info) = prefix_info { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt, medium) + NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt) } } @@ -488,7 +458,7 @@ impl<'a> Repr<'a> { packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); + NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); } } @@ -504,7 +474,7 @@ impl<'a> Repr<'a> { packet.set_target_addr(target_addr); if let Some(lladdr) = lladdr { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); + NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); } } @@ -522,7 +492,7 @@ impl<'a> Repr<'a> { let offset = match lladdr { Some(lladdr) => { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); - NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); + NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); 8 } None => 0, @@ -530,7 +500,7 @@ impl<'a> Repr<'a> { if let Some(redirected_hdr) = redirected_hdr { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); - NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt, medium); + NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt); } } } @@ -543,7 +513,6 @@ mod test { use crate::phy::ChecksumCapabilities; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; use crate::wire::EthernetAddress; - use crate::wire::HardwareAddress; use crate::wire::Icmpv6Repr; static ROUTER_ADVERT_BYTES: [u8; 24] = [ @@ -559,9 +528,7 @@ mod test { router_lifetime: Duration::from_secs(900), reachable_time: Duration::from_millis(900), retrans_time: Duration::from_millis(900), - lladdr: Some(HardwareAddress::Ethernet(EthernetAddress([ - 0x52, 0x54, 0x00, 0x12, 0x34, 0x56, - ]))), + lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()), mtu: None, prefix_info: None, }) @@ -606,8 +573,7 @@ mod test { &MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, &packet, - &ChecksumCapabilities::default(), - &Medium::Ethernet, + &ChecksumCapabilities::default() ) .unwrap(), create_repr() @@ -623,7 +589,6 @@ mod test { &MOCK_IP_ADDR_2, &mut packet, &ChecksumCapabilities::default(), - &Medium::Ethernet, ); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 59e212b84..5c598b31c 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -2,17 +2,11 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; -use crate::phy::Medium; use crate::time::Duration; -use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr}; +use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN}; use crate::{Error, Result}; -#[cfg(feature = "medium-ethernet")] -use crate::wire::EthernetAddress; -#[cfg(feature = "medium-ieee802154")] -use crate::wire::Ieee802154Address; - -use crate::wire::HardwareAddress; +use crate::wire::RawHardwareAddress; enum_with_unknown! { /// NDISC Option Type @@ -90,12 +84,6 @@ mod field { // | Type | Length | Link-Layer Address ... // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // Link-Layer Address - #[cfg(feature = "medium-ethernet")] - pub const LL_ADDR_ETHERNET: Field = 2..8; - #[cfg(feature = "medium-ieee802154")] - pub const LL_ADDR_IEEE802154: Field = 2..10; - // Prefix Information Option fields. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Type | Length | Prefix Length |L|A| Reserved1 | @@ -225,23 +213,10 @@ impl> NdiscOption { impl> NdiscOption { /// Return the Source/Target Link-layer Address. #[inline] - pub fn link_layer_addr(&self, medium: &Medium) -> HardwareAddress { + pub fn link_layer_addr(&self) -> RawHardwareAddress { + let len = MAX_HARDWARE_ADDRESS_LEN.min(self.data_len() as usize * 8 - 2); let data = self.buffer.as_ref(); - - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - let addr = &data[field::LL_ADDR_ETHERNET]; - HardwareAddress::Ethernet(EthernetAddress::from_bytes(addr)) - } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - let addr = &data[field::LL_ADDR_IEEE802154]; - HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(addr)) - } - #[cfg(feature = "medium-ip")] - _ => todo!(), // TODO(thvdveld) - } + RawHardwareAddress::from_bytes(&data[2..len + 2]) } } @@ -322,21 +297,9 @@ impl + AsMut<[u8]>> NdiscOption { impl + AsMut<[u8]>> NdiscOption { /// Set the Source/Target Link-layer Address. #[inline] - pub fn set_link_layer_addr(&mut self, addr: HardwareAddress) { + pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) { let data = self.buffer.as_mut(); - match addr { - #[cfg(feature = "medium-ethernet")] - HardwareAddress::Ethernet(addr) => { - let data = &mut data[field::LL_ADDR_ETHERNET]; - data.copy_from_slice(addr.as_bytes()); - } - #[cfg(feature = "medium-ieee802154")] - HardwareAddress::Ieee802154(addr) => { - let data = &mut data[field::LL_ADDR_IEEE802154]; - data.copy_from_slice(addr.as_bytes()); - } - _ => todo!(), - } + data[2..2 + addr.len()].copy_from_slice(addr.as_bytes()) } } @@ -413,18 +376,17 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> { } } -//impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { -//fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//// XXX(thvdveld): how do we pass the medium? -//match Repr::parse(self, &Medium::Ethernet) { -//Ok(repr) => write!(f, "{}", repr), -//Err(err) => { -//write!(f, "NDISC Option ({})", err)?; -//Ok(()) -//} -//} -//} -//} +impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match Repr::parse(self) { + Ok(repr) => write!(f, "{}", repr), + Err(err) => { + write!(f, "NDISC Option ({})", err)?; + Ok(()) + } + } + } +} #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -447,8 +409,8 @@ pub struct RedirectedHeader<'a> { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr<'a> { - SourceLinkLayerAddr(HardwareAddress), - TargetLinkLayerAddr(HardwareAddress), + SourceLinkLayerAddr(RawHardwareAddress), + TargetLinkLayerAddr(RawHardwareAddress), PrefixInformation(PrefixInformation), RedirectedHeader(RedirectedHeader<'a>), Mtu(u32), @@ -461,21 +423,21 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC Option and return a high-level representation. - pub fn parse(opt: &'a NdiscOption<&'a T>, medium: &Medium) -> Result> + pub fn parse(opt: &'a NdiscOption<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized, { match opt.option_type() { Type::SourceLinkLayerAddr => { if opt.data_len() == 1 { - Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr(medium))) + Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr())) } else { Err(Error::Malformed) } } Type::TargetLinkLayerAddr => { if opt.data_len() == 1 { - Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr(medium))) + Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) } else { Err(Error::Malformed) } @@ -524,57 +486,38 @@ impl<'a> Repr<'a> { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self, medium: &Medium) -> usize { - match (self, medium) { - #[cfg(feature = "medium-ethernet")] - (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ethernet) => { - field::LL_ADDR_ETHERNET.end - } - #[cfg(feature = "medium-ieee802154")] - (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ieee802154) => { - field::LL_ADDR_IEEE802154.end - } - #[cfg(feature = "medium-ip")] - (&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), _) => { - unreachable!() + pub fn buffer_len(&self) -> usize { + match self { + &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => { + let len = 2 + addr.len(); + // Round up to next multiple of 8 + (len + 7) / 8 * 8 } - (&Repr::PrefixInformation(_), _) => field::PREFIX.end, - (&Repr::RedirectedHeader(RedirectedHeader { header, data }), _) => { + &Repr::PrefixInformation(_) => field::PREFIX.end, + &Repr::RedirectedHeader(RedirectedHeader { header, data }) => { field::IP_DATA + header.buffer_len() + data.len() } - (&Repr::Mtu(_), _) => field::MTU.end, - (&Repr::Unknown { length, .. }, _) => field::DATA(length).end, + &Repr::Mtu(_) => field::MTU.end, + &Repr::Unknown { length, .. } => field::DATA(length).end, } } /// Emit a high-level representation into an NDISC Option. - pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>, medium: &Medium) + pub fn emit(&self, opt: &mut NdiscOption<&'a mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, { match *self { Repr::SourceLinkLayerAddr(addr) => { opt.set_option_type(Type::SourceLinkLayerAddr); - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => opt.set_data_len(1), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => opt.set_data_len(2), - #[cfg(feature = "medium-ip")] - _ => unreachable!(), - } + let opt_len = addr.len() + 2; + opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8. opt.set_link_layer_addr(addr); } Repr::TargetLinkLayerAddr(addr) => { opt.set_option_type(Type::TargetLinkLayerAddr); - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => opt.set_data_len(1), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => opt.set_data_len(2), - #[cfg(feature = "medium-ip")] - _ => unreachable!(), - } + let opt_len = addr.len() + 2; + opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8. opt.set_link_layer_addr(addr); } Repr::PrefixInformation(PrefixInformation { @@ -626,42 +569,51 @@ impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "NDISC Option: ")?; match *self { - Repr::SourceLinkLayerAddr(addr) => write!(f, "SourceLinkLayer addr={}", addr), - Repr::TargetLinkLayerAddr(addr) => write!(f, "TargetLinkLayer addr={}", addr), + Repr::SourceLinkLayerAddr(addr) => { + write!(f, "SourceLinkLayer addr={}", addr) + } + Repr::TargetLinkLayerAddr(addr) => { + write!(f, "TargetLinkLayer addr={}", addr) + } Repr::PrefixInformation(PrefixInformation { prefix, prefix_len, .. - }) => write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len), + }) => { + write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) + } Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { write!(f, "RedirectedHeader header={}", header) } - Repr::Mtu(mtu) => write!(f, "MTU mtu={}", mtu), + Repr::Mtu(mtu) => { + write!(f, "MTU mtu={}", mtu) + } Repr::Unknown { type_: id, length, .. - } => write!(f, "Unknown({}) length={}", id, length), + } => { + write!(f, "Unknown({}) length={}", id, length) + } } } } -//use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; - -//impl> PrettyPrint for NdiscOption { -//fn pretty_print( -//buffer: &dyn AsRef<[u8]>, -//f: &mut fmt::Formatter, -//indent: &mut PrettyIndent, -//) -> fmt::Result { -//// TODO(thvdveld): how do we pass the medium? -//match NdiscOption::new_checked(buffer) { -//Err(err) => return write!(f, "{}({})", indent, err), -//Ok(ndisc) => match Repr::parse(&ndisc, &Medium::Ethernet) { -//Err(_) => Ok(()), -//Ok(repr) => { -//write!(f, "{}{}", indent, repr) -//} -//}, -//} -//} -//} +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; + +impl> PrettyPrint for NdiscOption { + fn pretty_print( + buffer: &dyn AsRef<[u8]>, + f: &mut fmt::Formatter, + indent: &mut PrettyIndent, + ) -> fmt::Result { + match NdiscOption::new_checked(buffer) { + Err(err) => return write!(f, "{}({})", indent, err), + Ok(ndisc) => match Repr::parse(&ndisc) { + Err(_) => Ok(()), + Ok(repr) => { + write!(f, "{}{}", indent, repr) + } + }, + } + } +} #[cfg(test)] mod test { @@ -721,20 +673,14 @@ mod test { let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); { assert_eq!( - Repr::parse( - &NdiscOption::new_unchecked(&bytes), - &crate::phy::Medium::Ethernet - ), + Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::SourceLinkLayerAddr(addr.into())) ); } bytes[0] = 0x02; { assert_eq!( - Repr::parse( - &NdiscOption::new_unchecked(&bytes), - &crate::phy::Medium::Ethernet - ), + Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::TargetLinkLayerAddr(addr.into())) ); } @@ -750,10 +696,7 @@ mod test { prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); assert_eq!( - Repr::parse( - &NdiscOption::new_unchecked(&PREFIX_OPT_BYTES), - &crate::phy::Medium::Ethernet - ), + Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), Ok(repr) ); } @@ -769,7 +712,7 @@ mod test { prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), }); let mut opt = NdiscOption::new_unchecked(&mut bytes); - repr.emit(&mut opt, &crate::phy::Medium::Ethernet); + repr.emit(&mut opt); assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]); } @@ -777,10 +720,7 @@ mod test { fn test_repr_parse_mtu() { let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc]; assert_eq!( - Repr::parse( - &NdiscOption::new_unchecked(&bytes), - &crate::phy::Medium::Ethernet - ), + Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::Mtu(1500)) ); } From 102db1d156e092c15af70e3ae489f3d06e11fc21 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 04:38:10 +0200 Subject: [PATCH 228/566] wire: remove HardwareAddress::BROADCAST --- src/iface/interface.rs | 33 +++++++++++++++++++++------------ src/iface/neighbor.rs | 4 +--- src/wire/mod.rs | 5 ----- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 1435edcf8..c01da7cde 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -2165,42 +2165,51 @@ impl<'a> InterfaceInner<'a> { where Tx: TxToken, { + if dst_addr.is_broadcast() { + let hardware_addr = match cx.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST), + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + }; + + return Ok((hardware_addr, tx_token)); + } + if dst_addr.is_multicast() { let b = dst_addr.as_bytes(); let hardware_addr = match *dst_addr { - IpAddress::Unspecified => None, + IpAddress::Unspecified => unreachable!(), #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(_addr) => { - Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ 0x01, 0x00, 0x5e, b[1] & 0x7F, b[2], b[3], - ]))) + ])) } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(_addr) => match cx.caps.medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - Some(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ - 0x33, 0x33, b[12], b[13], b[14], b[15], - ]))) - } + Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0x33, 0x33, b[12], b[13], b[14], b[15], + ])), #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { // Not sure if this is correct - Some(HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)) + HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST) } #[cfg(feature = "medium-ip")] Medium::Ip => unreachable!(), }, }; - if let Some(hardware_addr) = hardware_addr { - return Ok((hardware_addr, tx_token)); - } + return Ok((hardware_addr, tx_token)); } let dst_addr = self.route(dst_addr, cx.now)?; diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 0b6b1469b..04f68623c 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -196,9 +196,7 @@ impl<'a> Cache<'a> { } pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer { - if protocol_addr.is_broadcast() { - return Answer::Found(HardwareAddress::BROADCAST); - } + assert!(protocol_addr.is_unicast()); if let Some(&Neighbor { expires_at, diff --git a/src/wire/mod.rs b/src/wire/mod.rs index c891a1bb5..8ffa99236 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -246,7 +246,6 @@ pub use self::dhcpv4::{ #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HardwareAddress { - BROADCAST, #[cfg(feature = "medium-ethernet")] Ethernet(EthernetAddress), #[cfg(feature = "medium-ieee802154")] @@ -260,7 +259,6 @@ impl HardwareAddress { HardwareAddress::Ethernet(addr) => addr.as_bytes(), #[cfg(feature = "medium-ieee802154")] HardwareAddress::Ieee802154(addr) => addr.as_bytes(), - _ => todo!(), } } @@ -271,7 +269,6 @@ impl HardwareAddress { HardwareAddress::Ethernet(addr) => addr.is_unicast(), #[cfg(feature = "medium-ieee802154")] HardwareAddress::Ieee802154(addr) => addr.is_unicast(), - _ => todo!(), } } @@ -282,7 +279,6 @@ impl HardwareAddress { HardwareAddress::Ethernet(addr) => addr.is_broadcast(), #[cfg(feature = "medium-ieee802154")] HardwareAddress::Ieee802154(addr) => addr.is_broadcast(), - _ => todo!(), } } } @@ -290,7 +286,6 @@ impl HardwareAddress { impl core::fmt::Display for HardwareAddress { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { - HardwareAddress::BROADCAST => write!(f, "BROADCAST"), #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => write!(f, "{}", addr), #[cfg(feature = "medium-ieee802154")] From 4b1de1199c5369b86c1f881804d0a97d10819f9c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 04:38:44 +0200 Subject: [PATCH 229/566] ipv6: fix Solicited Node address calculation --- src/wire/ipv6.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 3dee62947..0dc97fab3 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -183,12 +183,10 @@ impl Address { /// unicast. pub fn solicited_node(&self) -> Address { assert!(self.is_unicast()); - let mut bytes = [ - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - bytes[14..].copy_from_slice(&self.0[14..]); - Address(bytes) + Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, + self.0[13], self.0[14], self.0[15], + ]) } } From cd40acb625733f5a3dfa020ac770ff9584f7ec3e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 04:54:09 +0200 Subject: [PATCH 230/566] 6lowpan: do not fill neighbor cache from random packets Equivalent of 6210612be047ee706ac729015cdbc2581e6ae9a3 for 6lowpan --- src/iface/interface.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index c01da7cde..6cc1bb5c0 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1136,22 +1136,6 @@ impl<'a> InterfaceInner<'a> { let payload = iphc_packet.payload(); let ip_repr = IpRepr::Sixlowpan(iphc_repr); - // Fill the neighbor cache from IP header of unicast frames. - let ip_addr = ip_repr.src_addr(); - if self.in_same_network(&ip_addr) - && self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&ip_addr, cx.now) - .found() - { - self.neighbor_cache.as_mut().unwrap().fill( - ip_addr, - ieee802154_repr.src_addr.unwrap().into(), // TODO(thvdveld): Add checks before calling unwrap - cx.now, - ); - } // Currently we assume the next header is a UDP, so we mark all the rest with todo. match iphc_repr.next_header { From bdd09c4307a75be22ba488d297c0cdeaba0b7aef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 05:01:33 +0200 Subject: [PATCH 231/566] ieee80154: process packets without the FCS. We assume the FCS is checked by lower layers or by hardware. - Makes it consistent with Ethernet mediums, where we don't handle the FCS either. - Linux ieee802154 raw sockets send/receive packets without the FCS. --- src/iface/interface.rs | 7 ------- src/wire/ieee802154.rs | 13 ++----------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 6cc1bb5c0..5f7a05c84 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1136,7 +1136,6 @@ impl<'a> InterfaceInner<'a> { let payload = iphc_packet.payload(); let ip_repr = IpRepr::Sixlowpan(iphc_repr); - // Currently we assume the next header is a UDP, so we mark all the rest with todo. match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { @@ -2421,8 +2420,6 @@ impl<'a> InterfaceInner<'a> { _ => return Err(Error::Unrecognized), } - //tx_len += 2; // XXX: FCS calculation not needed when doing it in hardware - tx_token.consume(cx.now, tx_len, |mut tx_buffer| { // 1. Create the header of 802.15.4 let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buffer); @@ -2465,10 +2462,6 @@ impl<'a> InterfaceInner<'a> { _ => return Err(Error::Unrecognized), } - //let fcs = crate::wire::ieee802154::calculate_crc(&tx_buffer[..tx_len-2]); - //tx_buffer[tx_len-1] = ((fcs >> 8) & 0xff) as u8; - //tx_buffer[tx_len-2] = (fcs & 0xff) as u8; - Ok(()) }) } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 68ec3a5e0..fa05947d3 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -516,12 +516,6 @@ impl> Frame { todo!(); } - - /// Return the FCS fields - #[inline] - pub fn fcs(&self) -> &[u8] { - &self.buffer.as_ref()[self.buffer.as_ref().len() - 2..] - } } impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> { @@ -533,7 +527,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> { let data = &self.buffer.as_ref()[field::ADDRESSING]; let offset = self.addressing_fields().unwrap().len(); - Some(&data[offset..data.len() - 2]) // Remove the FCS field of the IEEE80.15.4 frame. + Some(&data[offset..]) } _ => None, } @@ -692,14 +686,13 @@ impl> fmt::Display for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "IEEE802.15.4 frame type={} seq={:2x?} dst_pan={:x?} dest={:x?} src_pan={:?} src={:x?} fcs={:x?}", + "IEEE802.15.4 frame type={} seq={:2x?} dst_pan={:x?} dest={:x?} src_pan={:?} src={:x?}", self.frame_type(), self.sequence_number(), self.dst_pan_id(), self.dst_addr(), self.src_pan_id(), self.src_addr(), - self.fcs(), ) } } @@ -908,7 +901,6 @@ mod test { 0xff, 0xff, // Short destination address 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, // Extended source address 0x2b, 0x00, 0x00, 0x00, // payload - 0xb3, 0x0d // FSM ]; frame_type -> FrameType::Data, security_enabled -> false, @@ -919,6 +911,5 @@ mod test { frame_version -> FrameVersion::Ieee802154_2006, src_addressing_mode -> AddressingMode::Extended, //payload -> Some(&[0x2b, 0x00, 0x00, 0x00]), - fcs -> [0xb3, 0x0d], } } From f35e76068b9b0ca0711ea54c34393da98ae479f9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 05:14:19 +0200 Subject: [PATCH 232/566] phy: Use right protocol on RawSocket based on the medium. --- src/phy/raw_socket.rs | 6 ++++-- src/phy/sys/bpf.rs | 3 ++- src/phy/sys/raw_socket.rs | 30 ++++++++++++++---------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 95b2dbc92..e01438f46 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -11,6 +11,7 @@ use crate::Result; /// A socket that captures or transmits the complete frame. #[derive(Debug)] pub struct RawSocket { + medium: Medium, lower: Rc>, mtu: usize, } @@ -27,7 +28,7 @@ impl RawSocket { /// This requires superuser privileges or a corresponding capability bit /// set on the executable. pub fn new(name: &str, medium: Medium) -> io::Result { - let mut lower = sys::RawSocketDesc::new(name)?; + let mut lower = sys::RawSocketDesc::new(name, medium)?; lower.bind_interface()?; let mut mtu = lower.interface_mtu()?; @@ -40,6 +41,7 @@ impl RawSocket { } Ok(RawSocket { + medium, lower: Rc::new(RefCell::new(lower)), mtu: mtu, }) @@ -53,7 +55,7 @@ impl<'a> Device<'a> for RawSocket { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, - medium: Medium::Ethernet, + medium: self.medium, ..DeviceCapabilities::default() } } diff --git a/src/phy/sys/bpf.rs b/src/phy/sys/bpf.rs index 4999f16f8..1935ac3fc 100644 --- a/src/phy/sys/bpf.rs +++ b/src/phy/sys/bpf.rs @@ -5,6 +5,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use libc; use super::{ifreq, ifreq_for}; +use crate::phy::Medium; use crate::wire::ETHERNET_HEADER_LEN; /// set interface @@ -67,7 +68,7 @@ fn open_device() -> io::Result { } impl BpfDevice { - pub fn new(name: &str) -> io::Result { + pub fn new(name: &str, _medium: Medium) -> io::Result { Ok(BpfDevice { fd: open_device()?, ifreq: ifreq_for(name), diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 23c9cdf48..2161b3d38 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -1,10 +1,12 @@ use super::*; +use crate::phy::Medium; use crate::wire::EthernetFrame; use std::os::unix::io::{AsRawFd, RawFd}; use std::{io, mem}; #[derive(Debug)] pub struct RawSocketDesc { + protocol: libc::c_short, lower: libc::c_int, ifreq: ifreq, } @@ -16,15 +18,17 @@ impl AsRawFd for RawSocketDesc { } impl RawSocketDesc { - pub fn new(name: &str) -> io::Result { - let lower = unsafe { - // TODO(thvdveld) - //#[cfg(feature = "medium-ieee802154")] - //let protocol = imp::ETH_P_IEEE802154; - + pub fn new(name: &str, medium: Medium) -> io::Result { + let protocol = match medium { #[cfg(feature = "medium-ethernet")] - let protocol = imp::ETH_P_ALL; + Medium::Ethernet => imp::ETH_P_ALL, + #[cfg(feature = "medium-ip")] + Medium::Ip => imp::ETH_P_ALL, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => imp::ETH_P_IEEE802154, + }; + let lower = unsafe { let lower = libc::socket( libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK, @@ -37,7 +41,8 @@ impl RawSocketDesc { }; Ok(RawSocketDesc { - lower: lower, + protocol, + lower, ifreq: ifreq_for(name), }) } @@ -51,16 +56,9 @@ impl RawSocketDesc { } pub fn bind_interface(&mut self) -> io::Result<()> { - // TODO(thvdveld) - //#[cfg(feature = "medium-ieee802154")] - //let protocol = imp::ETH_P_IEEE802154; - - #[cfg(feature = "medium-ethernet")] - let protocol = imp::ETH_P_ALL; - let sockaddr = libc::sockaddr_ll { sll_family: libc::AF_PACKET as u16, - sll_protocol: protocol.to_be() as u16, + sll_protocol: self.protocol.to_be() as u16, sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?, sll_hatype: 1, sll_pkttype: 0, From b74cd580365b6fee60489d2acc7882d2b104515f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 7 Oct 2021 05:26:44 +0200 Subject: [PATCH 233/566] example/6lowpan: expand readme, do not use monitor interface. Using a raw socket on `monitor0` causes weird results: packets we receive include FCS, packets we send are parsed as if they didn't have FCS, except by wireshark which always expects a FCS?? Turns out the sane way is to use raw sockets on normal `wpanX` interfaces, in which case all packets we send/receive are without FCS. --- examples/sixlowpan.rs | 74 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index b936224df..2b3439bea 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -1,27 +1,44 @@ -/* - -# Setup - - modprobe mac802154_hwsim - - ip link set wpan0 down - iwpan dev wpan0 set pan_id 0xbeef - ip link add link wpan0 name lowpan0 type lowpan - ip link set wpan0 up - ip link set lowpan0 up - - iwpan dev wpan1 del - iwpan phy phy1 interface add monitor%d type monitor - -# Running - - sudo ./target/debug/examples/sixlowpan - -# Teardown - - rmmod mac802154_hwsim - - */ +//! 6lowpan exmaple +//! +//! This example is designed to run using the Linux ieee802154/6lowpan support, +//! using mac802154_hwsim. +//! +//! mac802154_hwsim allows you to create multiple "virtual" radios and specify +//! which is in range with which. This is very useful for testing without +//! needing real hardware. By default it creates two interfaces `wpan0` and +//! `wpan1` that are in range with each other. You can customize this with +//! the `wpan-hwsim` tool. +//! +//! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1` +//! unconfigured so smoltcp can use it with a raw socket. +//! +//! # Setup +//! +//! modprobe mac802154_hwsim +//! +//! ip link set wpan0 down +//! ip link set wpan1 down +//! iwpan dev wpan0 set pan_id 0xbeef +//! iwpan dev wpan1 set pan_id 0xbeef +//! ip link add link wpan0 name lowpan0 type lowpan +//! ip link set wpan0 up +//! ip link set wpan1 up +//! ip link set lowpan0 up +//! +//! # Running +//! +//! Run it with `sudo ./target/debug/examples/sixlowpan`. +//! +//! You can set wireshark to sniff on interface `wpan0` to see the packets. +//! +//! Ping it with `ping fe80::180b:4242:4242:4242%lowpan0`. +//! +//! Speak UDP with `nc -uv fe80::180b:4242:4242:4242%lowpan0 6969`. +//! +//! # Teardown +//! +//! rmmod mac802154_hwsim +//! mod utils; @@ -35,7 +52,7 @@ use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::SocketSet; use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use smoltcp::time::Instant; -use smoltcp::wire::{IpAddress, IpCidr}; +use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; fn main() { utils::setup_logging(""); @@ -45,7 +62,7 @@ fn main() { let mut matches = utils::parse_options(&opts, free); - let device = RawSocket::new("monitor0", Medium::Ieee802154).unwrap(); + let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap(); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); @@ -64,7 +81,10 @@ fn main() { 64, )]; - let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device) + .ip_addrs(ip_addrs) + .dst_pan_id(Ieee802154Pan(0xbeef)) + .src_pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache); From f67f02488292f397f16fe011273161048f29c308 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 09:45:20 +0200 Subject: [PATCH 234/566] Use one PAN ID for source and destination Also check for the correct destination PAN id when receiving a frame (as discussed). Linux does this as well. However, hardware implementations also can drop those packets. --- examples/sixlowpan.rs | 3 +-- src/iface/interface.rs | 45 +++++++++++++++++------------------------- src/socket/mod.rs | 8 ++------ 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 2b3439bea..138c22c4b 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -83,8 +83,7 @@ fn main() { let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .dst_pan_id(Ieee802154Pan(0xbeef)) - .src_pan_id(Ieee802154Pan(0xbeef)); + .pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 5f7a05c84..83c16f86a 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -39,9 +39,7 @@ struct InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] sequence_no: u8, #[cfg(feature = "medium-ieee802154")] - src_pan_id: Option, - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: Option, + pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -63,9 +61,7 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { #[cfg(feature = "medium-ieee802154")] sequence_no: u8, #[cfg(feature = "medium-ieee802154")] - src_pan_id: Option, - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: Option, + pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -119,9 +115,7 @@ let iface = InterfaceBuilder::new(device) #[cfg(feature = "medium-ieee802154")] sequence_no: 1, #[cfg(feature = "medium-ieee802154")] - src_pan_id: None, - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: None, + pan_id: None, ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] @@ -155,17 +149,12 @@ let iface = InterfaceBuilder::new(device) self } - /// Set the IEEE802.15.4 source PAN ID the interface will use. - #[cfg(feature = "medium-ieee802154")] - pub fn src_pan_id(mut self, pan_id: Ieee802154Pan) -> Self { - self.src_pan_id = Some(pan_id); - self - } - - /// Set the IEEE802.15.4 destination PAN ID the interface will use. + /// Set the IEEE802.15.4 PAN ID the interface will use. + /// + /// **NOTE**: we use the same PAN ID for destination and source. #[cfg(feature = "medium-ieee802154")] - pub fn dst_pan_id(mut self, pan_id: Ieee802154Pan) -> Self { - self.dst_pan_id = Some(pan_id); + pub fn pan_id(mut self, pan_id: Ieee802154Pan) -> Self { + self.pan_id = Some(pan_id); self } @@ -312,9 +301,7 @@ let iface = InterfaceBuilder::new(device) #[cfg(feature = "medium-ieee802154")] sequence_no: self.sequence_no, #[cfg(feature = "medium-ieee802154")] - src_pan_id: self.src_pan_id, - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: self.dst_pan_id, + pan_id: self.pan_id, }, } } @@ -958,9 +945,7 @@ where ))] hardware_addr: self.inner.hardware_addr, #[cfg(feature = "medium-ieee802154")] - src_pan_id: self.inner.src_pan_id, - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: self.inner.dst_pan_id, + pan_id: self.inner.pan_id, } } } @@ -1111,6 +1096,12 @@ impl<'a> InterfaceInner<'a> { let ieee802154_frame = Ieee802154Frame::new_checked(sixlowpan_payload)?; let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame)?; + if ieee802154_repr.dst_pan_id != cx.pan_id { + // We sillently drop frames that have the wrong PAN id. + // NOTE: this is most of the time already implememted in hardware. + return Ok(None); + } + match ieee802154_frame.payload() { Some(payload) => self.process_sixlowpan(cx, sockets, &ieee802154_repr, payload), None => Ok(None), @@ -2373,9 +2364,9 @@ impl<'a> InterfaceInner<'a> { sequence_number: Some(self.get_sequence_number()), pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: cx.dst_pan_id, + dst_pan_id: cx.pan_id, dst_addr: Some(dst_hardware_addr), - src_pan_id: cx.src_pan_id, + src_pan_id: cx.pan_id, src_addr: ll_src_addr, }; diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 5f29026de..9836c4bb4 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -193,9 +193,7 @@ pub(crate) struct Context { ))] pub hardware_addr: Option, #[cfg(feature = "medium-ieee802154")] - pub src_pan_id: Option, - #[cfg(feature = "medium-ieee802154")] - pub dst_pan_id: Option, + pub pan_id: Option, pub caps: DeviceCapabilities, } @@ -232,8 +230,6 @@ impl Context { now: Instant::from_millis_const(0), #[cfg(feature = "medium-ieee802154")] - src_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), - #[cfg(feature = "medium-ieee802154")] - dst_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), + pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), }; } From 7bad4cff577935c16c76aa679c583f122a1eda25 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 09:47:41 +0200 Subject: [PATCH 235/566] Use `net_debug` and drop instead of `todo!` --- src/iface/interface.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 83c16f86a..9b6653cb3 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1131,8 +1131,9 @@ impl<'a> InterfaceInner<'a> { match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { match SixlowpanNhcPacket::dispatch(payload)? { - SixlowpanNhcPacket::ExtensionHeader(ext_header) => { - todo!("{:?}", ext_header) + SixlowpanNhcPacket::ExtensionHeader(_) => { + net_debug!("Extension headers are currently not supported for 6LoWPAN"); + Ok(None) } SixlowpanNhcPacket::UdpHeader(udp_packet) => { // Handle the UDP @@ -1184,7 +1185,10 @@ impl<'a> InterfaceInner<'a> { IpProtocol::Icmpv6 => { self.process_icmpv6(cx, sockets, ip_repr, iphc_packet.payload()) } - hdr => todo!("{:?}", hdr), + _ => { + net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN"); + Ok(None) + } }, } } From aed9fdb8be8fae34fdf9f77f60dbf74efaa7a64d Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 09:58:22 +0200 Subject: [PATCH 236/566] Send ICMP unreachable when no UDP socket is found --- src/iface/interface.rs | 53 +++++++++++++++++++++++++----------------- src/wire/mod.rs | 4 ++++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 9b6653cb3..30bdd7681 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1158,26 +1158,36 @@ impl<'a> InterfaceInner<'a> { } } - // The packet wasn't handled by a socket, send an ICMP port unreachable packet. - match ip_repr { - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(ipv6_repr) => { - let payload_len = icmp_reply_payload_len( - payload.len(), - IPV6_MIN_MTU, - ipv6_repr.buffer_len(), - ); - let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ipv6_repr, - data: &payload[0..payload_len], - }; - Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) - } - IpRepr::Unspecified { .. } => Err(Error::Unaddressable), - IpRepr::Sixlowpan(_) => Err(Error::Malformed), // XXX(thvdveld): this is just wrong; - r => todo!("{:?}", r), - } + // TODO(thvdveld): verify this implementation of sending an ICMP + let src_addr = match ip_repr.src_addr() { + IpAddress::Ipv6(addr) => addr, + _ => unreachable!(), + }; + + let dst_addr = match ip_repr.dst_addr() { + IpAddress::Ipv6(addr) => addr, + _ => unreachable!(), + }; + + let ipv6_repr = Ipv6Repr { + src_addr, + dst_addr, + hop_limit: ip_repr.hop_limit(), + next_header: IpProtocol::Unknown(0), + payload_len: ip_repr.payload_len(), + }; + + let payload_len = icmp_reply_payload_len( + payload.len(), + IPV6_MIN_MTU, + ipv6_repr.buffer_len(), + ); + let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ipv6_repr, + data: &payload[0..payload_len], + }; + Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } } } @@ -2284,7 +2294,8 @@ impl<'a> InterfaceInner<'a> { &ip_repr.dst_addr(), )? { (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - _ => unreachable!(), + #[cfg(feature = "medium-ieee802154")] + (HardwareAddress::Ieee802154(_), _) => unreachable!(), }; self.dispatch_ethernet(cx, tx_token, ip_repr.total_len(), |mut frame| { diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 8ffa99236..e9e433f8b 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -341,6 +341,10 @@ impl RawHardwareAddress { self.len as usize } + pub fn is_empty(&self) -> bool { + self.len == 0 + } + pub fn parse(&self, medium: Medium) -> Result { match medium { #[cfg(feature = "medium-ethernet")] From 3c4982258f7e5af4b8467e7b3b00799bea9a0736 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 10:52:31 +0200 Subject: [PATCH 237/566] Set HardwareAddress behind feature flag When there is no medium in the feature flags, then there is no HardwareAddress. --- src/wire/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index e9e433f8b..6b331a1ab 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -243,6 +243,7 @@ pub use self::dhcpv4::{ }; /// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HardwareAddress { @@ -252,6 +253,7 @@ pub enum HardwareAddress { Ieee802154(Ieee802154Address), } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl HardwareAddress { pub fn as_bytes(&self) -> &[u8] { match self { @@ -283,6 +285,7 @@ impl HardwareAddress { } } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl core::fmt::Display for HardwareAddress { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { @@ -316,12 +319,14 @@ pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8; /// Unparsed hardware address. /// /// Used to make NDISC parsing agnostic of the hardware medium in use. +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct RawHardwareAddress { len: u8, data: [u8; MAX_HARDWARE_ADDRESS_LEN], } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl RawHardwareAddress { pub fn from_bytes(addr: &[u8]) -> Self { let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN]; @@ -371,6 +376,7 @@ impl RawHardwareAddress { } } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl core::fmt::Display for RawHardwareAddress { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { for (i, &b) in self.as_bytes().iter().enumerate() { @@ -397,6 +403,7 @@ impl From for RawHardwareAddress { } } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl From for RawHardwareAddress { fn from(addr: HardwareAddress) -> Self { Self::from_bytes(addr.as_bytes()) From 2de8b7ad736374c75f87bea3b8cb0b1eeaf7f86c Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 10:56:54 +0200 Subject: [PATCH 238/566] Defmt for RawHardwareAddress --- src/wire/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 6b331a1ab..cd021524b 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -321,6 +321,7 @@ pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8; /// Used to make NDISC parsing agnostic of the hardware medium in use. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RawHardwareAddress { len: u8, data: [u8; MAX_HARDWARE_ADDRESS_LEN], From 954a7576c9eaa5a7516801aad4fa1781695ec266 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 11:10:05 +0200 Subject: [PATCH 239/566] Remove some more todos --- src/wire/ieee802154.rs | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index fa05947d3..90b58db1f 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -493,28 +493,8 @@ impl> Frame { return None; } - todo!(); - } - - /// Return the IE field (the header as well as the payload IE) - #[inline] - pub fn ie_field(&self) -> Option<&[u8]> { - match self.frame_type() { - FrameType::Data | FrameType::MacCommand | FrameType::Multipurpose => (), - FrameType::Beacon | FrameType::Acknowledgement - if self.frame_version() == FrameVersion::Ieee802154 => {} - FrameType::Beacon - | FrameType::Acknowledgement - | FrameType::Extended - | FrameType::FragmentOrFrak - | FrameType::Unknown(_) => return None, - } - - if !self.ie_present() { - return None; - } - - todo!(); + net_debug!("Auxilliary security header is currently not supported."); + None } } @@ -614,7 +594,7 @@ impl + AsMut<[u8]>> Frame { #[inline] pub fn set_src_pan_id(&mut self, value: Pan) { let offset = match self.dst_addressing_mode() { - AddressingMode::Absent => todo!("{}", self.dst_addressing_mode()), + AddressingMode::Absent => 0, AddressingMode::Short => 2, AddressingMode::Extended => 8, _ => unreachable!(), @@ -628,7 +608,7 @@ impl + AsMut<[u8]>> Frame { #[inline] pub fn set_src_addr(&mut self, mut value: Address) { let offset = match self.dst_addressing_mode() { - AddressingMode::Absent => todo!("{}", self.dst_addressing_mode()), + AddressingMode::Absent => 0, AddressingMode::Short => 2, AddressingMode::Extended => 8, _ => unreachable!(), From 0c39c50433a1b2bad352757ddf301fa92e381a2b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 11 Oct 2021 17:15:06 +0200 Subject: [PATCH 240/566] Set flags to zero, because buffer could be reused. --- src/wire/sixlowpan.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 3bef91202..8c07cd5a1 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -528,11 +528,11 @@ pub mod iphc { set_field!(set_tf_field, 0b11, 11); set_field!(set_nh_field, 0b1, 10); set_field!(set_hlim_field, 0b11, 8); - //set_field!(set_cid_field, 0b1, 7); + set_field!(set_cid_field, 0b1, 7); set_field!(set_sac_field, 0b1, 6); set_field!(set_sam_field, 0b11, 4); set_field!(set_m_field, 0b1, 3); - //set_field!(set_dac_field, 0b1, 2); + set_field!(set_dac_field, 0b1, 2); set_field!(set_dam_field, 0b11, 0); fn set_field(&mut self, idx: usize, value: &[u8]) { @@ -591,6 +591,9 @@ pub mod iphc { ll_src_addr: Option, mut idx: usize, ) -> usize { + self.set_cid_field(0); + self.set_sac_field(0); + self.set_sam_field(0b11); let src = src_addr.as_bytes(); if src_addr == ipv6::Address::UNSPECIFIED { self.set_sac_field(1); @@ -651,6 +654,9 @@ pub mod iphc { ll_dst_addr: Option, mut idx: usize, ) -> usize { + self.set_dac_field(0); + self.set_dam_field(0); + self.set_m_field(0); let dst = dst_addr.as_bytes(); if dst_addr.is_multicast() { self.set_m_field(1); From ddfd6f883c35495438add945b2ead936ff8f85e9 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 12 Oct 2021 14:49:35 +0200 Subject: [PATCH 241/566] Remove IpRepr::Sixlowpan --- src/iface/interface.rs | 95 ++++++++---------------------------------- src/wire/ip.rs | 25 ----------- src/wire/sixlowpan.rs | 2 +- 3 files changed, 18 insertions(+), 104 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 30bdd7681..a347840c7 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1125,7 +1125,13 @@ impl<'a> InterfaceInner<'a> { )?; let payload = iphc_packet.payload(); - let ip_repr = IpRepr::Sixlowpan(iphc_repr); + let mut ipv6_repr = Ipv6Repr { + src_addr: iphc_repr.src_addr, + dst_addr: iphc_repr.dst_addr, + hop_limit: iphc_repr.hop_limit, + next_header: IpProtocol::Unknown(0), + payload_len: iphc_repr.buffer_len(), + }; // Currently we assume the next header is a UDP, so we mark all the rest with todo. match iphc_repr.next_header { @@ -1136,6 +1142,7 @@ impl<'a> InterfaceInner<'a> { Ok(None) } SixlowpanNhcPacket::UdpHeader(udp_packet) => { + ipv6_repr.next_header = IpProtocol::Udp; // Handle the UDP let udp_repr = SixlowpanUdpRepr::parse( &udp_packet, @@ -1147,36 +1154,21 @@ impl<'a> InterfaceInner<'a> { // Look for UDP sockets that will accept the UDP packet. // If it does not accept the packet, then send an ICMP message. for mut udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { - if !udp_socket.accepts(&ip_repr, &udp_repr) { + if !udp_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { continue; } - match udp_socket.process(cx, &ip_repr, &udp_repr, udp_packet.payload()) - { + match udp_socket.process( + cx, + &IpRepr::Ipv6(ipv6_repr), + &udp_repr, + udp_packet.payload(), + ) { Ok(()) => return Ok(None), Err(e) => return Err(e), } } - // TODO(thvdveld): verify this implementation of sending an ICMP - let src_addr = match ip_repr.src_addr() { - IpAddress::Ipv6(addr) => addr, - _ => unreachable!(), - }; - - let dst_addr = match ip_repr.dst_addr() { - IpAddress::Ipv6(addr) => addr, - _ => unreachable!(), - }; - - let ipv6_repr = Ipv6Repr { - src_addr, - dst_addr, - hop_limit: ip_repr.hop_limit(), - next_header: IpProtocol::Unknown(0), - payload_len: ip_repr.payload_len(), - }; - let payload_len = icmp_reply_payload_len( payload.len(), IPV6_MIN_MTU, @@ -1193,7 +1185,8 @@ impl<'a> InterfaceInner<'a> { } SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { IpProtocol::Icmpv6 => { - self.process_icmpv6(cx, sockets, ip_repr, iphc_packet.payload()) + ipv6_repr.next_header = IpProtocol::Icmpv6; + self.process_icmpv6(cx, sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) } _ => { net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN"); @@ -1627,24 +1620,6 @@ impl<'a> InterfaceInner<'a> { }; Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) } - #[cfg(feature = "medium-ieee802154")] - IpRepr::Sixlowpan(sixlowpan_repr) => { - let icmp_reply_repr = Icmpv6Repr::EchoReply { - ident, - seq_no, - data, - }; - Ok(self.icmpv6_reply( - Ipv6Repr { - src_addr: sixlowpan_repr.src_addr, - dst_addr: sixlowpan_repr.dst_addr, - next_header: IpProtocol::Unknown(0), - payload_len: data.len(), - hop_limit: 64, - }, - icmp_reply_repr, - )) - } _ => Err(Error::Unrecognized), }, @@ -1655,20 +1630,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx, ipv6_repr, repr), - #[cfg(feature = "medium-ieee802154")] - IpRepr::Sixlowpan(sixlowpan_repr) => { - self.process_ndisc( - cx, - Ipv6Repr { - src_addr: sixlowpan_repr.src_addr, - dst_addr: sixlowpan_repr.dst_addr, - next_header: IpProtocol::Unknown(0), - payload_len: 10, // 2 + 8 - hop_limit: sixlowpan_repr.hop_limit, - }, - repr, - ) - } _ => Ok(None), }, @@ -1980,28 +1941,6 @@ impl<'a> InterfaceInner<'a> { }; Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } - #[cfg(feature = "proto-sixlowpan")] - IpRepr::Sixlowpan(sixlowpan_repr) => { - let ipv6_repr = Ipv6Repr { - src_addr: sixlowpan_repr.src_addr, - dst_addr: sixlowpan_repr.dst_addr, - next_header: IpProtocol::Udp, // XXX - payload_len: ip_payload.len(), - hop_limit: sixlowpan_repr.hop_limit, - }; - - let payload_len = icmp_reply_payload_len( - ip_payload.len(), - IPV6_MIN_MTU, - sixlowpan_repr.buffer_len(), - ); - let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ipv6_repr, - data: &ip_payload[0..payload_len], - }; - Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) - } IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index a084025e1..16c9de616 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -6,8 +6,6 @@ use crate::phy::ChecksumCapabilities; use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; -#[cfg(feature = "proto-sixlowpan")] -use crate::wire::{SixlowpanIphcPacket, SixlowpanIphcRepr}; use crate::{Error, Result}; /// Internet protocol version. @@ -515,8 +513,6 @@ pub enum Repr { Ipv4(Ipv4Repr), #[cfg(feature = "proto-ipv6")] Ipv6(Ipv6Repr), - #[cfg(feature = "proto-sixlowpan")] - Sixlowpan(SixlowpanIphcRepr), } #[cfg(feature = "proto-ipv4")] @@ -542,8 +538,6 @@ impl Repr { Repr::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(_) => Version::Ipv6, - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(_) => Version::Ipv6, } } @@ -555,8 +549,6 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr), - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => Address::Ipv6(repr.src_addr), } } @@ -568,8 +560,6 @@ impl Repr { Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr), - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => Address::Ipv6(repr.dst_addr), } } @@ -581,8 +571,6 @@ impl Repr { Repr::Ipv4(repr) => repr.protocol, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.next_header, - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => todo!("{:?}", repr), } } @@ -594,8 +582,6 @@ impl Repr { Repr::Ipv4(repr) => repr.payload_len, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.payload_len, - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => todo!("{:?}", repr), } } @@ -616,8 +602,6 @@ impl Repr { ref mut payload_len, .. }) => *payload_len = length, - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(_) => todo!(), } } @@ -629,8 +613,6 @@ impl Repr { Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit, - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(SixlowpanIphcRepr { hop_limit, .. }) => hop_limit, } } @@ -771,9 +753,6 @@ impl Repr { resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs) } - #[cfg(feature = "proto-sixlowpan")] - &Repr::Sixlowpan(_) => todo!(), // TODO(thvdveld): what do we need to do here? - &Repr::Unspecified { .. } => { panic!("source and destination IP address families do not match") } @@ -791,8 +770,6 @@ impl Repr { Repr::Ipv4(repr) => repr.buffer_len(), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.buffer_len(), - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => repr.buffer_len(), } } @@ -811,8 +788,6 @@ impl Repr { Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps), #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)), - #[cfg(feature = "proto-sixlowpan")] - Repr::Sixlowpan(repr) => repr.emit(&mut SixlowpanIphcPacket::new_unchecked(buffer)), } } diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 8c07cd5a1..27995b8c0 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -1381,7 +1381,7 @@ pub mod nhc { impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpPacket { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { - let start = 1 + self.ports_size() + self.checksum_size(); + let start = 1 + self.ports_size() + 2; // XXX(thvdveld): we assume we put the checksum inlined. &mut self.buffer.as_mut()[start..] } From d8e7b7a1f7c943f65bb5b843c85d69d5f5c86e8d Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 13 Oct 2021 11:15:54 +0200 Subject: [PATCH 242/566] ieee802154: ignore frames with types other than Data --- src/iface/interface.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a347840c7..20e99d7dc 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1102,6 +1102,10 @@ impl<'a> InterfaceInner<'a> { return Ok(None); } + if ieee802154_repr.frame_type != Ieee802154FrameType::Data { + return Ok(None); + } + match ieee802154_frame.payload() { Some(payload) => self.process_sixlowpan(cx, sockets, &ieee802154_repr, payload), None => Ok(None), From 0e3b668f098f986126e8aca509994155357f0ee5 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 19 Oct 2021 12:16:17 +0200 Subject: [PATCH 243/566] ieee802154: Correct filtering of PAN id --- src/iface/interface.rs | 13 +++++++++---- src/wire/ieee802154.rs | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 20e99d7dc..093bd6162 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1096,13 +1096,17 @@ impl<'a> InterfaceInner<'a> { let ieee802154_frame = Ieee802154Frame::new_checked(sixlowpan_payload)?; let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame)?; - if ieee802154_repr.dst_pan_id != cx.pan_id { - // We sillently drop frames that have the wrong PAN id. - // NOTE: this is most of the time already implememted in hardware. + if ieee802154_repr.frame_type != Ieee802154FrameType::Data { return Ok(None); } - if ieee802154_repr.frame_type != Ieee802154FrameType::Data { + // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this + // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. + // We always accept the broadcast PAN id. + if cx.pan_id.is_some() + && ieee802154_repr.dst_pan_id != cx.pan_id + && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) + { return Ok(None); } @@ -3684,6 +3688,7 @@ mod test { |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default(), ); + let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 90b58db1f..b52a2d9f6 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -109,6 +109,8 @@ impl fmt::Display for AddressingMode { pub struct Pan(pub u16); impl Pan { + pub const BROADCAST: Self = Self(0xffff); + /// Return the PAN ID as bytes. pub fn as_bytes(&self) -> [u8; 2] { let mut pan = [0u8; 2]; From 9fecf502ba4e9746cdc645fec9a2e44e778f3c76 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 20 Oct 2021 10:56:46 +0200 Subject: [PATCH 244/566] ieee802154: update documentation --- src/wire/sixlowpan.rs | 76 +++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 27995b8c0..3ca55abcd 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -1,3 +1,7 @@ +/// Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over +/// IEEE802.154-based networks. +/// +/// [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; @@ -5,6 +9,7 @@ use crate::Error; use crate::Result; #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum NextHeader { Compressed, Uncompressed(IpProtocol), @@ -14,6 +19,7 @@ pub enum NextHeader { /// This requires some context to convert it the an IPv6 address in some cases. /// For 802.15.4 the context are the short/extended addresses. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Address<'a> { Complete(ipv6::Address), WithContext(&'a [u8]), @@ -96,6 +102,7 @@ pub mod iphc { /// A read/write wrapper around a LOWPAN_IPHC frame buffer. #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, } @@ -132,7 +139,7 @@ pub mod iphc { self.buffer } - /// Parse the next header field. + /// Return the Next Header field of this IPHC packet. pub fn next_header(&self) -> NextHeader { let nh = self.nh_field(); @@ -150,7 +157,7 @@ pub mod iphc { } } - /// Parse the hop limit field. + /// Return the Hop Limit of this IPHC packet. pub fn hop_limit(&self) -> u8 { match self.hlim_field() { 0b00 => { @@ -168,7 +175,7 @@ pub mod iphc { } } - /// Return the source context identifier. + /// Return the Source Context Identifier of this IPHC packet. pub fn src_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); @@ -178,7 +185,7 @@ pub mod iphc { } } - /// Return the destination context identifier. + /// Return the Destination Context Identifier of this IPHC packet. pub fn dst_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); @@ -188,7 +195,7 @@ pub mod iphc { } } - /// Parse the source address field. + /// Return the Source Address of this IPHC packet. pub fn src_addr(&self) -> Result
{ let start = (self.ip_fields_start() + self.traffic_class_size() @@ -276,7 +283,7 @@ pub mod iphc { } } - /// Parse the destination address field. + /// Return the Destination Address of this IPHC packet. pub fn dst_addr(&self) -> Result
{ let start = (self.ip_fields_start() + self.traffic_class_size() @@ -540,22 +547,9 @@ pub mod iphc { raw[idx..idx + value.len()].copy_from_slice(value); } - /// Return a mutable pointer to the payload. - pub fn payload_mut(&mut self) -> &mut [u8] { - let mut len = self.ip_fields_start(); - - len += self.traffic_class_size(); - len += self.next_header_size(); - len += self.hop_limit_size(); - len += self.src_address_size(); - len += self.dst_address_size(); - - let len = len as usize; - - let data = self.buffer.as_mut(); - &mut data[len..] - } - + /// Set the Next Header of this IPHC packet. + /// + /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize { match nh { NextHeader::Uncompressed(nh) => { @@ -569,6 +563,9 @@ pub mod iphc { idx } + /// Set the Hop Limit of this IPHC packet. + /// + /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize { match hl { 255 => self.set_hlim_field(0b11), @@ -584,7 +581,9 @@ pub mod iphc { idx } - /// Set the source address based on the IPv6 address and the Link-Local address. + /// Set the Source Address based on the IPv6 address and the Link-Local address. + /// + /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_src_address( &mut self, src_addr: ipv6::Address, @@ -648,6 +647,9 @@ pub mod iphc { idx } + /// Set the Destination Address based on the IPv6 address and the Link-Local address. + /// + /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_dst_address( &mut self, dst_addr: ipv6::Address, @@ -723,10 +725,27 @@ pub mod iphc { idx } + + /// Return a mutable pointer to the payload. + pub fn payload_mut(&mut self) -> &mut [u8] { + let mut len = self.ip_fields_start(); + + len += self.traffic_class_size(); + len += self.next_header_size(); + len += self.hop_limit_size(); + len += self.src_address_size(); + len += self.dst_address_size(); + + let len = len as usize; + + let data = self.buffer.as_mut(); + &mut data[len..] + } } /// A high-level representation of a LOWPAN_IPHC header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub src_addr: ipv6::Address, pub ll_src_addr: Option, @@ -738,6 +757,9 @@ pub mod iphc { impl Repr { /// Parse a LOWPAN_IPHC packet and return a high-level representation. + /// + /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the + /// IPv6 packets. pub fn parse + ?Sized>( packet: &Packet<&T>, ll_src_addr: Option, @@ -858,7 +880,7 @@ pub mod iphc { packet.set_dispatch_field(); - // SETTING THE TRAFIX FLOW + // SETTING THE TRAFIC FLOW // TODO(thvdveld): needs more work. packet.set_tf_field(0b11); @@ -971,6 +993,7 @@ pub mod nhc { /// A read/write wrapper around a LOWPAN_NHC frame buffer. #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Packet> { ExtensionHeader(ExtensionHeaderPacket), UdpHeader(UdpPacket), @@ -998,6 +1021,7 @@ pub mod nhc { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ExtensionHeaderId { HopByHopHeader, RoutingHeader, @@ -1026,6 +1050,7 @@ pub mod nhc { /// A read/write wrapper around a LOWPAN_NHC Next Header frame buffer. #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ExtensionHeaderPacket> { buffer: T, } @@ -1177,6 +1202,7 @@ pub mod nhc { /// A high-level representation of an LOWPAN_NHC Extension Header header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ExtensionHeaderRepr { ext_header_id: ExtensionHeaderId, next_header: NextHeader, @@ -1228,6 +1254,7 @@ pub mod nhc { /// A read/write wrapper around a 6LoWPAN_NHC_UDP frame buffer. #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UdpPacket> { buffer: T, } @@ -1442,6 +1469,7 @@ pub mod nhc { /// A high-level representation of a LOWPAN_NHC UDP header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UdpNhcRepr(pub UdpRepr); impl<'a> UdpNhcRepr { From aeca423b77e76e964a6a9d14c502f35a56400f7a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 20 Oct 2021 11:26:50 +0200 Subject: [PATCH 245/566] ieee802154: log when we drop a frame --- src/iface/interface.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 093bd6162..c8993e1f7 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1107,6 +1107,10 @@ impl<'a> InterfaceInner<'a> { && ieee802154_repr.dst_pan_id != cx.pan_id && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) { + net_debug!( + "dropping {:?} because not our PAN id (or not broadcast)", + ieee802154_repr + ); return Ok(None); } From 5e7e75ab57cce7d4b7592331b9355c5ed79e3a65 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 01:31:15 +0200 Subject: [PATCH 246/566] Fix some comments. --- src/iface/interface.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index c8993e1f7..c6d7b605b 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -841,9 +841,7 @@ where socket.dispatch(cx, |response| respond!(IpPacket::Tcp(response))) } #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(ref mut socket) => - // todo don't unwrap - { + Socket::Dhcpv4(ref mut socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Dhcpv4(response))) } }; @@ -1145,7 +1143,7 @@ impl<'a> InterfaceInner<'a> { payload_len: iphc_repr.buffer_len(), }; - // Currently we assume the next header is a UDP, so we mark all the rest with todo. + // Currently we assume the next header is a UDP, so we ignore everything else. match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { match SixlowpanNhcPacket::dispatch(payload)? { From d5b2c75f084dd6d80205a9034505fd45406e9e95 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 01:38:19 +0200 Subject: [PATCH 247/566] Add medium-ieee802154 to CI. Not adding to `defmt` because it doesn't build yet. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f3ca51c8..8c160acf0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,7 +73,7 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - rand-custom-impl medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: From bc2934d62c03d56e696769579652ae9b7de9c802 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 01:42:18 +0200 Subject: [PATCH 248/566] Fix build when setting medium-ieee802154 but not proto-sixlowpan. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cab4bf334..633752d30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ verbose = [] rand-custom-impl = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] -"medium-ieee802154" = ["socket"] +"medium-ieee802154" = ["socket", "proto-sixlowpan"] "phy-raw_socket" = ["std", "libc"] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] From c1fe08a80b62bc51a265645e333d15a64114609f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 01:53:56 +0200 Subject: [PATCH 249/566] ci: do not run tests on master push bors already tests the *result* of merging PRs into master, and then pushes the *exact same commit* to master on success, so it's guaranteed to pass CI. No point in running everything again. This'll make other CI runs faster, since we have so many jobs that we're always running against the GHA limit of 10 concurrent jobs. --- .github/workflows/clippy.yml | 2 +- .github/workflows/fuzz.yml | 2 +- .github/workflows/rustfmt.yaml | 2 +- .github/workflows/test.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 5e2ec84b1..7aa762352 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,6 +1,6 @@ on: push: - branches: [ staging, trying, master ] + branches: [ staging, trying ] pull_request_target: name: Clippy check diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 98046b4c5..281c6df3c 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -1,6 +1,6 @@ on: push: - branches: [ staging, trying, master ] + branches: [ staging, trying ] pull_request: name: Fuzz diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml index 062306c39..3d065a20e 100644 --- a/.github/workflows/rustfmt.yaml +++ b/.github/workflows/rustfmt.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ staging, trying, master ] + branches: [ staging, trying ] pull_request: name: Rustfmt check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c160acf0..36e4732b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ on: push: - branches: [ staging, trying, master ] + branches: [ staging, trying ] pull_request: name: Test From 649a1858893f501c2cd8360d56cd76c0d11a1789 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 26 Oct 2021 16:16:24 +0200 Subject: [PATCH 250/566] ieee802154: remove unused CRC --- src/wire/ieee802154.rs | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index b52a2d9f6..5a066760a 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -6,45 +6,6 @@ use crate::wire::ipv6::Address as Ipv6Address; use crate::Error; use crate::Result; -const CRC_TABLE: [u16; 256] = [ - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, - 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, - 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, - 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, - 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, - 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, - 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, - 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, - 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, - 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, - 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, - 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, -]; - -pub fn calculate_crc(buffer: &[u8]) -> u16 { - fn crc_byte(crc: u16, c: u8) -> u16 { - (crc >> 8) ^ CRC_TABLE[((crc ^ (c as u16)) & 0xff) as usize] - } - - let mut crc = 0; - - for b in buffer { - crc = crc_byte(crc, *b); - } - - crc -} - enum_with_unknown! { /// IEEE 802.15.4 frame type. pub enum FrameType(u8) { From 2a8ef376865ad7f2b9be719e54ba7b15e3bb73b4 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 26 Oct 2021 16:52:41 +0200 Subject: [PATCH 251/566] ieee802154: add getters for security features --- src/wire/ieee802154.rs | 186 +++++++++++++++++++++++++++++++++++------ 1 file changed, 159 insertions(+), 27 deletions(-) diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 5a066760a..aca402e59 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -249,7 +249,8 @@ impl> Frame { /// Ensure that no accessor method will panic if called. /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { - if self.buffer.as_ref().is_empty() { + // We need at least 3 bytes + if self.buffer.as_ref().len() < 3 { Err(Error::Truncated) } else { Ok(()) @@ -437,27 +438,118 @@ impl> Frame { } } - /// Return the Auxilliary Security Header Field - #[inline] - pub fn aux_security_header(&self) -> Option<&[u8]> { - match self.frame_type() { - FrameType::Beacon - | FrameType::Data - | FrameType::MacCommand - | FrameType::Multipurpose => (), - FrameType::Acknowledgement if self.frame_version() == FrameVersion::Ieee802154 => (), - FrameType::Acknowledgement - | FrameType::Extended - | FrameType::FragmentOrFrak - | FrameType::Unknown(_) => return None, + /// Return the index where the auxiliary security header starts. + fn aux_security_header_start(&self) -> usize { + // We start with 3, because 2 bytes for frame control and the sequence number. + let mut index = 3; + index += self.addressing_fields().unwrap().len(); + index + } + + /// Return the index where the payload starts. + fn payload_start(&self) -> usize { + let mut index = self.aux_security_header_start(); + + if self.security_enabled() { + // We add 5 because 1 byte for control bits and 4 bytes for frame counter. + index += 5; + index += if let Some(len) = self.key_identifier_length() { + len as usize + } else { + 0 + }; } - if !self.security_enabled() { - return None; + index + } + + /// Return the lenght of the key identifier field. + fn key_identifier_length(&self) -> Option { + Some(match self.key_identifier_mode() { + 0 => 0, + 1 => 1, + 2 => 5, + 3 => 9, + _ => return None, + }) + } + + /// Return the security level of the auxiliary security header. + pub fn security_level(&self) -> u8 { + let index = self.aux_security_header_start(); + let b = self.buffer.as_ref()[index..][0]; + b & 0b111 + } + + /// Return the key identifier mode used by the auxiliary security header. + pub fn key_identifier_mode(&self) -> u8 { + let index = self.aux_security_header_start(); + let b = self.buffer.as_ref()[index..][0]; + (b >> 3) & 0b11 + } + + /// Return the frame counter field. + pub fn frame_counter(&self) -> u32 { + let index = self.aux_security_header_start(); + let b = &self.buffer.as_ref()[index..]; + LittleEndian::read_u32(&b[1..1 + 4]) + } + + /// Return the Key Identifier field. + fn key_identifier(&self) -> &[u8] { + let index = self.aux_security_header_start(); + let b = &self.buffer.as_ref()[index..]; + let length = if let Some(len) = self.key_identifier_length() { + len as usize + } else { + 0 + }; + &b[5..][..length] + } + + /// Return the Key Source field. + pub fn key_source(&self) -> Option<&[u8]> { + let ki = self.key_identifier(); + let len = ki.len(); + if len > 1 { + Some(&ki[..len - 1]) + } else { + None + } + } + + /// Return the Key Index field. + pub fn key_index(&self) -> Option { + let ki = self.key_identifier(); + let len = ki.len(); + + if len > 0 { + Some(ki[len - 1]) + } else { + None } + } - net_debug!("Auxilliary security header is currently not supported."); - None + /// Return the Message Integrity Code (MIC). + pub fn message_integrity_code(&self) -> Option<&[u8]> { + let mic_len = match self.security_level() { + 0 | 4 => return None, + 1 | 5 => 4, + 2 | 6 => 8, + 3 | 7 => 16, + _ => panic!(), + }; + + let data = &self.buffer.as_ref(); + let len = data.len(); + + Some(&data[len - mic_len..]) + } + + /// Return the MAC header. + pub fn mac_header(&self) -> &[u8] { + let data = &self.buffer.as_ref(); + &data[..self.payload_start()] } } @@ -467,10 +559,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> { pub fn payload(&self) -> Option<&'a [u8]> { match self.frame_type() { FrameType::Data => { - let data = &self.buffer.as_ref()[field::ADDRESSING]; - let offset = self.addressing_fields().unwrap().len(); + let index = self.payload_start(); + let data = &self.buffer.as_ref(); - Some(&data[offset..]) + Some(&data[index..]) } _ => None, } @@ -613,12 +705,9 @@ impl + AsMut<[u8]>> Frame { pub fn payload_mut(&mut self) -> Option<&mut [u8]> { match self.frame_type() { FrameType::Data => { - let mut start_offset = 3; - start_offset += self.addressing_fields().unwrap().len(); - + let index = self.payload_start(); let data = self.buffer.as_mut(); - let end_offset = start_offset + data.len() - 2; - Some(&mut data[start_offset..end_offset]) + Some(&mut data[index..]) } _ => None, } @@ -853,6 +942,49 @@ mod test { dst_addressing_mode -> AddressingMode::Short, frame_version -> FrameVersion::Ieee802154_2006, src_addressing_mode -> AddressingMode::Extended, - //payload -> Some(&[0x2b, 0x00, 0x00, 0x00]), + payload -> Some(&[0x2b, 0x00, 0x00, 0x00][..]), + } + + vector_test! { + security + [ + 0x69,0xdc, // frame control + 0x32, // sequence number + 0xcd,0xab, // destination PAN id + 0xbf,0x9b,0x15,0x06,0x00,0x4b,0x12,0x00, // extended destination address + 0xc7,0xd9,0xb5,0x14,0x00,0x4b,0x12,0x00, // extended source address + 0x05, // security control field + 0x31,0x01,0x00,0x00, // frame counter + 0x3e,0xe8,0xfb,0x85,0xe4,0xcc,0xf4,0x48,0x90,0xfe,0x56,0x66,0xf7,0x1c,0x65,0x9e,0xf9, // data + 0x93,0xc8,0x34,0x2e,// MIC + ]; + frame_type -> FrameType::Data, + security_enabled -> true, + frame_pending -> false, + ack_request -> true, + pan_id_compression -> true, + dst_addressing_mode -> AddressingMode::Extended, + frame_version -> FrameVersion::Ieee802154_2006, + src_addressing_mode -> AddressingMode::Extended, + dst_pan_id -> Some(Pan(0xabcd)), + dst_addr -> Some(Address::Extended([0x00,0x12,0x4b,0x00,0x06,0x15,0x9b,0xbf])), + src_pan_id -> None, + src_addr -> Some(Address::Extended([0x00,0x12,0x4b,0x00,0x14,0xb5,0xd9,0xc7])), + security_level -> 5, + key_identifier_mode -> 0, + frame_counter -> 305, + key_source -> None, + key_index -> None, + payload -> Some(&[0x3e,0xe8,0xfb,0x85,0xe4,0xcc,0xf4,0x48,0x90,0xfe,0x56,0x66,0xf7,0x1c,0x65,0x9e,0xf9,0x93,0xc8,0x34,0x2e][..]), + message_integrity_code -> Some(&[0x93, 0xC8, 0x34, 0x2E][..]), + mac_header -> &[ + 0x69,0xdc, // frame control + 0x32, // sequence number + 0xcd,0xab, // destination PAN id + 0xbf,0x9b,0x15,0x06,0x00,0x4b,0x12,0x00, // extended destination address + 0xc7,0xd9,0xb5,0x14,0x00,0x4b,0x12,0x00, // extended source address + 0x05, // security control field + 0x31,0x01,0x00,0x00, // frame counter + ][..], } } From bd1d6da03e2c9c4f8e776071fcd35c936c605273 Mon Sep 17 00:00:00 2001 From: luojia65 Date: Wed, 27 Oct 2021 16:26:57 +0800 Subject: [PATCH 252/566] Add lint `#[must_use]` for ring buffer functions. This lint is in stable Rust now: https://github.com/rust-lang/rust/issues/43302. These changes are noted according to code comment from @whitequark. Some test cases and functions are altered due to changes of #[must_use]. --- src/socket/tcp.rs | 3 ++- src/storage/packet_buffer.rs | 7 +++++-- src/storage/ring_buffer.rs | 38 +++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index e2d404da3..b8fbcddba 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1875,8 +1875,9 @@ impl<'a> TcpSocket<'a> { payload_len, payload_offset ); - self.rx_buffer + let len_written = self.rx_buffer .write_unallocated(payload_offset, repr.payload); + debug_assert!(len_written == payload_len); } Err(_) => { net_debug!( diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 6ce6d17db..db318bbaa 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -102,7 +102,9 @@ impl<'a, H> PacketBuffer<'a, H> { // Add padding to the end of the ring buffer so that the // contiguous window is at the beginning of the ring buffer. *self.metadata_ring.enqueue_one()? = PacketMetadata::padding(contig_window); - self.payload_ring.enqueue_many(contig_window); + // note(discard): function does not write to the result + // enqueued padding buffer location + let _buf_enqueued = self.payload_ring.enqueue_many(contig_window); } } @@ -121,7 +123,8 @@ impl<'a, H> PacketBuffer<'a, H> { let _ = metadata_ring.dequeue_one_with(|metadata| { if metadata.is_padding() { - payload_ring.dequeue_many(metadata.size); + // note(discard): function does not use value of dequeued padding bytes + let _buf_dequeued = payload_ring.dequeue_many(metadata.size); Ok(()) // dequeue metadata } else { Err(Error::Exhausted) // don't dequeue metadata diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index e802ad122..67dcf6a0b 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -1,4 +1,5 @@ -// Uncomment the #[must_use]s here once [RFC 1940] hits stable. +// Some of the functions in ring buffer is marked as #[must_use]. It notes that +// these functions may have side effects, and it's implemented by [RFC 1940]. // [RFC 1940]: https://github.com/rust-lang/rust/issues/43302 use core::cmp; @@ -202,7 +203,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// /// This function may return a slice smaller than the given size /// if the free space in the buffer is not contiguous. - // #[must_use] + #[must_use] pub fn enqueue_many(&mut self, size: usize) -> &mut [T] { self.enqueue_many_with(|buf| { let size = cmp::min(size, buf.len()); @@ -213,7 +214,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Enqueue as many elements from the given slice into the buffer as possible, /// and return the amount of elements that could fit. - // #[must_use] + #[must_use] pub fn enqueue_slice(&mut self, data: &[T]) -> usize where T: Copy, @@ -259,7 +260,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// /// This function may return a slice smaller than the given size /// if the allocated space in the buffer is not contiguous. - // #[must_use] + #[must_use] pub fn dequeue_many(&mut self, size: usize) -> &mut [T] { self.dequeue_many_with(|buf| { let size = cmp::min(size, buf.len()); @@ -270,7 +271,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Dequeue as many elements from the buffer into the given slice as possible, /// and return the amount of elements that could fit. - // #[must_use] + #[must_use] pub fn dequeue_slice(&mut self, data: &mut [T]) -> usize where T: Copy, @@ -294,7 +295,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { impl<'a, T: 'a> RingBuffer<'a, T> { /// Return the largest contiguous slice of unallocated buffer elements starting /// at the given offset past the last allocated element, and up to the given size. - // #[must_use] + #[must_use] pub fn get_unallocated(&mut self, offset: usize, mut size: usize) -> &mut [T] { let start_at = self.get_idx(self.length + offset); // We can't access past the end of unallocated data. @@ -318,7 +319,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Write as many elements from the given slice into unallocated buffer elements /// starting at the given offset past the last allocated element, and return /// the amount written. - // #[must_use] + #[must_use] pub fn write_unallocated(&mut self, offset: usize, data: &[T]) -> usize where T: Copy, @@ -349,7 +350,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Return the largest contiguous slice of allocated buffer elements starting /// at the given offset past the first allocated element, and up to the given size. - // #[must_use] + #[must_use] pub fn get_allocated(&self, offset: usize, mut size: usize) -> &[T] { let start_at = self.get_idx(offset); // We can't read past the end of the allocated data. @@ -373,7 +374,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// Read as many elements from allocated buffer elements into the given slice /// starting at the given offset past the first allocated element, and return /// the amount read. - // #[must_use] + #[must_use] pub fn read_allocated(&mut self, offset: usize, data: &mut [T]) -> usize where T: Copy, @@ -686,7 +687,8 @@ mod test { } assert_eq!(&ring.storage[..], b"abcd........"); - ring.enqueue_many(4); + let buf_enqueued = ring.enqueue_many(4); + assert_eq!(buf_enqueued.len(), 4); assert_eq!(ring.len(), 4); { @@ -730,15 +732,18 @@ mod test { assert_eq!(ring.get_allocated(16, 4), b""); assert_eq!(ring.get_allocated(0, 4), b""); - ring.enqueue_slice(b"abcd"); + let len_enqueued = ring.enqueue_slice(b"abcd"); assert_eq!(ring.get_allocated(0, 8), b"abcd"); + assert_eq!(len_enqueued, 4); - ring.enqueue_slice(b"efghijkl"); + let len_enqueued = ring.enqueue_slice(b"efghijkl"); ring.dequeue_many(4).copy_from_slice(b"...."); assert_eq!(ring.get_allocated(4, 8), b"ijkl"); + assert_eq!(len_enqueued, 8); - ring.enqueue_slice(b"abcd"); + let len_enqueued = ring.enqueue_slice(b"abcd"); assert_eq!(ring.get_allocated(4, 8), b"ijkl"); + assert_eq!(len_enqueued, 4); } #[test] @@ -782,10 +787,11 @@ mod test { #[test] fn test_buffer_write_wholly() { let mut ring = RingBuffer::new(vec![b'.'; 8]); - ring.enqueue_many(2).copy_from_slice(b"xx"); - ring.enqueue_many(2).copy_from_slice(b"xx"); + ring.enqueue_many(2).copy_from_slice(b"ab"); + ring.enqueue_many(2).copy_from_slice(b"cd"); assert_eq!(ring.len(), 4); - ring.dequeue_many(4); + let buf_dequeued = ring.dequeue_many(4); + assert_eq!(buf_dequeued, b"abcd"); assert_eq!(ring.len(), 0); let large = ring.enqueue_many(8); From 7a0df7da91908ba88bcd7b9c0f62829da111c25e Mon Sep 17 00:00:00 2001 From: luojia65 Date: Wed, 27 Oct 2021 16:35:05 +0800 Subject: [PATCH 253/566] Code format using `cargo fmt` --- src/socket/tcp.rs | 3 ++- src/storage/packet_buffer.rs | 2 +- src/storage/ring_buffer.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index b8fbcddba..064fd5f21 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1875,7 +1875,8 @@ impl<'a> TcpSocket<'a> { payload_len, payload_offset ); - let len_written = self.rx_buffer + let len_written = self + .rx_buffer .write_unallocated(payload_offset, repr.payload); debug_assert!(len_written == payload_len); } diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index db318bbaa..4a5080bdf 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -102,7 +102,7 @@ impl<'a, H> PacketBuffer<'a, H> { // Add padding to the end of the ring buffer so that the // contiguous window is at the beginning of the ring buffer. *self.metadata_ring.enqueue_one()? = PacketMetadata::padding(contig_window); - // note(discard): function does not write to the result + // note(discard): function does not write to the result // enqueued padding buffer location let _buf_enqueued = self.payload_ring.enqueue_many(contig_window); } diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index 67dcf6a0b..f54837b34 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -1,4 +1,4 @@ -// Some of the functions in ring buffer is marked as #[must_use]. It notes that +// Some of the functions in ring buffer is marked as #[must_use]. It notes that // these functions may have side effects, and it's implemented by [RFC 1940]. // [RFC 1940]: https://github.com/rust-lang/rust/issues/43302 From 858968bb26c3c3d8c5c36402fdbd86a5bb9cff87 Mon Sep 17 00:00:00 2001 From: Alexandra Sandulescu Date: Thu, 28 Oct 2021 10:37:57 +0200 Subject: [PATCH 254/566] fuzz: DHCP header parser --- fuzz/Cargo.toml | 6 ++++++ fuzz/fuzz_targets/dhcp_header.rs | 19 +++++++++++++++++++ src/wire/dhcpv4.rs | 6 +++++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 fuzz/fuzz_targets/dhcp_header.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 622bc773d..0db822f8d 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -28,3 +28,9 @@ name = "tcp_headers" path = "fuzz_targets/tcp_headers.rs" test = false doc = false + +[[bin]] +name = "dhcp_header" +path = "fuzz_targets/dhcp_header.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/dhcp_header.rs b/fuzz/fuzz_targets/dhcp_header.rs new file mode 100644 index 000000000..ea2c64dce --- /dev/null +++ b/fuzz/fuzz_targets/dhcp_header.rs @@ -0,0 +1,19 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{DhcpPacket, DhcpRepr}; + +fuzz_target!(|data: &[u8]| { + let _ = match DhcpPacket::new_checked(data) { + Ok(ref packet) => match DhcpRepr::parse(packet) { + Ok(dhcp_repr) => { + let mut dhcp_payload = vec![0; dhcp_repr.buffer_len()]; + match DhcpPacket::new_checked(&mut dhcp_payload[..]) { + Ok(mut dhcp_packet) => Some(dhcp_repr.emit(&mut dhcp_packet)), + Err(_) => None, + } + } + Err(_) => None, + }, + Err(_) => None, + }; +}); diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index bddf1448c..1982ab9b9 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -824,7 +824,11 @@ impl<'a> Repr<'a> { data, } => { let mut servers = [None; MAX_DNS_SERVER_COUNT]; - for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) { + let chunk_size = 4; + for (server, chunk) in servers.iter_mut().zip(data.chunks(chunk_size)) { + if chunk.len() != chunk_size { + return Err(Error::Malformed); + } *server = Some(Ipv4Address::from_bytes(chunk)); } dns_servers = Some(servers); From 275b8aca4390c4e27a4ef8bf493819d6a049f386 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sat, 30 Oct 2021 23:51:23 +0800 Subject: [PATCH 255/566] show log in testing --- src/macros.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index b69af365b..7e324a571 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,9 +1,17 @@ +#[cfg(not(test))] #[cfg(feature = "log")] macro_rules! net_log { (trace, $($arg:expr),*) => { log::trace!($($arg),*) }; (debug, $($arg:expr),*) => { log::debug!($($arg),*) }; } +#[cfg(test)] +#[cfg(feature = "log")] +macro_rules! net_log { + (trace, $($arg:expr),*) => { println!($($arg),*) }; + (debug, $($arg:expr),*) => { println!($($arg),*) }; +} + #[cfg(feature = "defmt")] macro_rules! net_log { (trace, $($arg:expr),*) => { defmt::trace!($($arg),*) }; From a666a7a8bee1a6f0f80bcea6ae2be4a5bf1f523a Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sun, 31 Oct 2021 11:42:18 +0800 Subject: [PATCH 256/566] arp: flush neighbor cache after IP update Related to #543 --- src/iface/interface.rs | 77 ++++++++++++++++++++++++++++++++++++++++++ src/iface/neighbor.rs | 27 +++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index c6d7b605b..8e58721b3 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -613,6 +613,7 @@ where /// This function panics if any of the addresses are not unicast. pub fn update_ip_addrs)>(&mut self, f: F) { f(&mut self.inner.ip_addrs); + InterfaceInner::flush_cache(&mut self.inner); InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) } @@ -2225,6 +2226,12 @@ impl<'a> InterfaceInner<'a> { Err(Error::Unaddressable) } + fn flush_cache(&mut self) { + if let Some(cache) = self.neighbor_cache.as_mut() { + cache.flush() + } + } + fn dispatch_ip( &mut self, cx: &Context, @@ -3313,6 +3320,76 @@ mod test { ); } + #[test] + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] + fn test_arp_flush_after_update_ip() { + let (mut iface, mut socket_set) = create_loopback_ethernet(); + + let mut eth_bytes = vec![0u8; 42]; + + let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + { + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + 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(&cx, &mut socket_set, frame.into_inner()), + Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: local_hw_addr, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + }))) + ); + + // Ensure the address of the requestor was entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + &cx, + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr) + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); + + // Update IP addrs to trigger ARP cache flush + let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); + }); + }); + + // ARP cache flush after address change + assert!(!iface + .inner + .has_neighbor(&cx, &IpAddress::Ipv4(remote_ip_addr))); + } + #[test] #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] fn test_icmpv4_socket() { diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 04f68623c..2b3a1f918 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -218,6 +218,10 @@ impl<'a> Cache<'a> { pub(crate) fn limit_rate(&mut self, timestamp: Instant) { self.silent_until = timestamp + Self::SILENT_TIME; } + + pub(crate) fn flush(&mut self) { + self.storage.clear() + } } #[cfg(test)] @@ -370,4 +374,27 @@ mod test { Answer::NotFound ); } + + #[test] + fn test_flush() { + let mut cache_storage = [Default::default(); 3]; + let mut cache = Cache::new(&mut cache_storage[..]); + + cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); + assert_eq!( + cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), + Answer::Found(HADDR_A) + ); + assert!(!cache + .lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)) + .found()); + + cache.flush(); + assert!(!cache + .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) + .found()); + assert!(!cache + .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) + .found()); + } } From e0ace4b8eeae60fdc91a383d4d2df6c53ac4c830 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Nov 2021 23:00:26 +0100 Subject: [PATCH 257/566] Remove manual `log::set_logger` from tests. No longer needed since `net_debug!` and `net_trace!` now call `println!` directly when testing! --- src/socket/dhcpv4.rs | 27 --------------------------- src/socket/tcp.rs | 30 ------------------------------ 2 files changed, 57 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 4906ebb95..c60fa8993 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -651,30 +651,6 @@ mod test { }); } - #[cfg(feature = "log")] - fn init_logger() { - struct Logger; - static LOGGER: Logger = Logger; - - impl log::Log for Logger { - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true - } - - fn log(&self, record: &log::Record) { - println!("{}", record.args()); - } - - fn flush(&self) {} - } - - // If it fails, that just means we've already set it to the same value. - let _ = log::set_logger(&LOGGER); - log::set_max_level(log::LevelFilter::Trace); - - println!(); - } - // =========================================================================================// // Constants @@ -822,9 +798,6 @@ mod test { // Tests fn socket() -> Dhcpv4Socket { - #[cfg(feature = "log")] - init_logger(); - let mut s = Dhcpv4Socket::new(); assert_eq!(s.poll(), Some(Event::Deconfigured)); s diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index e2d404da3..f8da8f8a5 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2648,38 +2648,11 @@ mod test { }}; } - #[cfg(feature = "log")] - fn init_logger() { - struct Logger; - static LOGGER: Logger = Logger; - - impl log::Log for Logger { - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true - } - - fn log(&self, record: &log::Record) { - println!("{}", record.args()); - } - - fn flush(&self) {} - } - - // If it fails, that just means we've already set it to the same value. - let _ = log::set_logger(&LOGGER); - log::set_max_level(log::LevelFilter::Trace); - - println!(); - } - fn socket() -> TcpSocket<'static> { socket_with_buffer_sizes(64, 64) } fn socket_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { - #[cfg(feature = "log")] - init_logger(); - let rx_buffer = SocketBuffer::new(vec![0; rx_len]); let tx_buffer = SocketBuffer::new(vec![0; tx_len]); let mut socket = TcpSocket::new(rx_buffer, tx_buffer); @@ -7048,9 +7021,6 @@ mod test { #[test] fn test_rtt_estimator() { - #[cfg(feature = "log")] - init_logger(); - let mut r = RttEstimator::default(); let rtos = &[ From a70c4831e7863868869ee277a431fc02d94cb1ee Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 03:14:51 +0200 Subject: [PATCH 258/566] socket/dhcpv4: return owned Config in Event. --- src/socket/dhcpv4.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index c60fa8993..c166397a7 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -29,7 +29,7 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[ ]; /// IPv4 configuration data provided by the DHCP server. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { /// IP address @@ -103,11 +103,11 @@ enum ClientState { /// Return value for the `Dhcpv4Socket::poll` function #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Event<'a> { +pub enum Event { /// Configuration has been lost (for example, the lease has expired) Deconfigured, /// Configuration has been newly acquired, or modified. - Configured(&'a Config), + Configured(Config), } #[derive(Debug)] @@ -544,12 +544,12 @@ impl Dhcpv4Socket { /// /// The socket has an internal "configuration changed" flag. If /// set, this function returns the configuration and resets the flag. - pub fn poll(&mut self) -> Option> { + pub fn poll(&mut self) -> Option { if !self.config_changed { None } else if let ClientState::Renewing(state) = &self.state { self.config_changed = false; - Some(Event::Configured(&state.config)) + Some(Event::Configured(state.config)) } else { self.config_changed = false; Some(Event::Deconfigured) @@ -836,7 +836,7 @@ mod test { assert_eq!( s.poll(), - Some(Event::Configured(&Config { + Some(Event::Configured(Config { address: Ipv4Cidr::new(MY_IP, 24), dns_servers: DNS_IPS, router: Some(SERVER_IP), From bde881d2f956b37e4bfe278290809f6008d2d628 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 03:19:22 +0200 Subject: [PATCH 259/566] iface: own the SocketSet instead of borrowing it --- examples/benchmark.rs | 16 ++-- examples/client.rs | 15 ++- examples/dhcp_client.rs | 14 +-- examples/httpclient.rs | 13 ++- examples/loopback.rs | 19 ++-- examples/multicast.rs | 19 ++-- examples/ping.rs | 13 ++- examples/server.rs | 28 +++--- examples/sixlowpan.rs | 12 +-- src/iface/interface.rs | 201 +++++++++++++++++++++++----------------- 10 files changed, 185 insertions(+), 165 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 72d0dc775..a07b9a608 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -13,7 +13,6 @@ use std::thread; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -97,7 +96,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) @@ -105,15 +104,14 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let tcp1_handle = sockets.add(tcp1_socket); - let tcp2_handle = sockets.add(tcp2_socket); + let tcp1_handle = iface.add_socket(tcp1_socket); + let tcp2_handle = iface.add_socket(tcp2_socket); let default_timeout = Some(Duration::from_millis(1000)); let mut processed = 0; while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -122,7 +120,7 @@ fn main() { // tcp:1234: emit data { - let mut socket = sockets.get::(tcp1_handle); + let mut socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -142,7 +140,7 @@ fn main() { // tcp:1235: sink data { - let mut socket = sockets.get::(tcp2_handle); + let mut socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } @@ -160,7 +158,7 @@ fn main() { } } - match iface.poll_at(&sockets, timestamp) { + match iface.poll_at(timestamp) { Some(poll_at) if timestamp < poll_at => { phy_wait(fd, Some(poll_at - timestamp)).expect("wait error"); } diff --git a/examples/client.rs b/examples/client.rs index c07e2f4ee..5cfa50150 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -7,7 +7,7 @@ use std::str::{self, FromStr}; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; @@ -42,7 +42,7 @@ fn main() { routes.add_default_ipv4_route(default_v4_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -52,18 +52,17 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let tcp_handle = sockets.add(tcp_socket); + let tcp_handle = iface.add_socket(tcp_socket); { - let mut socket = sockets.get::(tcp_handle); + let mut socket = iface.get_socket::(tcp_handle); socket.connect((address, port), 49500).unwrap(); } let mut tcp_active = false; loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -71,7 +70,7 @@ fn main() { } { - let mut socket = sockets.get::(tcp_handle); + let mut socket = iface.get_socket::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { @@ -109,6 +108,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 663fd46f7..456668e37 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -6,7 +6,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes}; -use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketSet}; +use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket}; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; use smoltcp::{ @@ -34,7 +34,7 @@ fn main() { let routes = Routes::new(&mut routes_storage[..]); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -44,7 +44,6 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); let mut dhcp_socket = Dhcpv4Socket::new(); // Set a ridiculously short max lease time to show DHCP renews work properly. @@ -53,15 +52,16 @@ fn main() { // IMPORTANT: This should be removed in production. dhcp_socket.set_max_lease_duration(Some(Duration::from_secs(10))); - let dhcp_handle = sockets.add(dhcp_socket); + let dhcp_handle = iface.add_socket(dhcp_socket); loop { let timestamp = Instant::now(); - if let Err(e) = iface.poll(&mut sockets, timestamp) { + if let Err(e) = iface.poll(timestamp) { debug!("poll error: {}", e); } - match sockets.get::(dhcp_handle).poll() { + let event = iface.get_socket::(dhcp_handle).poll(); + match event { None => {} Some(Dhcpv4Event::Configured(config)) => { debug!("DHCP config acquired!"); @@ -90,7 +90,7 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/examples/httpclient.rs b/examples/httpclient.rs index c23680030..73f08c3f2 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -8,7 +8,7 @@ use url::Url; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; @@ -48,7 +48,7 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -58,8 +58,7 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let tcp_handle = sockets.add(tcp_socket); + let tcp_handle = iface.add_socket(tcp_socket); enum State { Connect, @@ -70,7 +69,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -78,7 +77,7 @@ fn main() { } { - let mut socket = sockets.get::(tcp_handle); + let mut socket = iface.get_socket::(tcp_handle); state = match state { State::Connect if !socket.is_active() => { @@ -118,6 +117,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/examples/loopback.rs b/examples/loopback.rs index e8df98899..d08f1d405 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -11,7 +11,7 @@ use log::{debug, error, info}; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{Loopback, Medium}; -use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -94,7 +94,8 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = InterfaceBuilder::new(device) + let mut sockets: [_; 2] = Default::default(); + let mut iface = InterfaceBuilder::new(device, &mut sockets[..]) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) @@ -120,16 +121,14 @@ fn main() { TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) }; - let mut socket_set_entries: [_; 2] = Default::default(); - let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); - let server_handle = socket_set.add(server_socket); - let client_handle = socket_set.add(client_socket); + let server_handle = iface.add_socket(server_socket); + let client_handle = iface.add_socket(client_socket); let mut did_listen = false; let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(10_000) { - match iface.poll(&mut socket_set, clock.elapsed()) { + match iface.poll(clock.elapsed()) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -137,7 +136,7 @@ fn main() { } { - let mut socket = socket_set.get::(server_handle); + let mut socket = iface.get_socket::(server_handle); if !socket.is_active() && !socket.is_listening() { if !did_listen { debug!("listening"); @@ -157,7 +156,7 @@ fn main() { } { - let mut socket = socket_set.get::(client_handle); + let mut socket = iface.get_socket::(client_handle); if !socket.is_open() { if !did_connect { debug!("connecting"); @@ -178,7 +177,7 @@ fn main() { } } - match iface.poll_delay(&socket_set, clock.elapsed()) { + match iface.poll_delay(clock.elapsed()) { Some(Duration::ZERO) => debug!("resuming"), Some(delay) => { debug!("sleeping for {} ms", delay); diff --git a/examples/multicast.rs b/examples/multicast.rs index ba782a5fa..9bc90cd43 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -7,8 +7,7 @@ use std::os::unix::io::AsRawFd; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::wait as phy_wait; use smoltcp::socket::{ - RawPacketMetadata, RawSocket, RawSocketBuffer, SocketSet, UdpPacketMetadata, UdpSocket, - UdpSocketBuffer, + RawPacketMetadata, RawSocket, RawSocketBuffer, UdpPacketMetadata, UdpSocket, UdpSocketBuffer, }; use smoltcp::time::Instant; use smoltcp::wire::{ @@ -37,7 +36,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; - let mut iface = InterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device, vec![]) .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) @@ -50,8 +49,6 @@ fn main() { .join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now) .unwrap(); - let mut sockets = SocketSet::new(vec![]); - // Must fit at least one IGMP packet let raw_rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; 2], vec![0; 512]); // Will not send IGMP @@ -62,18 +59,18 @@ fn main() { raw_rx_buffer, raw_tx_buffer, ); - let raw_handle = sockets.add(raw_socket); + let raw_handle = iface.add_socket(raw_socket); // Must fit mDNS payload of at least one packet let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 4], vec![0; 1024]); // Will not send mDNS let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 0]); let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); - let udp_handle = sockets.add(udp_socket); + let udp_handle = iface.add_socket(udp_socket); loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -81,7 +78,7 @@ fn main() { } { - let mut socket = sockets.get::(raw_handle); + let mut socket = iface.get_socket::(raw_handle); if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets @@ -96,7 +93,7 @@ fn main() { } } { - let mut socket = sockets.get::(udp_handle); + let mut socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(MDNS_PORT).unwrap() } @@ -111,6 +108,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/examples/ping.rs b/examples/ping.rs index 530b72f75..4b14c1444 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::wait as phy_wait; use smoltcp::phy::Device; -use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketSet}; +use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; use smoltcp::wire::{ EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr, Ipv4Address, Ipv6Address, @@ -127,7 +127,7 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -137,8 +137,7 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let icmp_handle = sockets.add(icmp_socket); + let icmp_handle = iface.add_socket(icmp_socket); let mut send_at = Instant::from_millis(0); let mut seq_no = 0; @@ -149,7 +148,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -158,7 +157,7 @@ fn main() { { let timestamp = Instant::now(); - let mut socket = sockets.get::(icmp_handle); + let mut socket = iface.get_socket::(icmp_handle); if !socket.is_open() { socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); send_at = timestamp; @@ -261,7 +260,7 @@ fn main() { } let timestamp = Instant::now(); - match iface.poll_at(&sockets, timestamp) { + match iface.poll_at(timestamp) { Some(poll_at) if timestamp < poll_at => { let resume_at = cmp::min(poll_at, send_at); phy_wait(fd, Some(resume_at - timestamp)).expect("wait error"); diff --git a/examples/server.rs b/examples/server.rs index be13f5c34..7e1c32a70 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -8,7 +8,6 @@ use std::str; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -56,7 +55,7 @@ fn main() { ]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) @@ -64,17 +63,16 @@ fn main() { } let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let udp_handle = sockets.add(udp_socket); - let tcp1_handle = sockets.add(tcp1_socket); - let tcp2_handle = sockets.add(tcp2_socket); - let tcp3_handle = sockets.add(tcp3_socket); - let tcp4_handle = sockets.add(tcp4_socket); + let udp_handle = iface.add_socket(udp_socket); + let tcp1_handle = iface.add_socket(tcp1_socket); + let tcp2_handle = iface.add_socket(tcp2_socket); + let tcp3_handle = iface.add_socket(tcp3_socket); + let tcp4_handle = iface.add_socket(tcp4_socket); let mut tcp_6970_active = false; loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -83,7 +81,7 @@ fn main() { // udp:6969: respond "hello" { - let mut socket = sockets.get::(udp_handle); + let mut socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -111,7 +109,7 @@ fn main() { // tcp:6969: respond "hello" { - let mut socket = sockets.get::(tcp1_handle); + let mut socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(6969).unwrap(); } @@ -126,7 +124,7 @@ fn main() { // tcp:6970: echo with reverse { - let mut socket = sockets.get::(tcp2_handle); + let mut socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(6970).unwrap() } @@ -170,7 +168,7 @@ fn main() { // tcp:6971: sinkhole { - let mut socket = sockets.get::(tcp3_handle); + let mut socket = iface.get_socket::(tcp3_handle); if !socket.is_open() { socket.listen(6971).unwrap(); socket.set_keep_alive(Some(Duration::from_millis(1000))); @@ -193,7 +191,7 @@ fn main() { // tcp:6972: fountain { - let mut socket = sockets.get::(tcp4_handle); + let mut socket = iface.get_socket::(tcp4_handle); if !socket.is_open() { socket.listen(6972).unwrap() } @@ -213,6 +211,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 138c22c4b..477391438 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -49,7 +49,6 @@ use std::str; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; -use smoltcp::socket::SocketSet; use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -81,7 +80,7 @@ fn main() { 64, )]; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder @@ -89,12 +88,11 @@ fn main() { .neighbor_cache(neighbor_cache); let mut iface = builder.finalize(); - let mut sockets = SocketSet::new(vec![]); - let udp_handle = sockets.add(udp_socket); + let udp_handle = iface.add_socket(udp_socket); loop { let timestamp = Instant::now(); - match iface.poll(&mut sockets, timestamp) { + match iface.poll(timestamp) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -103,7 +101,7 @@ fn main() { // udp:6969: respond "hello" { - let mut socket = sockets.get::(udp_handle); + let mut socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -129,6 +127,6 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 8e58721b3..7469e9103 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -21,6 +21,7 @@ use crate::{Error, Result}; /// a `&mut [T]`, or `Vec` if a heap is available. pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, + sockets: SocketSet<'a>, inner: InterfaceInner<'a>, } @@ -63,6 +64,7 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { #[cfg(feature = "medium-ieee802154")] pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, + sockets: SocketSet<'a>, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes<'a>, @@ -96,7 +98,7 @@ let neighbor_cache = // ... # NeighborCache::new(BTreeMap::new()); let ip_addrs = // ... # []; -let iface = InterfaceBuilder::new(device) +let iface = InterfaceBuilder::new(device, vec![]) .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) @@ -104,9 +106,14 @@ let iface = InterfaceBuilder::new(device) ``` "## )] - pub fn new(device: DeviceT) -> Self { + pub fn new(device: DeviceT, sockets: SocketsT) -> Self + where + SocketsT: Into>>>, + { InterfaceBuilder { device: device, + sockets: SocketSet::new(sockets), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: None, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -285,6 +292,7 @@ let iface = InterfaceBuilder::new(device) Interface { device: self.device, + sockets: self.sockets, inner: InterfaceInner { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr, @@ -461,6 +469,34 @@ impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d>, { + /// Add a socket to the interface with the reference count 1, and return its handle. + /// + /// # Panics + /// This function panics if the storage is fixed-size (not a `Vec`) and is full. + pub fn add_socket(&mut self, socket: T) -> SocketHandle + where + T: Into>, + { + self.sockets.add(socket) + } + + /// Get a socket from the interface by its handle, as mutable. + /// + /// # Panics + /// This function may panic if the handle does not belong to this socket set + /// or the socket has the wrong type. + pub fn get_socket>(&mut self, handle: SocketHandle) -> SocketRef { + self.sockets.get(handle) + } + + /// Remove a socket from the set, without changing its state. + /// + /// # Panics + /// This function may panic if the handle does not belong to this socket set. + pub fn remove_socket(&mut self, handle: SocketHandle) -> Socket<'a> { + self.sockets.remove(handle) + } + /// Get the HardwareAddress address of the interface. /// /// # Panics @@ -653,13 +689,13 @@ where /// packets containing any unsupported protocol, option, or form, which is /// 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 { + pub fn poll(&mut self, timestamp: Instant) -> Result { let cx = self.context(timestamp); let mut readiness_may_have_changed = false; loop { - let processed_any = self.socket_ingress(&cx, sockets); - let emitted_any = self.socket_egress(&cx, sockets)?; + let processed_any = self.socket_ingress(&cx); + let emitted_any = self.socket_egress(&cx)?; #[cfg(feature = "proto-igmp")] self.igmp_egress(&cx, timestamp)?; @@ -681,10 +717,10 @@ where /// /// [poll]: #method.poll /// [Instant]: struct.Instant.html - pub fn poll_at(&self, sockets: &SocketSet, timestamp: Instant) -> Option { + pub fn poll_at(&self, timestamp: Instant) -> Option { let cx = self.context(timestamp); - sockets + self.sockets .iter() .filter_map(|socket| { let socket_poll_at = socket.poll_at(&cx); @@ -707,19 +743,20 @@ where /// /// [poll]: #method.poll /// [Duration]: struct.Duration.html - pub fn poll_delay(&self, sockets: &SocketSet, timestamp: Instant) -> Option { - match self.poll_at(sockets, timestamp) { + pub fn poll_delay(&self, timestamp: Instant) -> Option { + match self.poll_at(timestamp) { Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), Some(_) => Some(Duration::from_millis(0)), _ => None, } } - fn socket_ingress(&mut self, cx: &Context, sockets: &mut SocketSet) -> bool { + fn socket_ingress(&mut self, cx: &Context) -> bool { let mut processed_any = false; - let &mut Self { - ref mut device, - ref mut inner, + let Self { + device, + inner, + sockets, } = self; while let Some((rx_token, tx_token)) = device.receive() { if let Err(err) = rx_token.consume(cx.now, |frame| match cx.caps.medium { @@ -784,24 +821,25 @@ where processed_any } - fn socket_egress(&mut self, cx: &Context, sockets: &mut SocketSet) -> Result { - let _caps = self.device.capabilities(); + fn socket_egress(&mut self, cx: &Context) -> Result { + let Self { + device, + inner, + sockets, + } = self; + let _caps = device.capabilities(); let mut emitted_any = false; for mut socket in sockets.iter_mut() { if !socket .meta_mut() - .egress_permitted(cx.now, |ip_addr| self.inner.has_neighbor(cx, &ip_addr)) + .egress_permitted(cx.now, |ip_addr| inner.has_neighbor(cx, &ip_addr)) { continue; } let mut neighbor_addr = None; let mut device_result = Ok(()); - let &mut Self { - ref mut device, - ref mut inner, - } = self; macro_rules! respond { ($response:expr) => {{ @@ -2488,7 +2526,6 @@ mod test { #[cfg(feature = "medium-ethernet")] use crate::iface::NeighborCache; use crate::phy::{ChecksumCapabilities, Loopback}; - use crate::socket::SocketSet; #[cfg(feature = "proto-igmp")] use crate::time::Instant; use crate::{Error, Result}; @@ -2500,7 +2537,7 @@ mod test { } } - fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create_loopback<'a>() -> Interface<'a, Loopback> { #[cfg(feature = "medium-ethernet")] return create_loopback_ethernet(); #[cfg(not(feature = "medium-ethernet"))] @@ -2509,7 +2546,7 @@ mod test { #[cfg(all(feature = "medium-ip"))] #[allow(unused)] - fn create_loopback_ip<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create_loopback_ip<'a>() -> Interface<'a, Loopback> { // Create a basic device let device = Loopback::new(Medium::Ip); let ip_addrs = [ @@ -2521,16 +2558,14 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - let iface_builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let iface_builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(); - - (iface, SocketSet::new(vec![])) + iface_builder.finalize() } #[cfg(all(feature = "medium-ethernet"))] - fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create_loopback_ethernet<'a>() -> Interface<'a, Loopback> { // Create a basic device let device = Loopback::new(Medium::Ethernet); let ip_addrs = [ @@ -2542,15 +2577,13 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - let iface_builder = InterfaceBuilder::new(device) + let iface_builder = InterfaceBuilder::new(device, vec![]) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(); - - (iface, SocketSet::new(vec![])) + iface_builder.finalize() } #[cfg(feature = "proto-igmp")] @@ -2583,13 +2616,13 @@ mod test { #[should_panic(expected = "hardware_addr required option was not set")] #[cfg(all(feature = "medium-ethernet"))] fn test_builder_initialization_panic() { - InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); + InterfaceBuilder::new(Loopback::new(Medium::Ethernet), vec![]).finalize(); } #[test] #[cfg(feature = "proto-ipv4")] fn test_no_icmp_no_unicast_ipv4() { - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); // Unknown Ipv4 Protocol // @@ -2613,7 +2646,7 @@ mod test { // broadcast address let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), Ok(None) ); } @@ -2621,7 +2654,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_no_icmp_no_unicast_ipv6() { - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); // Unknown Ipv6 Protocol // @@ -2645,7 +2678,7 @@ mod test { // broadcast address let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv6(&cx, &mut socket_set, &frame), + iface.inner.process_ipv6(&cx, &mut iface.sockets, &frame), Ok(None) ); } @@ -2654,7 +2687,7 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_icmp_error_no_payload() { static NO_BYTES: [u8; 0] = []; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { @@ -2698,7 +2731,7 @@ mod test { // And we correctly handle no payload. let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), Ok(Some(expected_repr)) ); } @@ -2706,7 +2739,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv4")] fn test_local_subnet_broadcasts() { - let (mut iface, _) = create_loopback(); + let mut iface = create_loopback(); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); @@ -2763,7 +2796,7 @@ mod test { static UDP_PAYLOAD: [u8; 12] = [ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; - let (iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let mut udp_bytes_unicast = vec![0u8; 20]; let mut udp_bytes_broadcast = vec![0u8; 20]; @@ -2825,7 +2858,7 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut socket_set, ip_repr, false, data), + .process_udp(&cx, &mut iface.sockets, ip_repr, false, data), Ok(Some(expected_repr)) ); @@ -2853,7 +2886,7 @@ mod test { assert_eq!( iface.inner.process_udp( &cx, - &mut socket_set, + &mut iface.sockets, ip_repr, false, packet_broadcast.into_inner() @@ -2870,7 +2903,7 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); let tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); @@ -2880,7 +2913,7 @@ mod test { let mut udp_bytes = vec![0u8; 13]; let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); - let socket_handle = socket_set.add(udp_socket); + let socket_handle = iface.add_socket(udp_socket); #[cfg(feature = "proto-ipv6")] let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -2911,7 +2944,7 @@ mod test { { // Bind the socket to port 68 - let mut socket = socket_set.get::(socket_handle); + let mut socket = iface.get_socket::(socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -2931,14 +2964,14 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut socket_set, ip_repr, false, packet.into_inner()), + .process_udp(&cx, &mut iface.sockets, ip_repr, false, packet.into_inner()), Ok(None) ); { // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer - let mut socket = socket_set.get::(socket_handle); + let mut socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -2952,7 +2985,7 @@ mod test { fn test_handle_ipv4_broadcast() { use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let our_ipv4_addr = iface.ipv4_address().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); @@ -3005,7 +3038,7 @@ mod test { let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), Ok(Some(expected_packet)) ); } @@ -3025,7 +3058,7 @@ mod test { #[cfg(feature = "proto-ipv6")] const MAX_PAYLOAD_LEN: usize = 1192; - let (iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let src_addr = Ipv4Address([192, 168, 1, 1]); @@ -3119,7 +3152,7 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut socket_set, ip_repr.into(), false, payload), + .process_udp(&cx, &mut iface.sockets, ip_repr.into(), false, payload), Ok(Some(IpPacket::Icmpv4(( expected_ip_repr, expected_icmp_repr @@ -3129,7 +3162,7 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut socket_set, ip_repr.into(), false, payload), + .process_udp(&cx, &mut iface.sockets, ip_repr.into(), false, payload), Ok(Some(IpPacket::Icmpv6(( expected_ip_repr, expected_icmp_repr @@ -3140,7 +3173,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { - let (mut iface, mut socket_set) = create_loopback_ethernet(); + let mut iface = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3172,7 +3205,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&cx, &mut socket_set, frame.into_inner()), + .process_ethernet(&cx, &mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3197,7 +3230,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { - let (mut iface, mut socket_set) = create_loopback_ethernet(); + let mut iface = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 86]; @@ -3252,7 +3285,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&cx, &mut socket_set, frame.into_inner()), + .process_ethernet(&cx, &mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected @@ -3274,7 +3307,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { - let (mut iface, mut socket_set) = create_loopback_ethernet(); + let mut iface = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3304,7 +3337,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&cx, &mut socket_set, frame.into_inner()), + .process_ethernet(&cx, &mut iface.sockets, frame.into_inner()), Ok(None) ); @@ -3323,7 +3356,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_arp_flush_after_update_ip() { - let (mut iface, mut socket_set) = create_loopback_ethernet(); + let mut iface = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3355,7 +3388,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&cx, &mut socket_set, frame.into_inner()), + .process_ethernet(&cx, &mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3396,21 +3429,21 @@ mod test { use crate::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; use crate::wire::Icmpv4Packet; - let (iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 24]); let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 24]); let icmpv4_socket = IcmpSocket::new(rx_buffer, tx_buffer); - let socket_handle = socket_set.add(icmpv4_socket); + let socket_handle = iface.add_socket(icmpv4_socket); let ident = 0x1234; let seq_no = 0x5432; let echo_data = &[0xff; 16]; { - let mut socket = socket_set.get::(socket_handle); + let mut socket = iface.get_socket::(socket_handle); // Bind to the ID 0x1234 assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(())); } @@ -3438,7 +3471,7 @@ mod test { // Open a socket and ensure the packet is handled due to the listening // socket. { - assert!(!socket_set.get::(socket_handle).can_recv()); + assert!(!iface.get_socket::(socket_handle).can_recv()); } // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening @@ -3456,12 +3489,12 @@ mod test { assert_eq!( iface .inner - .process_icmpv4(&cx, &mut socket_set, ip_repr, icmp_data), + .process_icmpv4(&cx, &mut iface.sockets, ip_repr, icmp_data), Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) ); { - let mut socket = socket_set.get::(socket_handle); + let mut socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3476,7 +3509,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { - let (mut iface, _) = create_loopback(); + let mut iface = create_loopback(); let mut new_addrs = vec![ IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), @@ -3499,7 +3532,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_icmpv6_nxthdr_unknown() { - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -3556,7 +3589,7 @@ mod test { // Ensure the unknown next header causes a ICMPv6 Parameter Problem // error message to be sent to the sender. assert_eq!( - iface.inner.process_ipv6(&cx, &mut socket_set, &frame), + iface.inner.process_ipv6(&cx, &mut iface.sockets, &frame), Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))) ); } @@ -3598,7 +3631,7 @@ mod test { Ipv4Address::new(224, 0, 0, 56), ]; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); // Join multicast groups let timestamp = Instant::now(); @@ -3643,7 +3676,7 @@ mod test { // GENERAL_QUERY_BYTES. Therefore `recv_all()` would return 0 // pkts that could be checked. let cx = iface.context(timestamp); - iface.socket_ingress(&cx, &mut socket_set); + iface.socket_ingress(&cx); // Leave multicast groups let timestamp = Instant::now(); @@ -3666,7 +3699,7 @@ mod test { use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let packets = 1; let rx_buffer = @@ -3676,7 +3709,7 @@ mod test { vec![0; 48 * packets], ); let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - socket_set.add(raw_socket); + iface.add_socket(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); @@ -3725,7 +3758,7 @@ mod test { let cx = iface.context(Instant::from_millis(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), Ok(None) ); } @@ -3736,7 +3769,7 @@ mod test { use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let packets = 1; let rx_buffer = @@ -3746,7 +3779,7 @@ mod test { vec![0; 48 * packets], ); let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - socket_set.add(raw_socket); + iface.add_socket(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); @@ -3795,7 +3828,7 @@ mod test { }; let cx = iface.context(Instant::from_millis(0)); - let frame = iface.inner.process_ipv4(&cx, &mut socket_set, &frame); + let frame = iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame); // because the packet could not be handled we should send an Icmp message assert!(match frame { @@ -3815,15 +3848,15 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (mut iface, mut socket_set) = create_loopback(); + let mut iface = create_loopback(); let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); - let udp_socket_handle = socket_set.add(udp_socket); + let udp_socket_handle = iface.add_socket(udp_socket); { // Bind the socket to port 68 - let mut socket = socket_set.get::(udp_socket_handle); + let mut socket = iface.get_socket::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -3842,7 +3875,7 @@ mod test { raw_rx_buffer, raw_tx_buffer, ); - socket_set.add(raw_socket); + iface.add_socket(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); @@ -3890,13 +3923,13 @@ mod test { let cx = iface.context(Instant::from_millis(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut socket_set, &frame), + iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), Ok(None) ); { // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let mut socket = socket_set.get::(udp_socket_handle); + let mut socket = iface.get_socket::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), From ef213fa7720d013dd57736540daa1adb55e67f91 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 03:49:54 +0200 Subject: [PATCH 260/566] socket: remove SocketRef. The intent was to run custom code after the user is done modifying the socket, for example to update a (not yet existing) port->socket map in SocketSet. However this wouldn't work, since the SocketRef would have to borrow the SocketSet at the same time as the Socket to be able to notify the SocketSet. I believe such indexing can be achieved by setting a "dirty" bit *before* giving the socket to the user, then on poll() reindexing all dirty sockets. This could even be faster: if user gets a socket multiple times between polls, it'd be reindexed only once. --- examples/benchmark.rs | 4 +- examples/client.rs | 4 +- examples/httpclient.rs | 2 +- examples/multicast.rs | 4 +- examples/ping.rs | 2 +- examples/server.rs | 10 ++--- examples/sixlowpan.rs | 2 +- src/iface/interface.rs | 30 +++++++-------- src/socket/mod.rs | 24 ++++-------- src/socket/ref_.rs | 87 ------------------------------------------ src/socket/set.rs | 15 ++++---- 11 files changed, 44 insertions(+), 140 deletions(-) delete mode 100644 src/socket/ref_.rs diff --git a/examples/benchmark.rs b/examples/benchmark.rs index a07b9a608..9ba3d2978 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -120,7 +120,7 @@ fn main() { // tcp:1234: emit data { - let mut socket = iface.get_socket::(tcp1_handle); + let socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -140,7 +140,7 @@ fn main() { // tcp:1235: sink data { - let mut socket = iface.get_socket::(tcp2_handle); + let socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } diff --git a/examples/client.rs b/examples/client.rs index 5cfa50150..f09617465 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -55,7 +55,7 @@ fn main() { let tcp_handle = iface.add_socket(tcp_socket); { - let mut socket = iface.get_socket::(tcp_handle); + let socket = iface.get_socket::(tcp_handle); socket.connect((address, port), 49500).unwrap(); } @@ -70,7 +70,7 @@ fn main() { } { - let mut socket = iface.get_socket::(tcp_handle); + let socket = iface.get_socket::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 73f08c3f2..0ced70fbc 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -77,7 +77,7 @@ fn main() { } { - let mut socket = iface.get_socket::(tcp_handle); + let socket = iface.get_socket::(tcp_handle); state = match state { State::Connect if !socket.is_active() => { diff --git a/examples/multicast.rs b/examples/multicast.rs index 9bc90cd43..e3f2fc653 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -78,7 +78,7 @@ fn main() { } { - let mut socket = iface.get_socket::(raw_handle); + let socket = iface.get_socket::(raw_handle); if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets @@ -93,7 +93,7 @@ fn main() { } } { - let mut socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(MDNS_PORT).unwrap() } diff --git a/examples/ping.rs b/examples/ping.rs index 4b14c1444..b4e9038d0 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -157,7 +157,7 @@ fn main() { { let timestamp = Instant::now(); - let mut socket = iface.get_socket::(icmp_handle); + let socket = iface.get_socket::(icmp_handle); if !socket.is_open() { socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); send_at = timestamp; diff --git a/examples/server.rs b/examples/server.rs index 7e1c32a70..c2abb1832 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -81,7 +81,7 @@ fn main() { // udp:6969: respond "hello" { - let mut socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -109,7 +109,7 @@ fn main() { // tcp:6969: respond "hello" { - let mut socket = iface.get_socket::(tcp1_handle); + let socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(6969).unwrap(); } @@ -124,7 +124,7 @@ fn main() { // tcp:6970: echo with reverse { - let mut socket = iface.get_socket::(tcp2_handle); + let socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(6970).unwrap() } @@ -168,7 +168,7 @@ fn main() { // tcp:6971: sinkhole { - let mut socket = iface.get_socket::(tcp3_handle); + let socket = iface.get_socket::(tcp3_handle); if !socket.is_open() { socket.listen(6971).unwrap(); socket.set_keep_alive(Some(Duration::from_millis(1000))); @@ -191,7 +191,7 @@ fn main() { // tcp:6972: fountain { - let mut socket = iface.get_socket::(tcp4_handle); + let socket = iface.get_socket::(tcp4_handle); if !socket.is_open() { socket.listen(6972).unwrap() } diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 477391438..c653d66aa 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -101,7 +101,7 @@ fn main() { // udp:6969: respond "hello" { - let mut socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 7469e9103..ac7c43b0f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -485,7 +485,7 @@ where /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get_socket>(&mut self, handle: SocketHandle) -> SocketRef { + pub fn get_socket>(&mut self, handle: SocketHandle) -> &mut T { self.sockets.get(handle) } @@ -830,7 +830,7 @@ where let _caps = device.capabilities(); let mut emitted_any = false; - for mut socket in sockets.iter_mut() { + for socket in sockets.iter_mut() { if !socket .meta_mut() .egress_permitted(cx.now, |ip_addr| inner.has_neighbor(cx, &ip_addr)) @@ -1202,7 +1202,7 @@ impl<'a> InterfaceInner<'a> { // Look for UDP sockets that will accept the UDP packet. // If it does not accept the packet, then send an ICMP message. - for mut udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { + for udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { if !udp_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { continue; } @@ -1328,7 +1328,7 @@ impl<'a> InterfaceInner<'a> { 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) { + for raw_socket in sockets.iter_mut().filter_map(RawSocket::downcast) { if !raw_socket.accepts(ip_repr) { continue; } @@ -1460,7 +1460,7 @@ 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) = + if let Some(dhcp_socket) = sockets.iter_mut().filter_map(Dhcpv4Socket::downcast).next() { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); @@ -1639,7 +1639,7 @@ impl<'a> InterfaceInner<'a> { 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) { + for icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) { if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue; } @@ -1825,7 +1825,7 @@ impl<'a> InterfaceInner<'a> { 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) { + for icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) { if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue; } @@ -1949,7 +1949,7 @@ impl<'a> InterfaceInner<'a> { 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) { + for udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { if !udp_socket.accepts(&ip_repr, &udp_repr) { continue; } @@ -2006,7 +2006,7 @@ impl<'a> InterfaceInner<'a> { let tcp_packet = TcpPacket::new_checked(ip_payload)?; 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) { + for tcp_socket in sockets.iter_mut().filter_map(TcpSocket::downcast) { if !tcp_socket.accepts(&ip_repr, &tcp_repr) { continue; } @@ -2944,7 +2944,7 @@ mod test { { // Bind the socket to port 68 - let mut socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -2971,7 +2971,7 @@ mod test { { // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer - let mut socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3443,7 +3443,7 @@ mod test { let echo_data = &[0xff; 16]; { - let mut socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); // Bind to the ID 0x1234 assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(())); } @@ -3494,7 +3494,7 @@ mod test { ); { - let mut socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3856,7 +3856,7 @@ mod test { let udp_socket_handle = iface.add_socket(udp_socket); { // Bind the socket to port 68 - let mut socket = iface.get_socket::(udp_socket_handle); + let socket = iface.get_socket::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -3929,7 +3929,7 @@ mod test { { // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let mut socket = iface.get_socket::(udp_socket_handle); + let socket = iface.get_socket::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 9836c4bb4..30262b048 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -24,7 +24,6 @@ mod icmp; mod meta; #[cfg(feature = "socket-raw")] mod raw; -mod ref_; mod set; #[cfg(feature = "socket-tcp")] mod tcp; @@ -59,9 +58,6 @@ pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Even pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; -pub use self::ref_::Ref as SocketRef; -pub(crate) use self::ref_::Session as SocketSession; - /// Gives an indication on the next time the socket should be polled. #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -144,25 +140,19 @@ impl<'a> Socket<'a> { } } -impl<'a> SocketSession for Socket<'a> { - fn finish(&mut self) { - dispatch_socket!(mut self, |socket| socket.finish()) - } -} - /// A conversion trait for network sockets. -pub trait AnySocket<'a>: SocketSession + Sized { - fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option>; +pub trait AnySocket<'a>: Sized { + fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; } macro_rules! from_socket { ($socket:ty, $variant:ident) => { impl<'a> AnySocket<'a> for $socket { - fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option> { - if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) { - Some(SocketRef::new(socket)) - } else { - None + fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> { + #[allow(unreachable_patterns)] + match socket { + Socket::$variant(socket) => Some(socket), + _ => None, } } } diff --git a/src/socket/ref_.rs b/src/socket/ref_.rs deleted file mode 100644 index 93992c93a..000000000 --- a/src/socket/ref_.rs +++ /dev/null @@ -1,87 +0,0 @@ -use core::ops::{Deref, DerefMut}; - -/// A trait for tracking a socket usage session. -/// -/// Allows implementation of custom drop logic that runs only if the socket was changed -/// in specific ways. For example, drop logic for UDP would check if the local endpoint -/// has changed, and if yes, notify the socket set. -#[doc(hidden)] -pub trait Session { - fn finish(&mut self) {} -} - -#[cfg(feature = "socket-raw")] -impl<'a> Session for crate::socket::RawSocket<'a> {} -#[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") -))] -impl<'a> Session for crate::socket::IcmpSocket<'a> {} -#[cfg(feature = "socket-udp")] -impl<'a> Session for crate::socket::UdpSocket<'a> {} -#[cfg(feature = "socket-tcp")] -impl<'a> Session for crate::socket::TcpSocket<'a> {} -#[cfg(feature = "socket-dhcpv4")] -impl Session for crate::socket::Dhcpv4Socket {} - -/// A smart pointer to a socket. -/// -/// Allows the network stack to efficiently determine if the socket state was changed in any way. -pub struct Ref<'a, T: Session + 'a> { - /// Reference to the socket. - /// - /// This is almost always `Some` except when dropped in `into_inner` which removes the socket - /// reference. This properly tracks the initialization state without any additional bytes as - /// the `None` variant occupies the `0` pattern which is invalid for the reference. - socket: Option<&'a mut T>, -} - -impl<'a, T: Session + 'a> Ref<'a, T> { - /// Wrap a pointer to a socket to make a smart pointer. - /// - /// Calling this function is only necessary if your code is using [into_inner]. - /// - /// [into_inner]: #method.into_inner - pub fn new(socket: &'a mut T) -> Self { - Ref { - socket: Some(socket), - } - } - - /// Unwrap a smart pointer to a socket. - /// - /// The finalization code is not run. Prompt operation of the network stack depends - /// on wrapping the returned pointer back and dropping it. - /// - /// Calling this function is only necessary to achieve composability if you *must* - /// map a `&mut SocketRef<'a, XSocket>` to a `&'a mut XSocket` (note the lifetimes); - /// be sure to call [new] afterwards. - /// - /// [new]: #method.new - pub fn into_inner(mut ref_: Self) -> &'a mut T { - ref_.socket.take().unwrap() - } -} - -impl<'a, T: Session> Deref for Ref<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - // Deref is only used while the socket is still in place (into inner has not been called). - self.socket.as_ref().unwrap() - } -} - -impl<'a, T: Session> DerefMut for Ref<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.socket.as_mut().unwrap() - } -} - -impl<'a, T: Session> Drop for Ref<'a, T> { - fn drop(&mut self) { - if let Some(socket) = self.socket.take() { - Session::finish(socket); - } - } -} diff --git a/src/socket/set.rs b/src/socket/set.rs index 4c37efa64..cb96dfe51 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -3,7 +3,7 @@ use managed::ManagedSlice; #[cfg(feature = "socket-tcp")] use crate::socket::TcpState; -use crate::socket::{AnySocket, Socket, SocketRef}; +use crate::socket::{AnySocket, Socket}; /// An item of a socket set. /// @@ -84,10 +84,11 @@ impl<'a> Set<'a> { /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get>(&mut self, handle: Handle) -> SocketRef { + pub fn get>(&mut self, handle: Handle) -> &mut T { match self.sockets[handle.0].as_mut() { - Some(item) => T::downcast(SocketRef::new(&mut item.socket)) - .expect("handle refers to a socket of a wrong type"), + Some(item) => { + T::downcast(&mut item.socket).expect("handle refers to a socket of a wrong type") + } None => panic!("handle does not refer to a valid socket"), } } @@ -179,7 +180,7 @@ impl<'a> Set<'a> { } } - /// Iterate every socket in this set, as SocketRef. + /// Iterate every socket in this set. pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> { IterMut { lower: self.sockets.iter_mut(), @@ -217,12 +218,12 @@ pub struct IterMut<'a, 'b: 'a> { } impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> { - type Item = SocketRef<'a, Socket<'b>>; + type Item = &'a mut Socket<'b>; fn next(&mut self) -> Option { for item_opt in &mut self.lower { if let Some(item) = item_opt.as_mut() { - return Some(SocketRef::new(&mut item.socket)); + return Some(&mut item.socket); } } None From 9a2093e39e26f5a41cbeebc55227c5068b388a22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 04:11:41 +0200 Subject: [PATCH 261/566] Remove braces that were previously needed to workaround SocketRef borrow issues. --- examples/benchmark.rs | 56 ++++++----- examples/client.rs | 76 ++++++++------- examples/httpclient.rs | 74 ++++++++------- examples/loopback.rs | 64 ++++++------- examples/multicast.rs | 51 +++++------ examples/ping.rs | 181 ++++++++++++++++++------------------ examples/server.rs | 204 ++++++++++++++++++++--------------------- examples/sixlowpan.rs | 40 ++++---- src/iface/interface.rs | 119 ++++++++++-------------- 9 files changed, 407 insertions(+), 458 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 9ba3d2978..f9142c239 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -119,42 +119,38 @@ fn main() { } // tcp:1234: emit data - { - let socket = iface.get_socket::(tcp1_handle); - if !socket.is_open() { - socket.listen(1234).unwrap(); - } + let socket = iface.get_socket::(tcp1_handle); + if !socket.is_open() { + socket.listen(1234).unwrap(); + } - if socket.can_send() { - if processed < AMOUNT { - let length = socket - .send(|buffer| { - let length = cmp::min(buffer.len(), AMOUNT - processed); - (length, length) - }) - .unwrap(); - processed += length; - } + if socket.can_send() { + if processed < AMOUNT { + let length = socket + .send(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); + processed += length; } } // tcp:1235: sink data - { - let socket = iface.get_socket::(tcp2_handle); - if !socket.is_open() { - socket.listen(1235).unwrap(); - } + let socket = iface.get_socket::(tcp2_handle); + if !socket.is_open() { + socket.listen(1235).unwrap(); + } - if socket.can_recv() { - if processed < AMOUNT { - let length = socket - .recv(|buffer| { - let length = cmp::min(buffer.len(), AMOUNT - processed); - (length, length) - }) - .unwrap(); - processed += length; - } + if socket.can_recv() { + if processed < AMOUNT { + let length = socket + .recv(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); + processed += length; } } diff --git a/examples/client.rs b/examples/client.rs index f09617465..00e7c709b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -54,10 +54,8 @@ fn main() { let tcp_handle = iface.add_socket(tcp_socket); - { - let socket = iface.get_socket::(tcp_handle); - socket.connect((address, port), 49500).unwrap(); - } + let socket = iface.get_socket::(tcp_handle); + socket.connect((address, port), 49500).unwrap(); let mut tcp_active = false; loop { @@ -69,43 +67,41 @@ fn main() { } } - { - let socket = iface.get_socket::(tcp_handle); - if socket.is_active() && !tcp_active { - debug!("connected"); - } else if !socket.is_active() && tcp_active { - debug!("disconnected"); - break; - } - tcp_active = socket.is_active(); - - if socket.may_recv() { - let data = socket - .recv(|data| { - let mut data = data.to_owned(); - if !data.is_empty() { - debug!( - "recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); - data = data.split(|&b| b == b'\n').collect::>().concat(); - data.reverse(); - data.extend(b"\n"); - } - (data.len(), data) - }) - .unwrap(); - if socket.can_send() && !data.is_empty() { - debug!( - "send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); - socket.send_slice(&data[..]).unwrap(); - } - } else if socket.may_send() { - debug!("close"); - socket.close(); + let socket = iface.get_socket::(tcp_handle); + if socket.is_active() && !tcp_active { + debug!("connected"); + } else if !socket.is_active() && tcp_active { + debug!("disconnected"); + break; + } + tcp_active = socket.is_active(); + + if socket.may_recv() { + let data = socket + .recv(|data| { + let mut data = data.to_owned(); + if !data.is_empty() { + debug!( + "recv data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + data = data.split(|&b| b == b'\n').collect::>().concat(); + data.reverse(); + data.extend(b"\n"); + } + (data.len(), data) + }) + .unwrap(); + if socket.can_send() && !data.is_empty() { + debug!( + "send data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + socket.send_slice(&data[..]).unwrap(); } + } else if socket.may_send() { + debug!("close"); + socket.close(); } phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 0ced70fbc..d1370c1b4 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -76,46 +76,44 @@ fn main() { } } - { - let socket = iface.get_socket::(tcp_handle); + let socket = iface.get_socket::(tcp_handle); - state = match state { - State::Connect if !socket.is_active() => { - debug!("connecting"); - let local_port = 49152 + rand::random::() % 16384; - socket - .connect((address, url.port().unwrap_or(80)), local_port) - .unwrap(); - State::Request - } - State::Request if socket.may_send() => { - debug!("sending request"); - let http_get = "GET ".to_owned() + url.path() + " HTTP/1.1\r\n"; - socket.send_slice(http_get.as_ref()).expect("cannot send"); - let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n"; - socket.send_slice(http_host.as_ref()).expect("cannot send"); - socket - .send_slice(b"Connection: close\r\n") - .expect("cannot send"); - socket.send_slice(b"\r\n").expect("cannot send"); - State::Response - } - State::Response if socket.can_recv() => { - socket - .recv(|data| { - println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)")); - (data.len(), ()) - }) - .unwrap(); - State::Response - } - State::Response if !socket.may_recv() => { - debug!("received complete response"); - break; - } - _ => state, + state = match state { + State::Connect if !socket.is_active() => { + debug!("connecting"); + let local_port = 49152 + rand::random::() % 16384; + socket + .connect((address, url.port().unwrap_or(80)), local_port) + .unwrap(); + State::Request } - } + State::Request if socket.may_send() => { + debug!("sending request"); + let http_get = "GET ".to_owned() + url.path() + " HTTP/1.1\r\n"; + socket.send_slice(http_get.as_ref()).expect("cannot send"); + let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n"; + socket.send_slice(http_host.as_ref()).expect("cannot send"); + socket + .send_slice(b"Connection: close\r\n") + .expect("cannot send"); + socket.send_slice(b"\r\n").expect("cannot send"); + State::Response + } + State::Response if socket.can_recv() => { + socket + .recv(|data| { + println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)")); + (data.len(), ()) + }) + .unwrap(); + State::Response + } + State::Response if !socket.may_recv() => { + debug!("received complete response"); + break; + } + _ => state, + }; phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); } diff --git a/examples/loopback.rs b/examples/loopback.rs index d08f1d405..9b8abf330 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -135,46 +135,42 @@ fn main() { } } - { - let mut socket = iface.get_socket::(server_handle); - if !socket.is_active() && !socket.is_listening() { - if !did_listen { - debug!("listening"); - socket.listen(1234).unwrap(); - did_listen = true; - } + let mut socket = iface.get_socket::(server_handle); + if !socket.is_active() && !socket.is_listening() { + if !did_listen { + debug!("listening"); + socket.listen(1234).unwrap(); + did_listen = true; } + } - if socket.can_recv() { - debug!( - "got {:?}", - socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) }) - ); - socket.close(); - done = true; - } + if socket.can_recv() { + debug!( + "got {:?}", + socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) }) + ); + socket.close(); + done = true; } - { - let mut socket = iface.get_socket::(client_handle); - if !socket.is_open() { - if !did_connect { - debug!("connecting"); - socket - .connect( - (IpAddress::v4(127, 0, 0, 1), 1234), - (IpAddress::Unspecified, 65000), - ) - .unwrap(); - did_connect = true; - } + let mut socket = iface.get_socket::(client_handle); + if !socket.is_open() { + if !did_connect { + debug!("connecting"); + socket + .connect( + (IpAddress::v4(127, 0, 0, 1), 1234), + (IpAddress::Unspecified, 65000), + ) + .unwrap(); + did_connect = true; } + } - if socket.can_send() { - debug!("sending"); - socket.send_slice(b"0123456789abcdef").unwrap(); - socket.close(); - } + if socket.can_send() { + debug!("sending"); + socket.send_slice(b"0123456789abcdef").unwrap(); + socket.close(); } match iface.poll_delay(clock.elapsed()) { diff --git a/examples/multicast.rs b/examples/multicast.rs index e3f2fc653..b92115b92 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -77,35 +77,32 @@ fn main() { } } - { - let socket = iface.get_socket::(raw_handle); - - if socket.can_recv() { - // For display purposes only - normally we wouldn't process incoming IGMP packets - // in the application layer - socket - .recv() - .and_then(Ipv4Packet::new_checked) - .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) - .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) - .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) - .unwrap_or_else(|e| println!("Recv IGMP error: {:?}", e)); - } + let socket = iface.get_socket::(raw_handle); + + if socket.can_recv() { + // For display purposes only - normally we wouldn't process incoming IGMP packets + // in the application layer + socket + .recv() + .and_then(Ipv4Packet::new_checked) + .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) + .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) + .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) + .unwrap_or_else(|e| println!("Recv IGMP error: {:?}", e)); } - { - let socket = iface.get_socket::(udp_handle); - if !socket.is_open() { - socket.bind(MDNS_PORT).unwrap() - } - if socket.can_recv() { - socket - .recv() - .map(|(data, sender)| { - println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender) - }) - .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); - } + let socket = iface.get_socket::(udp_handle); + if !socket.is_open() { + socket.bind(MDNS_PORT).unwrap() + } + + if socket.can_recv() { + socket + .recv() + .map(|(data, sender)| { + println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender) + }) + .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); } phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); diff --git a/examples/ping.rs b/examples/ping.rs index b4e9038d0..66fad82f2 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -155,108 +155,105 @@ fn main() { } } - { - let timestamp = Instant::now(); - let socket = iface.get_socket::(icmp_handle); - if !socket.is_open() { - socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); - send_at = timestamp; - } + let timestamp = Instant::now(); + let socket = iface.get_socket::(icmp_handle); + if !socket.is_open() { + socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); + send_at = timestamp; + } - if socket.can_send() && seq_no < count as u16 && send_at <= timestamp { - NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis()); + if socket.can_send() && seq_no < count as u16 && send_at <= timestamp { + NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis()); - match remote_addr { - IpAddress::Ipv4(_) => { - let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - Icmpv4Repr, - Icmpv4Packet, - ident, - seq_no, - echo_payload, - socket, - remote_addr - ); - icmp_repr.emit(&mut icmp_packet, &device_caps.checksum); - } - IpAddress::Ipv6(_) => { - let (icmp_repr, mut icmp_packet) = send_icmp_ping!( - Icmpv6Repr, - Icmpv6Packet, - ident, - seq_no, - echo_payload, - socket, - remote_addr - ); - icmp_repr.emit( - &src_ipv6, - &remote_addr, - &mut icmp_packet, - &device_caps.checksum, - ); - } - _ => unimplemented!(), + match remote_addr { + IpAddress::Ipv4(_) => { + let (icmp_repr, mut icmp_packet) = send_icmp_ping!( + Icmpv4Repr, + Icmpv4Packet, + ident, + seq_no, + echo_payload, + socket, + remote_addr + ); + icmp_repr.emit(&mut icmp_packet, &device_caps.checksum); } - - waiting_queue.insert(seq_no, timestamp); - seq_no += 1; - send_at += interval; + IpAddress::Ipv6(_) => { + let (icmp_repr, mut icmp_packet) = send_icmp_ping!( + Icmpv6Repr, + Icmpv6Packet, + ident, + seq_no, + echo_payload, + socket, + remote_addr + ); + icmp_repr.emit( + &src_ipv6, + &remote_addr, + &mut icmp_packet, + &device_caps.checksum, + ); + } + _ => unimplemented!(), } - if socket.can_recv() { - let (payload, _) = socket.recv().unwrap(); + waiting_queue.insert(seq_no, timestamp); + seq_no += 1; + send_at += interval; + } + + if socket.can_recv() { + let (payload, _) = socket.recv().unwrap(); - match remote_addr { - IpAddress::Ipv4(_) => { - let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap(); - let icmp_repr = - Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap(); - get_icmp_pong!( - Icmpv4Repr, - icmp_repr, - payload, - waiting_queue, - remote_addr, - timestamp, - received - ); - } - IpAddress::Ipv6(_) => { - let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); - let icmp_repr = Icmpv6Repr::parse( - &remote_addr, - &src_ipv6, - &icmp_packet, - &device_caps.checksum, - ) - .unwrap(); - get_icmp_pong!( - Icmpv6Repr, - icmp_repr, - payload, - waiting_queue, - remote_addr, - timestamp, - received - ); - } - _ => unimplemented!(), + match remote_addr { + IpAddress::Ipv4(_) => { + let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap(); + let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap(); + get_icmp_pong!( + Icmpv4Repr, + icmp_repr, + payload, + waiting_queue, + remote_addr, + timestamp, + received + ); } - } - - waiting_queue.retain(|seq, from| { - if timestamp - *from < timeout { - true - } else { - println!("From {} icmp_seq={} timeout", remote_addr, seq); - false + IpAddress::Ipv6(_) => { + let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); + let icmp_repr = Icmpv6Repr::parse( + &remote_addr, + &src_ipv6, + &icmp_packet, + &device_caps.checksum, + ) + .unwrap(); + get_icmp_pong!( + Icmpv6Repr, + icmp_repr, + payload, + waiting_queue, + remote_addr, + timestamp, + received + ); } - }); + _ => unimplemented!(), + } + } - if seq_no == count as u16 && waiting_queue.is_empty() { - break; + waiting_queue.retain(|seq, from| { + if timestamp - *from < timeout { + true + } else { + println!("From {} icmp_seq={} timeout", remote_addr, seq); + false } + }); + + if seq_no == count as u16 && waiting_queue.is_empty() { + break; } let timestamp = Instant::now(); diff --git a/examples/server.rs b/examples/server.rs index c2abb1832..d518b3349 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -80,135 +80,125 @@ fn main() { } // udp:6969: respond "hello" - { - let socket = iface.get_socket::(udp_handle); - if !socket.is_open() { - socket.bind(6969).unwrap() - } + let socket = iface.get_socket::(udp_handle); + if !socket.is_open() { + socket.bind(6969).unwrap() + } - let client = match socket.recv() { - Ok((data, endpoint)) => { - debug!( - "udp:6969 recv data: {:?} from {}", - str::from_utf8(data).unwrap(), - endpoint - ); - Some(endpoint) - } - Err(_) => None, - }; - if let Some(endpoint) = client { - let data = b"hello\n"; + let client = match socket.recv() { + Ok((data, endpoint)) => { debug!( - "udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap() + "udp:6969 recv data: {:?} from {}", + str::from_utf8(data).unwrap(), + endpoint ); - socket.send_slice(data, endpoint).unwrap(); + Some(endpoint) } + Err(_) => None, + }; + if let Some(endpoint) = client { + let data = b"hello\n"; + debug!( + "udp:6969 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap() + ); + socket.send_slice(data, endpoint).unwrap(); } // tcp:6969: respond "hello" - { - let socket = iface.get_socket::(tcp1_handle); - if !socket.is_open() { - socket.listen(6969).unwrap(); - } + let socket = iface.get_socket::(tcp1_handle); + if !socket.is_open() { + socket.listen(6969).unwrap(); + } - if socket.can_send() { - debug!("tcp:6969 send greeting"); - writeln!(socket, "hello").unwrap(); - debug!("tcp:6969 close"); - socket.close(); - } + if socket.can_send() { + debug!("tcp:6969 send greeting"); + writeln!(socket, "hello").unwrap(); + debug!("tcp:6969 close"); + socket.close(); } // tcp:6970: echo with reverse - { - let socket = iface.get_socket::(tcp2_handle); - if !socket.is_open() { - socket.listen(6970).unwrap() - } + let socket = iface.get_socket::(tcp2_handle); + if !socket.is_open() { + socket.listen(6970).unwrap() + } - if socket.is_active() && !tcp_6970_active { - debug!("tcp:6970 connected"); - } else if !socket.is_active() && tcp_6970_active { - debug!("tcp:6970 disconnected"); - } - tcp_6970_active = socket.is_active(); - - if socket.may_recv() { - let data = socket - .recv(|buffer| { - let recvd_len = buffer.len(); - let mut data = buffer.to_owned(); - if !data.is_empty() { - debug!( - "tcp:6970 recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); - data = data.split(|&b| b == b'\n').collect::>().concat(); - data.reverse(); - data.extend(b"\n"); - } - (recvd_len, data) - }) - .unwrap(); - if socket.can_send() && !data.is_empty() { - debug!( - "tcp:6970 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); - socket.send_slice(&data[..]).unwrap(); - } - } else if socket.may_send() { - debug!("tcp:6970 close"); - socket.close(); + if socket.is_active() && !tcp_6970_active { + debug!("tcp:6970 connected"); + } else if !socket.is_active() && tcp_6970_active { + debug!("tcp:6970 disconnected"); + } + tcp_6970_active = socket.is_active(); + + if socket.may_recv() { + let data = socket + .recv(|buffer| { + let recvd_len = buffer.len(); + let mut data = buffer.to_owned(); + if !data.is_empty() { + debug!( + "tcp:6970 recv data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + data = data.split(|&b| b == b'\n').collect::>().concat(); + data.reverse(); + data.extend(b"\n"); + } + (recvd_len, data) + }) + .unwrap(); + if socket.can_send() && !data.is_empty() { + debug!( + "tcp:6970 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + socket.send_slice(&data[..]).unwrap(); } + } else if socket.may_send() { + debug!("tcp:6970 close"); + socket.close(); } // tcp:6971: sinkhole - { - let socket = iface.get_socket::(tcp3_handle); - if !socket.is_open() { - socket.listen(6971).unwrap(); - socket.set_keep_alive(Some(Duration::from_millis(1000))); - socket.set_timeout(Some(Duration::from_millis(2000))); - } + let socket = iface.get_socket::(tcp3_handle); + if !socket.is_open() { + socket.listen(6971).unwrap(); + socket.set_keep_alive(Some(Duration::from_millis(1000))); + socket.set_timeout(Some(Duration::from_millis(2000))); + } - if socket.may_recv() { - socket - .recv(|buffer| { - if !buffer.is_empty() { - debug!("tcp:6971 recv {:?} octets", buffer.len()); - } - (buffer.len(), ()) - }) - .unwrap(); - } else if socket.may_send() { - socket.close(); - } + if socket.may_recv() { + socket + .recv(|buffer| { + if !buffer.is_empty() { + debug!("tcp:6971 recv {:?} octets", buffer.len()); + } + (buffer.len(), ()) + }) + .unwrap(); + } else if socket.may_send() { + socket.close(); } // tcp:6972: fountain - { - let socket = iface.get_socket::(tcp4_handle); - if !socket.is_open() { - socket.listen(6972).unwrap() - } + let socket = iface.get_socket::(tcp4_handle); + if !socket.is_open() { + socket.listen(6972).unwrap() + } - if socket.may_send() { - socket - .send(|data| { - if !data.is_empty() { - debug!("tcp:6972 send {:?} octets", data.len()); - for (i, b) in data.iter_mut().enumerate() { - *b = (i % 256) as u8; - } + if socket.may_send() { + socket + .send(|data| { + if !data.is_empty() { + debug!("tcp:6972 send {:?} octets", data.len()); + for (i, b) in data.iter_mut().enumerate() { + *b = (i % 256) as u8; } - (data.len(), ()) - }) - .unwrap(); - } + } + (data.len(), ()) + }) + .unwrap(); } phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index c653d66aa..234b96443 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -100,31 +100,29 @@ fn main() { } // udp:6969: respond "hello" - { - let socket = iface.get_socket::(udp_handle); - if !socket.is_open() { - socket.bind(6969).unwrap() - } + let socket = iface.get_socket::(udp_handle); + if !socket.is_open() { + socket.bind(6969).unwrap() + } - let client = match socket.recv() { - Ok((data, endpoint)) => { - debug!( - "udp:6969 recv data: {:?} from {}", - str::from_utf8(data).unwrap(), - endpoint - ); - Some(endpoint) - } - Err(_) => None, - }; - if let Some(endpoint) = client { - let data = b"hello\n"; + let client = match socket.recv() { + Ok((data, endpoint)) => { debug!( - "udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap() + "udp:6969 recv data: {:?} from {}", + str::from_utf8(data).unwrap(), + endpoint ); - socket.send_slice(data, endpoint).unwrap(); + Some(endpoint) } + Err(_) => None, + }; + if let Some(endpoint) = client { + let data = b"hello\n"; + debug!( + "udp:6969 send data: {:?}", + str::from_utf8(data.as_ref()).unwrap() + ); + socket.send_slice(data, endpoint).unwrap(); } phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ac7c43b0f..786089923 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -2942,13 +2942,11 @@ mod test { hop_limit: 0x40, }); - { - // Bind the socket to port 68 - let socket = iface.get_socket::(socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - } + // Bind the socket to port 68 + let socket = iface.get_socket::(socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); udp_repr.emit( &mut packet, @@ -2968,16 +2966,14 @@ mod test { Ok(None) ); - { - // Make sure the payload to the UDP packet processed by process_udp is - // appended to the bound sockets rx_buffer - let socket = iface.get_socket::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) - ); - } + // Make sure the payload to the UDP packet processed by process_udp is + // appended to the bound sockets rx_buffer + let socket = iface.get_socket::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) + ); } #[test] @@ -3194,10 +3190,8 @@ mod test { frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Arp); - { - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - } + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); let cx = iface.context(Instant::from_secs(0)); @@ -3255,15 +3249,13 @@ mod test { frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00])); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Ipv6); - { - ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - solicit.emit( - &remote_ip_addr.into(), - &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), - &ChecksumCapabilities::default(), - ); - } + ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); + solicit.emit( + &remote_ip_addr.into(), + &local_ip_addr.solicited_node().into(), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), + &ChecksumCapabilities::default(), + ); let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, @@ -3326,10 +3318,8 @@ mod test { frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Arp); - { - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - } + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); let cx = iface.context(Instant::from_secs(0)); @@ -3442,11 +3432,9 @@ mod test { let seq_no = 0x5432; let echo_data = &[0xff; 16]; - { - let socket = iface.get_socket::(socket_handle); - // Bind to the ID 0x1234 - assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(())); - } + let socket = iface.get_socket::(socket_handle); + // Bind to the ID 0x1234 + assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(())); // Ensure the ident we bound to and the ident of the packet are the same. let mut bytes = [0xff; 24]; @@ -3470,9 +3458,7 @@ mod test { // Open a socket and ensure the packet is handled due to the listening // socket. - { - assert!(!iface.get_socket::(socket_handle).can_recv()); - } + assert!(!iface.get_socket::(socket_handle).can_recv()); // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening let echo_reply = Icmpv4Repr::EchoReply { @@ -3493,17 +3479,15 @@ mod test { Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) ); - { - let socket = iface.get_socket::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok(( - icmp_data, - IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) - )) - ); - } + let socket = iface.get_socket::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok(( + icmp_data, + IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) + )) + ); } #[test] @@ -3854,13 +3838,12 @@ mod test { let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); let udp_socket_handle = iface.add_socket(udp_socket); - { - // Bind the socket to port 68 - let socket = iface.get_socket::(udp_socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - } + + // Bind the socket to port 68 + let socket = iface.get_socket::(udp_socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); let packets = 1; let raw_rx_buffer = @@ -3927,14 +3910,12 @@ mod test { Ok(None) ); - { - // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = iface.get_socket::(udp_socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) - ); - } + // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP + let socket = iface.get_socket::(udp_socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) + ); } } From d242ff483cb6ab0c4bbe73922ce71520ec9512e0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Oct 2021 04:18:03 +0200 Subject: [PATCH 262/566] socket/set: remove reference counting. It is never used in practice, so it's not worth the complexity and the extra RAM usage. --- src/iface/interface.rs | 2 +- src/socket/set.rs | 75 ++---------------------------------------- 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 786089923..a84f1c734 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -469,7 +469,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d>, { - /// Add a socket to the interface with the reference count 1, and return its handle. + /// Add a socket to the interface, and return its handle. /// /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. diff --git a/src/socket/set.rs b/src/socket/set.rs index cb96dfe51..3fda640d5 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -1,8 +1,6 @@ use core::{fmt, slice}; use managed::ManagedSlice; -#[cfg(feature = "socket-tcp")] -use crate::socket::TcpState; use crate::socket::{AnySocket, Socket}; /// An item of a socket set. @@ -12,7 +10,6 @@ use crate::socket::{AnySocket, Socket}; #[derive(Debug)] pub struct Item<'a> { socket: Socket<'a>, - refs: usize, } /// A handle, identifying a socket in a set. @@ -44,7 +41,7 @@ impl<'a> Set<'a> { Set { sockets } } - /// Add a socket to the set with the reference count 1, and return its handle. + /// Add a socket to the set, and return its handle. /// /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. @@ -56,7 +53,7 @@ impl<'a> Set<'a> { net_trace!("[{}]: adding", index); let handle = Handle(index); socket.meta_mut().handle = handle; - *slot = Some(Item { socket, refs: 1 }); + *slot = Some(Item { socket }); handle } @@ -105,74 +102,6 @@ impl<'a> Set<'a> { } } - /// Increase reference count by 1. - /// - /// # Panics - /// This function may panic if the handle does not belong to this socket set. - pub fn retain(&mut self, handle: Handle) { - self.sockets[handle.0] - .as_mut() - .expect("handle does not refer to a valid socket") - .refs += 1 - } - - /// Decrease reference count by 1. - /// - /// # Panics - /// This function may panic if the handle does not belong to this socket set, - /// or if the reference count is already zero. - pub fn release(&mut self, handle: Handle) { - let refs = &mut self.sockets[handle.0] - .as_mut() - .expect("handle does not refer to a valid socket") - .refs; - if *refs == 0 { - panic!("decreasing reference count past zero") - } - *refs -= 1 - } - - /// Prune the sockets in this set. - /// - /// Pruning affects sockets with reference count 0. Open sockets are closed. - /// Closed sockets are removed and dropped. - pub fn prune(&mut self) { - for (index, item) in self.sockets.iter_mut().enumerate() { - let mut may_remove = false; - if let Some(Item { - refs: 0, - ref mut socket, - }) = *item - { - match *socket { - #[cfg(feature = "socket-raw")] - Socket::Raw(_) => may_remove = true, - #[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") - ))] - Socket::Icmp(_) => may_remove = true, - #[cfg(feature = "socket-udp")] - Socket::Udp(_) => may_remove = true, - #[cfg(feature = "socket-tcp")] - Socket::Tcp(ref mut socket) => { - if socket.state() == TcpState::Closed { - may_remove = true - } else { - socket.close() - } - } - #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(_) => may_remove = true, - } - } - if may_remove { - net_trace!("[{}]: pruning", index); - *item = None - } - } - } - /// Iterate every socket in this set. pub fn iter<'d>(&'d self) -> Iter<'d, 'a> { Iter { From 21e3dad2b217136bd8d7fd51be572ab212471924 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Nov 2021 23:38:23 +0100 Subject: [PATCH 263/566] Fix clippys --- src/iface/interface.rs | 4 ++-- src/socket/dhcpv4.rs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a84f1c734..3a4f60562 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -3582,12 +3582,12 @@ mod test { #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { fn recv_igmp( - mut iface: &mut Interface<'_, Loopback>, + iface: &mut Interface<'_, Loopback>, timestamp: Instant, ) -> Vec<(Ipv4Repr, IgmpRepr)> { let caps = iface.device.capabilities(); let checksum_caps = &caps.checksum; - recv_all(&mut iface, timestamp) + recv_all(iface, timestamp) .iter() .filter_map(|frame| { let ipv4_packet = match caps.medium { diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index c166397a7..3472473b2 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -626,9 +626,7 @@ mod test { }); } - if i != reprs.len() { - panic!("Too few reprs emitted. Wanted {}, got {}", reprs.len(), i); - } + assert_eq!(i, reprs.len()); } macro_rules! send { From d9f114a2aba781fdaf5fc3368b17c62c6671d854 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 4 Nov 2021 11:33:26 +0100 Subject: [PATCH 264/566] Add fuzzing for IEEE802.15.4 Because IEEE802.15.4 uses a lot of compression in its frame, fuzzing it is maybe a good idea. Adding this fuzz target showed that some frame methods were panicking. `check_len` now checks if accessors will panic or not. I ran the fuzzer for about 15 minutes and nothing showed up. --- fuzz/Cargo.toml | 6 ++++++ fuzz/fuzz_targets/ieee802154_header.rs | 19 +++++++++++++++++++ src/wire/ieee802154.rs | 20 +++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 fuzz/fuzz_targets/ieee802154_header.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 0db822f8d..8cbb5db37 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -34,3 +34,9 @@ name = "dhcp_header" path = "fuzz_targets/dhcp_header.rs" test = false doc = false + +[[bin]] +name = "ieee802154_header" +path = "fuzz_targets/ieee802154_header.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/ieee802154_header.rs b/fuzz/fuzz_targets/ieee802154_header.rs new file mode 100644 index 000000000..af54f88fd --- /dev/null +++ b/fuzz/fuzz_targets/ieee802154_header.rs @@ -0,0 +1,19 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{Ieee802154Frame, Ieee802154Repr}; + +fuzz_target!(|data: &[u8]| { + if let Ok(ref frame) = Ieee802154Frame::new_checked(data) { + if let Ok(repr) = Ieee802154Repr::parse(frame) { + // The buffer len returns only the lenght required for emitting the header + // and does not take into account the length of the payload. + let mut buffer = vec![0; repr.buffer_len()]; + + // NOTE: unchecked because the checked version checks if the addressing mode field + // is valid or not. The addressing mode field is required for calculating the length of + // the header, which is used in `check_len`. + let mut frame = Ieee802154Frame::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + }; +}); diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index aca402e59..41df5e1ee 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -251,10 +251,24 @@ impl> Frame { pub fn check_len(&self) -> Result<()> { // We need at least 3 bytes if self.buffer.as_ref().len() < 3 { - Err(Error::Truncated) - } else { - Ok(()) + return Err(Error::Truncated); } + + let mut offset = field::ADDRESSING.start + 2; + + // Calculate the size of the addressing field. + offset += self.dst_addressing_mode().size(); + offset += self.src_addressing_mode().size(); + + if !self.pan_id_compression() { + offset += 2; + } + + if offset > self.buffer.as_ref().len() { + return Err(Error::Truncated); + } + + Ok(()) } /// Consumes the frame, returning the underlying buffer. From b540ec30b4e9b4e741d1fcb6451668ee8cfbe209 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 4 Nov 2021 19:10:33 +0100 Subject: [PATCH 265/566] Add fuzz tests for 6LoWPAN_IPHC and 6LoWPAN_UDP Adding fuzz tests for 6LoWPAN_IPHC and 6LoWPAN_UDP. Some bugs were found. Ran for 10 minutes. --- fuzz/Cargo.toml | 13 ++++++ fuzz/fuzz_targets/sixlowpan_iphc_header.rs | 42 +++++++++++++++++++ fuzz/fuzz_targets/sixlowpan_udp_header.rs | 43 +++++++++++++++++++ src/wire/sixlowpan.rs | 49 +++++++++++++++++----- 4 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 fuzz/fuzz_targets/sixlowpan_iphc_header.rs create mode 100644 fuzz/fuzz_targets/sixlowpan_udp_header.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8cbb5db37..98bd4d0f8 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,6 +10,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } getopts = "0.2" smoltcp = { path = "..", features = [ "medium-ethernet" ] } @@ -40,3 +41,15 @@ name = "ieee802154_header" path = "fuzz_targets/ieee802154_header.rs" test = false doc = false + +[[bin]] +name = "sixlowpan_udp_header" +path = "fuzz_targets/sixlowpan_udp_header.rs" +test = false +doc = false + +[[bin]] +name = "sixlowpan_iphc_header" +path = "fuzz_targets/sixlowpan_iphc_header.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/sixlowpan_iphc_header.rs b/fuzz/fuzz_targets/sixlowpan_iphc_header.rs new file mode 100644 index 000000000..3ed012b0c --- /dev/null +++ b/fuzz/fuzz_targets/sixlowpan_iphc_header.rs @@ -0,0 +1,42 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{Ieee802154Address, SixlowpanIphcPacket, SixlowpanIphcRepr}; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] +pub enum AddressFuzzer { + Absent, + Short([u8; 2]), + Extended([u8; 8]), +} + +impl From for Ieee802154Address { + fn from(val: AddressFuzzer) -> Self { + match val { + AddressFuzzer::Absent => Ieee802154Address::Absent, + AddressFuzzer::Short(b) => Ieee802154Address::Short(b), + AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b), + } + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct SixlowpanIphcPacketFuzzer<'a> { + data: &'a [u8], + ll_src_addr: Option, + ll_dst_addr: Option, +} + +fuzz_target!(|fuzz: SixlowpanIphcPacketFuzzer| { + if let Ok(ref frame) = SixlowpanIphcPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanIphcRepr::parse( + frame, + fuzz.ll_src_addr.map(Into::into), + fuzz.ll_dst_addr.map(Into::into), + ) { + let mut buffer = vec![0; repr.buffer_len()]; + + let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + }; +}); diff --git a/fuzz/fuzz_targets/sixlowpan_udp_header.rs b/fuzz/fuzz_targets/sixlowpan_udp_header.rs new file mode 100644 index 000000000..9a2079b4d --- /dev/null +++ b/fuzz/fuzz_targets/sixlowpan_udp_header.rs @@ -0,0 +1,43 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{Ipv6Address, SixlowpanUdpPacket, SixlowpanUdpRepr}; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] +pub struct AddressFuzzer(pub [u8; 16]); + +impl From for Ipv6Address { + fn from(val: AddressFuzzer) -> Self { + Ipv6Address(val.0) + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct SixlowpanUdpPacketFuzzer<'a> { + data: &'a [u8], + src_addr: AddressFuzzer, + dst_addr: AddressFuzzer, + checksum: Option, +} + +fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| { + if let Ok(ref frame) = SixlowpanUdpPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanUdpRepr::parse( + frame, + &fuzz.src_addr.into(), + &fuzz.dst_addr.into(), + fuzz.checksum, + ) { + let payload = frame.payload(); + let mut buffer = vec![0; repr.header_len() + payload.len()]; + + let mut frame = SixlowpanUdpPacket::new_unchecked(&mut buffer[..]); + repr.emit( + &mut frame, + &fuzz.src_addr.into(), + &fuzz.dst_addr.into(), + payload.len(), + |b| b.copy_from_slice(payload), + ); + } + }; +}); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 3ca55abcd..ae605f743 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -128,10 +128,21 @@ pub mod iphc { pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.len() < 2 { - Err(Error::Truncated) - } else { - Ok(()) + return Err(Error::Truncated); } + + let mut offset = self.ip_fields_start() + + self.traffic_class_size() + + self.next_header_size() + + self.hop_limit_size(); + offset += self.src_address_size(); + offset += self.dst_address_size(); + + if offset as usize > buffer.len() { + return Err(Error::Truncated); + } + + Ok(()) } /// Consumes the frame, returning the underlying buffer. @@ -797,6 +808,12 @@ pub mod iphc { 1 // The next header field is inlined }; + // Hop Limit size + len += match self.hop_limit { + 255 | 64 | 1 => 0, // We can inline the hop limit + _ => 1, + }; + // Add the lenght of the source address len += if self.src_addr == ipv6::Address::UNSPECIFIED { 0 @@ -1279,11 +1296,17 @@ pub mod nhc { /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); + if buffer.is_empty() { - Err(Error::Truncated) - } else { - Ok(()) + return Err(Error::Truncated); + } + + let index = 1 + self.ports_size() + self.checksum_size(); + if index > buffer.len() { + return Err(Error::Truncated); } + + Ok(()) } /// Consumes the frame, returning the underlying buffer. @@ -1357,7 +1380,7 @@ pub mod nhc { let data = self.buffer.as_ref(); let start = self.nhc_fields_start(); - 0xf0b0 + (NetworkEndian::read_u16(&data[start..start + 1]) & 0xff) + 0xf0b0 + (data[start] & 0xff) as u16 } _ => unreachable!(), } @@ -1501,10 +1524,14 @@ pub mod nhc { checksum::data(packet.payload()), ]); - // TODO(thvdveld): remove the unwrap - if chk_sum != packet.checksum().unwrap() { - return Err(Error::Checksum); - } + if let Some(checksum) = packet.checksum() { + if chk_sum != checksum { + return Err(Error::Checksum); + } + } else { + net_trace!("Currently we do not support ellided checksums."); + return Err(Error::Unrecognized); + }; Ok(UdpNhcRepr(UdpRepr { src_port: packet.src_port(), From 1a28ef563c1db7f2058e668cf14a227e5dfae892 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 14 Nov 2021 09:46:58 +0100 Subject: [PATCH 266/566] Expose `self.sockets` in `Interface` via iterators This is needed to not loose access to methods on sockets, e.g. iterating over them and closing or checking which ports are being used. --- src/iface/interface.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 3a4f60562..50f9d0f99 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -555,6 +555,16 @@ where &mut self.device } + /// Get an iterator to the inner sockets. + pub fn sockets(&self) -> impl Iterator> { + self.sockets.iter() + } + + /// Get a mutable iterator to the inner sockets. + pub fn sockets_mut(&mut self) -> impl Iterator> { + self.sockets.iter_mut() + } + /// Add an address to a list of subscribed multicast IP addresses. /// /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent` From 7f5747aecd7b26acf8e8ee865f06d24e513c97a4 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 15 Nov 2021 16:23:28 +0100 Subject: [PATCH 267/566] Support defmt version 0.3 with new MSRV --- .github/workflows/test.yml | 6 +++--- Cargo.toml | 8 +------- src/wire/ieee802154.rs | 3 +++ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36e4732b0..2b19a3889 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.53.0 + - 1.56.0 - nightly features: @@ -68,13 +68,13 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.53.0 + - 1.56.0 - nightly features: # These feature sets cannot run tests, so we only check they build. - rand-custom-impl medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 633752d30..185b6bd73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ byteorder = { version = "1.0", default-features = false } log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } -defmt = { version = "0.2.0", optional = true } +defmt = { version = "0.3", optional = true } rand_core = { version = "0.6.3", optional = true, default-features = false } [dev-dependencies] @@ -57,12 +57,6 @@ rand-custom-impl = [] "async" = [] -defmt-trace = [] -defmt-debug = [] -defmt-info = [] -defmt-warn = [] -defmt-error = [] - default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", "medium-ieee802154", diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 41df5e1ee..2793a0951 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -67,6 +67,7 @@ impl fmt::Display for AddressingMode { /// A IEEE 802.15.4 PAN. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Pan(pub u16); impl Pan { @@ -82,6 +83,7 @@ impl Pan { /// A IEEE 802.15.4 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Address { Absent, Short([u8; 2]), @@ -745,6 +747,7 @@ impl> fmt::Display for Frame { /// A high-level representation of an IEEE802.15.4 frame. #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub frame_type: FrameType, pub security_enabled: bool, From 92676b9b674607efc424569fb39b0be5c7b3b1ad Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Nov 2021 19:59:22 +0100 Subject: [PATCH 268/566] Fix build when enabling only medium-ip --- .github/workflows/test.yml | 10 +++++----- src/iface/interface.rs | 27 +++++++++++++++++---------- src/phy/mod.rs | 8 ++------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b19a3889..d46e011d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ on: push: - branches: [ staging, trying ] + branches: [staging, trying] pull_request: name: Test @@ -37,13 +37,13 @@ jobs: - std medium-ethernet proto-ipv4 proto-igmp socket-raw - std medium-ethernet proto-ipv4 socket-udp socket-tcp - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp - - std medium-ethernet proto-ipv6 socket-udp + - std medium-ethernet medium-ip medium-ieee802154 proto-ipv6 socket-udp - std medium-ethernet proto-ipv6 socket-tcp - - std medium-ethernet proto-ipv4 socket-icmp socket-tcp - - std medium-ethernet proto-ipv6 socket-icmp socket-tcp + - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp + - std medium-ip proto-ipv6 socket-icmp socket-tcp # Test features chosen to be as aggressive as possible. - - std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async + - std medium-ethernet medium-ip medium-ieee802154 proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async include: # Test alloc feature which requires nightly. diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 50f9d0f99..1589331c4 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -253,6 +253,7 @@ let iface = InterfaceBuilder::new(device, vec![]) pub fn finalize(self) -> Interface<'a, DeviceT> { let device_capabilities = self.device.capabilities(); + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] let (hardware_addr, neighbor_cache) = match device_capabilities.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => ( @@ -2275,6 +2276,7 @@ impl<'a> InterfaceInner<'a> { } fn flush_cache(&mut self) { + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] if let Some(cache) = self.neighbor_cache.as_mut() { cache.flush() } @@ -2394,16 +2396,17 @@ impl<'a> InterfaceInner<'a> { _ => return Err(Error::Unaddressable), }; - let next_header = match &packet { - IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, - IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), - _ => return Err(Error::Unrecognized), - }; - - let hop_limit = match packet { - IpPacket::Icmpv6((_, Icmpv6Repr::Ndisc(_))) => 255, - IpPacket::Icmpv6((_, Icmpv6Repr::EchoReply { .. })) => 64, - IpPacket::Udp(..) => 64, + #[allow(unreachable_patterns)] + let (next_header, hop_limit) = match &packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp(_) => (SixlowpanNextHeader::Compressed, 64), + IpPacket::Icmpv6((_, repr)) => ( + SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), + match repr { + Icmpv6Repr::Ndisc(_) => 255, + _ => 64, + }, + ), _ => return Err(Error::Unrecognized), }; @@ -2419,7 +2422,9 @@ impl<'a> InterfaceInner<'a> { tx_len += ieee_repr.buffer_len(); tx_len += iphc_repr.buffer_len(); + #[allow(unreachable_patterns)] match &packet { + #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udp_repr, payload)) => { let udp_repr = SixlowpanUdpRepr(*udp_repr); tx_len += udp_repr.header_len() + payload.len(); @@ -2443,6 +2448,7 @@ impl<'a> InterfaceInner<'a> { iphc_repr.emit(&mut iphc_packet); start += iphc_repr.buffer_len(); + #[allow(unreachable_patterns)] match packet { IpPacket::Udp((_, udp_repr, payload)) => { // 3. Create the header for 6LoWPAN UDP @@ -2457,6 +2463,7 @@ impl<'a> InterfaceInner<'a> { |buf| buf.copy_from_slice(payload), ); } + #[cfg(feature = "proto-ipv6")] IpPacket::Icmpv6((_, icmp_repr)) => { // 3. Create the header for ICMPv6 let mut icmp_packet = diff --git a/src/phy/mod.rs b/src/phy/mod.rs index ae4a11819..3a7799dab 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -286,11 +286,7 @@ impl Default for Medium { fn default() -> Medium { #[cfg(feature = "medium-ethernet")] return Medium::Ethernet; - #[cfg(all( - feature = "medium-ip", - not(feature = "medium-ethernet"), - not(feature = "medium-ieee802154") - ))] + #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))] return Medium::Ip; #[cfg(all( feature = "medium-ieee802154", @@ -303,7 +299,7 @@ impl Default for Medium { not(feature = "medium-ethernet"), not(feature = "medium-ieee802154") ))] - panic!("No medium enabled"); + return panic!("No medium enabled"); } } From 20124dbfd43b0e9738db7627b161bb52065e2f1a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 19:30:19 +0100 Subject: [PATCH 269/566] socket: cleanup some cfg's --- src/iface/interface.rs | 10 ++------- src/lib.rs | 3 ++- src/socket/mod.rs | 49 +++++++++++++++--------------------------- 3 files changed, 21 insertions(+), 41 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 1589331c4..51e24d8c4 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -867,10 +867,7 @@ where Socket::Raw(ref mut socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Raw(response))) } - #[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") - ))] + #[cfg(feature = "socket-icmp")] Socket::Icmp(ref mut socket) => socket.dispatch(cx, |response| match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { @@ -1325,10 +1322,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(all( - any(feature = "proto-ipv4", feature = "proto-ipv6"), - feature = "socket-raw" - ))] + #[cfg(feature = "socket-raw")] fn raw_socket_filter<'frame>( &mut self, cx: &Context, diff --git a/src/lib.rs b/src/lib.rs index b8653cf0b..5a951d6ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,9 +111,10 @@ compile_error!("You must enable at least one of the following features: proto-ip feature = "socket-udp", feature = "socket-tcp", feature = "socket-icmp", + feature = "socket-dhcp", )) ))] -compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp"); +compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp, socket-dhcp"); #[cfg(all( feature = "socket", diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 30262b048..a2b478be4 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -14,17 +14,15 @@ size for a buffer, allocate it, and let the networking stack use it. use crate::phy::DeviceCapabilities; use crate::time::Instant; +mod meta; +mod set; + #[cfg(feature = "socket-dhcpv4")] mod dhcpv4; -#[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") -))] +#[cfg(feature = "socket-icmp")] mod icmp; -mod meta; #[cfg(feature = "socket-raw")] mod raw; -mod set; #[cfg(feature = "socket-tcp")] mod tcp; #[cfg(feature = "socket-udp")] @@ -34,29 +32,22 @@ mod udp; mod waker; pub(crate) use self::meta::Meta as SocketMeta; -#[cfg(feature = "async")] -pub(crate) use self::waker::WakerRegistration; +pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; +pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; +#[cfg(feature = "socket-dhcpv4")] +pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; +#[cfg(feature = "socket-icmp")] +pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; #[cfg(feature = "socket-raw")] pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer}; - -#[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") -))] -pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; - -#[cfg(feature = "socket-udp")] -pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; - #[cfg(feature = "socket-tcp")] pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket}; +#[cfg(feature = "socket-udp")] +pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; -#[cfg(feature = "socket-dhcpv4")] -pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; - -pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; -pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; +#[cfg(feature = "async")] +pub(crate) use self::waker::WakerRegistration; /// Gives an indication on the next time the socket should be polled. #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] @@ -84,10 +75,7 @@ pub(crate) enum PollAt { pub enum Socket<'a> { #[cfg(feature = "socket-raw")] Raw(RawSocket<'a>), - #[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") - ))] + #[cfg(feature = "socket-icmp")] Icmp(IcmpSocket<'a>), #[cfg(feature = "socket-udp")] Udp(UdpSocket<'a>), @@ -108,7 +96,7 @@ macro_rules! dispatch_socket { match $self_ { #[cfg(feature = "socket-raw")] &$( $mut_ )* Socket::Raw(ref $( $mut_ )* $socket) => $code, - #[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] + #[cfg(feature = "socket-icmp")] &$( $mut_ )* Socket::Icmp(ref $( $mut_ )* $socket) => $code, #[cfg(feature = "socket-udp")] &$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code, @@ -161,10 +149,7 @@ macro_rules! from_socket { #[cfg(feature = "socket-raw")] from_socket!(RawSocket<'a>, Raw); -#[cfg(all( - feature = "socket-icmp", - any(feature = "proto-ipv4", feature = "proto-ipv6") -))] +#[cfg(feature = "socket-icmp")] from_socket!(IcmpSocket<'a>, Icmp); #[cfg(feature = "socket-udp")] from_socket!(UdpSocket<'a>, Udp); From 9372561ea141a9a8f7d136e72a280de889539dd9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 22:23:30 +0100 Subject: [PATCH 270/566] iface: do not compile at all if no medium is enabled. --- src/iface/mod.rs | 10 ---------- src/lib.rs | 6 ++++++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/iface/mod.rs b/src/iface/mod.rs index f1c6313cd..bea35ca6e 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,11 +4,6 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ -#[cfg(any( - feature = "medium-ethernet", - feature = "medium-ip", - feature = "medium-ieee802154" -))] mod interface; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] mod neighbor; @@ -22,9 +17,4 @@ pub use self::neighbor::Cache as NeighborCache; pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; -#[cfg(any( - feature = "medium-ethernet", - feature = "medium-ip", - feature = "medium-ieee802154" -))] pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/lib.rs b/src/lib.rs index 5a951d6ca..cbe9d8b08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,7 +139,13 @@ mod rand; #[cfg(feature = "rand-custom-impl")] pub use crate::rand::Rand; +#[cfg(any( + feature = "medium-ethernet", + feature = "medium-ip", + feature = "medium-ieee802154" +))] pub mod iface; + pub mod phy; #[cfg(feature = "socket")] pub mod socket; From d1098f530699f5fec6152b9cd78cc92436fc2245 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 22:34:51 +0100 Subject: [PATCH 271/566] 6lowpan: support compiling with sixlowpan enabled but socket-udp disabled. --- src/iface/interface.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 51e24d8c4..600df590c 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1198,6 +1198,12 @@ impl<'a> InterfaceInner<'a> { net_debug!("Extension headers are currently not supported for 6LoWPAN"); Ok(None) } + #[cfg(not(feature = "socket-udp"))] + SixlowpanNhcPacket::UdpHeader(_) => { + net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); + Ok(None) + } + #[cfg(feature = "socket-udp")] SixlowpanNhcPacket::UdpHeader(udp_packet) => { ipv6_repr.next_header = IpProtocol::Udp; // Handle the UDP @@ -2444,6 +2450,7 @@ impl<'a> InterfaceInner<'a> { #[allow(unreachable_patterns)] match packet { + #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udp_repr, payload)) => { // 3. Create the header for 6LoWPAN UDP let mut udp_packet = From ff47259603df2ac88d81f9d54d7c1114446533c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 22:04:23 +0100 Subject: [PATCH 272/566] socket: move meta from XxxSocket to SocketSetItem. --- src/iface/interface.rs | 74 +++++++++++------- src/socket/dhcpv4.rs | 19 +---- src/socket/icmp.rs | 36 ++------- src/socket/meta.rs | 2 +- src/socket/mod.rs | 49 ++++-------- src/socket/raw.rs | 32 ++------ src/socket/set.rs | 75 ++++-------------- src/socket/tcp.rs | 172 +++++++++++++---------------------------- src/socket/udp.rs | 33 ++------ 9 files changed, 148 insertions(+), 344 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 600df590c..254e177e4 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -474,10 +474,7 @@ where /// /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. - pub fn add_socket(&mut self, socket: T) -> SocketHandle - where - T: Into>, - { + pub fn add_socket>(&mut self, socket: T) -> SocketHandle { self.sockets.add(socket) } @@ -558,12 +555,12 @@ where /// Get an iterator to the inner sockets. pub fn sockets(&self) -> impl Iterator> { - self.sockets.iter() + self.sockets.iter().map(|i| &i.socket) } /// Get a mutable iterator to the inner sockets. pub fn sockets_mut(&mut self) -> impl Iterator> { - self.sockets.iter_mut() + self.sockets.iter_mut().map(|i| &mut i.socket) } /// Add an address to a list of subscribed multicast IP addresses. @@ -733,9 +730,9 @@ where self.sockets .iter() - .filter_map(|socket| { - let socket_poll_at = socket.poll_at(&cx); - match socket.meta().poll_at(socket_poll_at, |ip_addr| { + .filter_map(|item| { + let socket_poll_at = item.socket.poll_at(&cx); + match item.meta.poll_at(socket_poll_at, |ip_addr| { self.inner.has_neighbor(&cx, &ip_addr) }) { PollAt::Ingress => None, @@ -841,9 +838,9 @@ where let _caps = device.capabilities(); let mut emitted_any = false; - for socket in sockets.iter_mut() { - if !socket - .meta_mut() + for item in sockets.iter_mut() { + if !item + .meta .egress_permitted(cx.now, |ip_addr| inner.has_neighbor(cx, &ip_addr)) { continue; @@ -862,13 +859,13 @@ where }}; } - let socket_result = match *socket { + let socket_result = match &mut item.socket { #[cfg(feature = "socket-raw")] - Socket::Raw(ref mut socket) => { + Socket::Raw(socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Raw(response))) } #[cfg(feature = "socket-icmp")] - Socket::Icmp(ref mut socket) => socket.dispatch(cx, |response| match response { + Socket::Icmp(socket) => socket.dispatch(cx, |response| match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { respond!(IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) @@ -880,15 +877,15 @@ where _ => Err(Error::Unaddressable), }), #[cfg(feature = "socket-udp")] - Socket::Udp(ref mut socket) => { + Socket::Udp(socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Udp(response))) } #[cfg(feature = "socket-tcp")] - Socket::Tcp(ref mut socket) => { + Socket::Tcp(socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Tcp(response))) } #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(ref mut socket) => { + Socket::Dhcpv4(socket) => { socket.dispatch(cx, |response| respond!(IpPacket::Dhcpv4(response))) } }; @@ -901,15 +898,14 @@ where // 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() + item.meta .neighbor_missing(cx.now, neighbor_addr.expect("non-IP response packet")); break; } (Err(err), _) | (_, Err(err)) => { net_debug!( "{}: cannot dispatch egress packet: {}", - socket.meta().handle, + item.meta.handle, err ); return Err(err); @@ -1216,7 +1212,10 @@ impl<'a> InterfaceInner<'a> { // Look for UDP sockets that will accept the UDP packet. // If it does not accept the packet, then send an ICMP message. - for udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { + for udp_socket in sockets + .iter_mut() + .filter_map(|i| UdpSocket::downcast(&mut i.socket)) + { if !udp_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { continue; } @@ -1339,7 +1338,10 @@ impl<'a> InterfaceInner<'a> { let mut handled_by_raw_socket = false; // Pass every IP packet to all raw sockets we have registered. - for raw_socket in sockets.iter_mut().filter_map(RawSocket::downcast) { + for raw_socket in sockets + .iter_mut() + .filter_map(|i| RawSocket::downcast(&mut i.socket)) + { if !raw_socket.accepts(ip_repr) { continue; } @@ -1471,8 +1473,10 @@ impl<'a> InterfaceInner<'a> { if udp_packet.src_port() == DHCP_SERVER_PORT && udp_packet.dst_port() == DHCP_CLIENT_PORT { - if let Some(dhcp_socket) = - sockets.iter_mut().filter_map(Dhcpv4Socket::downcast).next() + if let Some(dhcp_socket) = sockets + .iter_mut() + .filter_map(|i| Dhcpv4Socket::downcast(&mut i.socket)) + .next() { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_repr = @@ -1650,7 +1654,10 @@ impl<'a> InterfaceInner<'a> { let mut handled_by_icmp_socket = false; #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] - for icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) { + for icmp_socket in _sockets + .iter_mut() + .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) + { if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue; } @@ -1836,7 +1843,10 @@ impl<'a> InterfaceInner<'a> { let mut handled_by_icmp_socket = false; #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] - for icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) { + for icmp_socket in _sockets + .iter_mut() + .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) + { if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { continue; } @@ -1960,7 +1970,10 @@ impl<'a> InterfaceInner<'a> { let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; let udp_payload = udp_packet.payload(); - for udp_socket in sockets.iter_mut().filter_map(UdpSocket::downcast) { + for udp_socket in sockets + .iter_mut() + .filter_map(|i| UdpSocket::downcast(&mut i.socket)) + { if !udp_socket.accepts(&ip_repr, &udp_repr) { continue; } @@ -2017,7 +2030,10 @@ impl<'a> InterfaceInner<'a> { let tcp_packet = TcpPacket::new_checked(ip_payload)?; let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; - for tcp_socket in sockets.iter_mut().filter_map(TcpSocket::downcast) { + for tcp_socket in sockets + .iter_mut() + .filter_map(|i| TcpSocket::downcast(&mut i.socket)) + { if !tcp_socket.accepts(&ip_repr, &tcp_repr) { continue; } diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 3472473b2..7462cbe67 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -1,5 +1,4 @@ -use crate::socket::SocketHandle; -use crate::socket::{Context, SocketMeta}; +use crate::socket::Context; use crate::time::{Duration, Instant}; use crate::wire::dhcpv4::field as dhcpv4_field; use crate::wire::HardwareAddress; @@ -9,7 +8,7 @@ use crate::wire::{ }; use crate::{Error, Result}; -use super::{PollAt, Socket}; +use super::PollAt; const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10); @@ -112,7 +111,6 @@ pub enum Event { #[derive(Debug)] pub struct Dhcpv4Socket { - pub(crate) meta: SocketMeta, /// State of the DHCP client. state: ClientState, /// Set to true on config/state change, cleared back to false by the `config` function. @@ -138,7 +136,6 @@ impl Dhcpv4Socket { #[allow(clippy::new_without_default)] pub fn new() -> Self { Dhcpv4Socket { - meta: SocketMeta::default(), state: ClientState::Discovering(DiscoverState { retry_at: Instant::from_millis(0), }), @@ -520,12 +517,6 @@ impl Dhcpv4Socket { } } - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta.handle - } - /// Reset state and restart discovery phase. /// /// Use this to speed up acquisition of an address in a new @@ -557,12 +548,6 @@ impl Dhcpv4Socket { } } -impl<'a> From for Socket<'a> { - fn from(val: Dhcpv4Socket) -> Self { - Socket::Dhcpv4(val) - } -} - #[cfg(test)] mod test { diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index acdee1204..91fb8f55e 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -5,7 +5,7 @@ use core::task::Waker; use crate::phy::ChecksumCapabilities; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::socket::{Context, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; use crate::{Error, Result}; @@ -62,7 +62,6 @@ pub type IcmpSocketBuffer<'a> = PacketBuffer<'a, IpAddress>; /// [bind]: #method.bind #[derive(Debug)] pub struct IcmpSocket<'a> { - pub(crate) meta: SocketMeta, rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>, /// The endpoint this socket is communicating with @@ -79,7 +78,6 @@ impl<'a> IcmpSocket<'a> { /// Create an ICMP socket with the given buffers. pub fn new(rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> { IcmpSocket { - meta: SocketMeta::default(), rx_buffer: rx_buffer, tx_buffer: tx_buffer, endpoint: Endpoint::default(), @@ -126,12 +124,6 @@ impl<'a> IcmpSocket<'a> { self.tx_waker.register(waker) } - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta.handle - } - /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. /// /// See also the [set_hop_limit](#method.set_hop_limit) method @@ -290,12 +282,7 @@ impl<'a> IcmpSocket<'a> { let packet_buf = self.tx_buffer.enqueue(size, endpoint)?; - net_trace!( - "{}:{}: buffer to send {} octets", - self.meta.handle, - endpoint, - size - ); + net_trace!("icmp:{}: buffer to send {} octets", endpoint, size); Ok(packet_buf) } @@ -316,8 +303,7 @@ impl<'a> IcmpSocket<'a> { let (endpoint, packet_buf) = self.rx_buffer.dequeue()?; net_trace!( - "{}:{}: receive {} buffered octets", - self.meta.handle, + "icmp:{}: receive {} buffered octets", endpoint, packet_buf.len() ); @@ -417,8 +403,7 @@ impl<'a> IcmpSocket<'a> { ); net_trace!( - "{}:{}: receiving {} octets", - self.meta.handle, + "icmp:{}: receiving {} octets", icmp_repr.buffer_len(), packet_buf.len() ); @@ -436,8 +421,7 @@ impl<'a> IcmpSocket<'a> { ); net_trace!( - "{}:{}: receiving {} octets", - self.meta.handle, + "icmp:{}: receiving {} octets", icmp_repr.buffer_len(), packet_buf.len() ); @@ -454,12 +438,10 @@ impl<'a> IcmpSocket<'a> { where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>, { - let handle = self.meta.handle; let hop_limit = self.hop_limit.unwrap_or(64); self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { net_trace!( - "{}:{}: sending {} octets", - handle, + "icmp:{}: sending {} octets", remote_endpoint, packet_buf.len() ); @@ -515,12 +497,6 @@ impl<'a> IcmpSocket<'a> { } } -impl<'a> From> for Socket<'a> { - fn from(val: IcmpSocket<'a>) -> Self { - Socket::Icmp(val) - } -} - #[cfg(test)] mod tests_common { pub use super::*; diff --git a/src/socket/meta.rs b/src/socket/meta.rs index a6908a1d9..a1e51d811 100644 --- a/src/socket/meta.rs +++ b/src/socket/meta.rs @@ -31,7 +31,7 @@ impl Default for NeighborState { /// is interested in, but which are more conveniently stored inside the socket itself. #[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Meta { +pub(crate) struct Meta { /// Handle of this socket within its enclosing `SocketSet`. /// Mainly useful for debug output. pub(crate) handle: SocketHandle, diff --git a/src/socket/mod.rs b/src/socket/mod.rs index a2b478be4..fcfbee1f7 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -31,9 +31,7 @@ mod udp; #[cfg(feature = "async")] mod waker; -pub(crate) use self::meta::Meta as SocketMeta; pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; -pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut}; #[cfg(feature = "socket-dhcpv4")] pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; @@ -85,57 +83,36 @@ pub enum Socket<'a> { Dhcpv4(Dhcpv4Socket), } -macro_rules! dispatch_socket { - ($self_:expr, |$socket:ident| $code:expr) => { - dispatch_socket!(@inner $self_, |$socket| $code) - }; - (mut $self_:expr, |$socket:ident| $code:expr) => { - dispatch_socket!(@inner mut $self_, |$socket| $code) - }; - (@inner $( $mut_:ident )* $self_:expr, |$socket:ident| $code:expr) => { - match $self_ { +impl<'a> Socket<'a> { + pub(crate) fn poll_at(&self, cx: &Context) -> PollAt { + match self { #[cfg(feature = "socket-raw")] - &$( $mut_ )* Socket::Raw(ref $( $mut_ )* $socket) => $code, + Socket::Raw(s) => s.poll_at(cx), #[cfg(feature = "socket-icmp")] - &$( $mut_ )* Socket::Icmp(ref $( $mut_ )* $socket) => $code, + Socket::Icmp(s) => s.poll_at(cx), #[cfg(feature = "socket-udp")] - &$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code, + Socket::Udp(s) => s.poll_at(cx), #[cfg(feature = "socket-tcp")] - &$( $mut_ )* Socket::Tcp(ref $( $mut_ )* $socket) => $code, + Socket::Tcp(s) => s.poll_at(cx), #[cfg(feature = "socket-dhcpv4")] - &$( $mut_ )* Socket::Dhcpv4(ref $( $mut_ )* $socket) => $code, + Socket::Dhcpv4(s) => s.poll_at(cx), } - }; -} - -impl<'a> Socket<'a> { - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta().handle - } - - pub(crate) fn meta(&self) -> &SocketMeta { - dispatch_socket!(self, |socket| &socket.meta) - } - - pub(crate) fn meta_mut(&mut self) -> &mut SocketMeta { - dispatch_socket!(mut self, |socket| &mut socket.meta) - } - - pub(crate) fn poll_at(&self, cx: &Context) -> PollAt { - dispatch_socket!(self, |socket| socket.poll_at(cx)) } } /// A conversion trait for network sockets. pub trait AnySocket<'a>: Sized { + fn upcast(self) -> Socket<'a>; fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; } macro_rules! from_socket { ($socket:ty, $variant:ident) => { impl<'a> AnySocket<'a> for $socket { + fn upcast(self) -> Socket<'a> { + Socket::$variant(self) + } + fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> { #[allow(unreachable_patterns)] match socket { diff --git a/src/socket/raw.rs b/src/socket/raw.rs index acd61bf49..4514c97ba 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -5,7 +5,7 @@ use core::task::Waker; use crate::phy::ChecksumCapabilities; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::socket::{Context, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; use crate::{Error, Result}; @@ -27,7 +27,6 @@ pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>; /// transmit and receive packet buffers. #[derive(Debug)] pub struct RawSocket<'a> { - pub(crate) meta: SocketMeta, ip_version: IpVersion, ip_protocol: IpProtocol, rx_buffer: RawSocketBuffer<'a>, @@ -48,7 +47,6 @@ impl<'a> RawSocket<'a> { tx_buffer: RawSocketBuffer<'a>, ) -> RawSocket<'a> { RawSocket { - meta: SocketMeta::default(), ip_version, ip_protocol, rx_buffer, @@ -95,12 +93,6 @@ impl<'a> RawSocket<'a> { self.tx_waker.register(waker) } - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta.handle - } - /// Return the IP version the socket is bound to. #[inline] pub fn ip_version(&self) -> IpVersion { @@ -164,8 +156,7 @@ impl<'a> RawSocket<'a> { let packet_buf = self.tx_buffer.enqueue(size, ())?; net_trace!( - "{}:{}:{}: buffer to send {} octets", - self.meta.handle, + "raw:{}:{}: buffer to send {} octets", self.ip_version, self.ip_protocol, packet_buf.len() @@ -191,8 +182,7 @@ impl<'a> RawSocket<'a> { let ((), packet_buf) = self.rx_buffer.dequeue()?; net_trace!( - "{}:{}:{}: receive {} buffered octets", - self.meta.handle, + "raw:{}:{}: receive {} buffered octets", self.ip_version, self.ip_protocol, packet_buf.len() @@ -231,8 +221,7 @@ impl<'a> RawSocket<'a> { packet_buf[header_len..].copy_from_slice(payload); net_trace!( - "{}:{}:{}: receiving {} octets", - self.meta.handle, + "raw:{}:{}: receiving {} octets", self.ip_version, self.ip_protocol, packet_buf.len() @@ -286,15 +275,13 @@ impl<'a> RawSocket<'a> { } } - let handle = self.meta.handle; 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, &cx.caps.checksum) { Ok((ip_repr, raw_packet)) => { net_trace!( - "{}:{}:{}: sending {} octets", - handle, + "raw:{}:{}: sending {} octets", ip_version, ip_protocol, ip_repr.buffer_len() + raw_packet.len() @@ -303,8 +290,7 @@ impl<'a> RawSocket<'a> { } Err(error) => { net_debug!( - "{}:{}:{}: dropping outgoing packet ({})", - handle, + "raw:{}:{}: dropping outgoing packet ({})", ip_version, ip_protocol, error @@ -330,12 +316,6 @@ impl<'a> RawSocket<'a> { } } -impl<'a> From> for Socket<'a> { - fn from(val: RawSocket<'a>) -> Self { - Socket::Raw(val) - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/socket/set.rs b/src/socket/set.rs index 3fda640d5..66b017df4 100644 --- a/src/socket/set.rs +++ b/src/socket/set.rs @@ -1,15 +1,18 @@ -use core::{fmt, slice}; +use core::fmt; use managed::ManagedSlice; use crate::socket::{AnySocket, Socket}; +use super::meta::Meta; + /// An item of a socket set. /// /// The only reason this struct is public is to allow the socket set storage /// to be allocated externally. #[derive(Debug)] pub struct Item<'a> { - socket: Socket<'a>, + pub(crate) meta: Meta, + pub(crate) socket: Socket<'a>, } /// A handle, identifying a socket in a set. @@ -45,19 +48,17 @@ impl<'a> Set<'a> { /// /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. - pub fn add(&mut self, socket: T) -> Handle - where - T: Into>, - { - fn put<'a>(index: usize, slot: &mut Option>, mut socket: Socket<'a>) -> Handle { + pub fn add>(&mut self, socket: T) -> Handle { + fn put<'a>(index: usize, slot: &mut Option>, socket: Socket<'a>) -> Handle { net_trace!("[{}]: adding", index); let handle = Handle(index); - socket.meta_mut().handle = handle; - *slot = Some(Item { socket }); + let mut meta = Meta::default(); + meta.handle = handle; + *slot = Some(Item { socket, meta }); handle } - let socket = socket.into(); + let socket = socket.upcast(); for (index, slot) in self.sockets.iter_mut().enumerate() { if slot.is_none() { @@ -103,58 +104,12 @@ impl<'a> Set<'a> { } /// Iterate every socket in this set. - pub fn iter<'d>(&'d self) -> Iter<'d, 'a> { - Iter { - lower: self.sockets.iter(), - } + pub fn iter(&self) -> impl Iterator> + '_ { + self.sockets.iter().filter_map(|x| x.as_ref()) } /// Iterate every socket in this set. - pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> { - IterMut { - lower: self.sockets.iter_mut(), - } - } -} - -/// Immutable socket set iterator. -/// -/// This struct is created by the [iter](struct.SocketSet.html#method.iter) -/// on [socket sets](struct.SocketSet.html). -pub struct Iter<'a, 'b: 'a> { - lower: slice::Iter<'a, Option>>, -} - -impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> { - type Item = &'a Socket<'b>; - - fn next(&mut self) -> Option { - for item_opt in &mut self.lower { - if let Some(item) = item_opt.as_ref() { - return Some(&item.socket); - } - } - None - } -} - -/// Mutable socket set iterator. -/// -/// This struct is created by the [iter_mut](struct.SocketSet.html#method.iter_mut) -/// on [socket sets](struct.SocketSet.html). -pub struct IterMut<'a, 'b: 'a> { - lower: slice::IterMut<'a, Option>>, -} - -impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> { - type Item = &'a mut Socket<'b>; - - fn next(&mut self) -> Option { - for item_opt in &mut self.lower { - if let Some(item) = item_opt.as_mut() { - return Some(&mut item.socket); - } - } - None + pub fn iter_mut(&mut self) -> impl Iterator> + '_ { + self.sockets.iter_mut().filter_map(|x| x.as_mut()) } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 81478504f..2c17e0db7 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -8,7 +8,7 @@ use core::{cmp, fmt, mem}; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::socket::{Context, PollAt}; use crate::storage::{Assembler, RingBuffer}; use crate::time::{Duration, Instant}; use crate::wire::{ @@ -308,7 +308,6 @@ enum AckDelayTimer { /// attempts will be reset. #[derive(Debug)] pub struct TcpSocket<'a> { - pub(crate) meta: SocketMeta, state: State, timer: Timer, rtte: RttEstimator, @@ -411,7 +410,6 @@ impl<'a> TcpSocket<'a> { let rx_cap_log2 = mem::size_of::() * 8 - rx_capacity.leading_zeros() as usize; TcpSocket { - meta: SocketMeta::default(), state: State::Closed, timer: Timer::new(), rtte: RttEstimator::default(), @@ -486,12 +484,6 @@ impl<'a> TcpSocket<'a> { self.tx_waker.register(waker) } - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta.handle - } - /// Return the timeout duration. /// /// See also the [set_timeout](#method.set_timeout) method. @@ -948,8 +940,7 @@ impl<'a> TcpSocket<'a> { if size > 0 { #[cfg(any(test, feature = "verbose"))] net_trace!( - "{}:{}:{}: tx buffer: enqueueing {} octets (now {})", - self.meta.handle, + "tcp:{}:{}: tx buffer: enqueueing {} octets (now {})", self.local_endpoint, self.remote_endpoint, size, @@ -1010,8 +1001,7 @@ impl<'a> TcpSocket<'a> { if size > 0 { #[cfg(any(test, feature = "verbose"))] net_trace!( - "{}:{}:{}: rx buffer: dequeueing {} octets (now {})", - self.meta.handle, + "tcp:{}:{}: rx buffer: dequeueing {} octets (now {})", self.local_endpoint, self.remote_endpoint, size, @@ -1062,8 +1052,7 @@ impl<'a> TcpSocket<'a> { if !buffer.is_empty() { #[cfg(any(test, feature = "verbose"))] net_trace!( - "{}:{}:{}: rx buffer: peeking at {} octets", - self.meta.handle, + "tcp:{}:{}: rx buffer: peeking at {} octets", self.local_endpoint, self.remote_endpoint, buffer.len() @@ -1103,16 +1092,14 @@ impl<'a> TcpSocket<'a> { if self.state != state { if self.remote_endpoint.addr.is_unspecified() { net_trace!( - "{}:{}: state={}=>{}", - self.meta.handle, + "tcp:{}: state={}=>{}", self.local_endpoint, self.state, state ); } else { net_trace!( - "{}:{}:{}: state={}=>{}", - self.meta.handle, + "tcp:{}:{}: state={}=>{}", self.local_endpoint, self.remote_endpoint, self.state, @@ -1307,9 +1294,8 @@ impl<'a> TcpSocket<'a> { // the initial SYN. (State::SynSent, TcpControl::Rst, None) => { net_debug!( - "{}:{}:{}: unacceptable RST (expecting RST|ACK) \ + "tcp:{}:{}: unacceptable RST (expecting RST|ACK) \ in response to initial SYN", - self.meta.handle, self.local_endpoint, self.remote_endpoint ); @@ -1318,8 +1304,7 @@ impl<'a> TcpSocket<'a> { (State::SynSent, TcpControl::Rst, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!( - "{}:{}:{}: unacceptable RST|ACK in response to initial SYN", - self.meta.handle, + "tcp:{}:{}: unacceptable RST|ACK in response to initial SYN", self.local_endpoint, self.remote_endpoint ); @@ -1335,8 +1320,7 @@ impl<'a> TcpSocket<'a> { // Every packet after the initial SYN must be an acknowledgement. (_, _, None) => { net_debug!( - "{}:{}:{}: expecting an ACK", - self.meta.handle, + "tcp:{}:{}: expecting an ACK", self.local_endpoint, self.remote_endpoint ); @@ -1346,8 +1330,7 @@ impl<'a> TcpSocket<'a> { (State::SynSent, TcpControl::Syn, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!( - "{}:{}:{}: unacceptable SYN|ACK in response to initial SYN", - self.meta.handle, + "tcp:{}:{}: unacceptable SYN|ACK in response to initial SYN", self.local_endpoint, self.remote_endpoint ); @@ -1362,18 +1345,16 @@ impl<'a> TcpSocket<'a> { // does it, we do too. if ack_number == self.local_seq_no + 1 { net_debug!( - "{}:{}:{}: expecting a SYN|ACK, received an ACK with the right ack_number, ignoring.", - self.meta.handle, - self.local_endpoint, + "tcp:{}:{}: expecting a SYN|ACK, received an ACK with the right ack_number, ignoring.", + self.local_endpoint, self.remote_endpoint ); return Err(Error::Dropped); } net_debug!( - "{}:{}:{}: expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST.", - self.meta.handle, - self.local_endpoint, + "tcp:{}:{}: expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST.", + self.local_endpoint, self.remote_endpoint ); return Ok(Some(Self::rst_reply(ip_repr, repr))); @@ -1381,8 +1362,7 @@ impl<'a> TcpSocket<'a> { // Anything else in the SYN-SENT state is invalid. (State::SynSent, _, _) => { net_debug!( - "{}:{}:{}: expecting a SYN|ACK", - self.meta.handle, + "tcp:{}:{}: expecting a SYN|ACK", self.local_endpoint, self.remote_endpoint ); @@ -1392,8 +1372,7 @@ impl<'a> TcpSocket<'a> { (State::SynReceived, _, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!( - "{}:{}:{}: unacceptable ACK in response to SYN|ACK", - self.meta.handle, + "tcp:{}:{}: unacceptable ACK in response to SYN|ACK", self.local_endpoint, self.remote_endpoint ); @@ -1415,8 +1394,7 @@ impl<'a> TcpSocket<'a> { if ack_number < ack_min { net_debug!( - "{}:{}:{}: duplicate ACK ({} not in {}...{})", - self.meta.handle, + "tcp:{}:{}: duplicate ACK ({} not in {}...{})", self.local_endpoint, self.remote_endpoint, ack_number, @@ -1428,8 +1406,7 @@ impl<'a> TcpSocket<'a> { if ack_number > ack_max { net_debug!( - "{}:{}:{}: unacceptable ACK ({} not in {}...{})", - self.meta.handle, + "tcp:{}:{}: unacceptable ACK ({} not in {}...{})", self.local_endpoint, self.remote_endpoint, ack_number, @@ -1456,9 +1433,8 @@ impl<'a> TcpSocket<'a> { if window_start == window_end && segment_start != segment_end { net_debug!( - "{}:{}:{}: non-zero-length segment with zero receive window, \ + "tcp:{}:{}: non-zero-length segment with zero receive window, \ will only send an ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint ); @@ -1467,9 +1443,8 @@ impl<'a> TcpSocket<'a> { if segment_start == segment_end && segment_end == window_start - 1 { net_debug!( - "{}:{}:{}: received a keep-alive or window probe packet, \ + "tcp:{}:{}: received a keep-alive or window probe packet, \ will send an ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint ); @@ -1478,9 +1453,8 @@ impl<'a> TcpSocket<'a> { && (window_start <= segment_end && segment_end <= window_end)) { net_debug!( - "{}:{}:{}: segment not in receive window \ + "tcp:{}:{}: segment not in receive window \ ({}..{} not intersecting {}..{}), will send challenge ACK", - self.meta.handle, self.local_endpoint, self.remote_endpoint, segment_start, @@ -1526,8 +1500,7 @@ impl<'a> TcpSocket<'a> { if sent_fin && self.tx_buffer.len() + 1 == ack_len { ack_len -= 1; net_trace!( - "{}:{}:{}: received ACK of FIN", - self.meta.handle, + "tcp:{}:{}: received ACK of FIN", self.local_endpoint, self.remote_endpoint ); @@ -1557,8 +1530,7 @@ impl<'a> TcpSocket<'a> { // RSTs in SYN-RECEIVED flip the socket back to the LISTEN state. (State::SynReceived, TcpControl::Rst) => { net_trace!( - "{}:{}:{}: received RST", - self.meta.handle, + "tcp:{}:{}: received RST", self.local_endpoint, self.remote_endpoint ); @@ -1571,8 +1543,7 @@ impl<'a> TcpSocket<'a> { // RSTs in any other state close the socket. (_, TcpControl::Rst) => { net_trace!( - "{}:{}:{}: received RST", - self.meta.handle, + "tcp:{}:{}: received RST", self.local_endpoint, self.remote_endpoint ); @@ -1584,12 +1555,11 @@ impl<'a> TcpSocket<'a> { // SYN packets in the LISTEN state change it to SYN-RECEIVED. (State::Listen, TcpControl::Syn) => { - net_trace!("{}:{}: received SYN", self.meta.handle, self.local_endpoint); + net_trace!("tcp:{}: received SYN", self.local_endpoint); if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { net_trace!( - "{}:{}:{}: received SYNACK with zero MSS, ignoring", - self.meta.handle, + "tcp:{}:{}: received SYNACK with zero MSS, ignoring", self.local_endpoint, self.remote_endpoint ); @@ -1632,16 +1602,14 @@ impl<'a> TcpSocket<'a> { // SYN|ACK packets in the SYN-SENT state change it to ESTABLISHED. (State::SynSent, TcpControl::Syn) => { net_trace!( - "{}:{}:{}: received SYN|ACK", - self.meta.handle, + "tcp:{}:{}: received SYN|ACK", self.local_endpoint, self.remote_endpoint ); if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { net_trace!( - "{}:{}:{}: received SYNACK with zero MSS, ignoring", - self.meta.handle, + "tcp:{}:{}: received SYNACK with zero MSS, ignoring", self.local_endpoint, self.remote_endpoint ); @@ -1745,8 +1713,7 @@ impl<'a> TcpSocket<'a> { _ => { net_debug!( - "{}:{}:{}: unexpected packet {}", - self.meta.handle, + "tcp:{}:{}: unexpected packet {}", self.local_endpoint, self.remote_endpoint, repr @@ -1770,8 +1737,7 @@ impl<'a> TcpSocket<'a> { // Dequeue acknowledged octets. debug_assert!(self.tx_buffer.len() >= ack_len); net_trace!( - "{}:{}:{}: tx buffer: dequeueing {} octets (now {})", - self.meta.handle, + "tcp:{}:{}: tx buffer: dequeueing {} octets (now {})", self.local_endpoint, self.remote_endpoint, ack_len, @@ -1805,8 +1771,7 @@ impl<'a> TcpSocket<'a> { self.local_rx_dup_acks = self.local_rx_dup_acks.saturating_add(1); net_debug!( - "{}:{}:{}: received duplicate ACK for seq {} (duplicate nr {}{})", - self.meta.handle, + "tcp:{}:{}: received duplicate ACK for seq {} (duplicate nr {}{})", self.local_endpoint, self.remote_endpoint, ack_number, @@ -1821,8 +1786,7 @@ impl<'a> TcpSocket<'a> { if self.local_rx_dup_acks == 3 { self.timer.set_for_fast_retransmit(); net_debug!( - "{}:{}:{}: started fast retransmit", - self.meta.handle, + "tcp:{}:{}: started fast retransmit", self.local_endpoint, self.remote_endpoint ); @@ -1833,8 +1797,7 @@ impl<'a> TcpSocket<'a> { if self.local_rx_dup_acks > 0 { self.local_rx_dup_acks = 0; net_debug!( - "{}:{}:{}: reset duplicate ACK count", - self.meta.handle, + "tcp:{}:{}: reset duplicate ACK count", self.local_endpoint, self.remote_endpoint ); @@ -1868,8 +1831,7 @@ impl<'a> TcpSocket<'a> { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. net_trace!( - "{}:{}:{}: rx buffer: receiving {} octets at offset {}", - self.meta.handle, + "tcp:{}:{}: rx buffer: receiving {} octets at offset {}", self.local_endpoint, self.remote_endpoint, payload_len, @@ -1882,8 +1844,7 @@ impl<'a> TcpSocket<'a> { } Err(_) => { net_debug!( - "{}:{}:{}: assembler: too many holes to add {} octets at offset {}", - self.meta.handle, + "tcp:{}:{}: assembler: too many holes to add {} octets at offset {}", self.local_endpoint, self.remote_endpoint, payload_len, @@ -1897,8 +1858,7 @@ impl<'a> TcpSocket<'a> { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Enqueue the contiguous data octets in front of the buffer. net_trace!( - "{}:{}:{}: rx buffer: enqueueing {} octets (now {})", - self.meta.handle, + "tcp:{}:{}: rx buffer: enqueueing {} octets (now {})", self.local_endpoint, self.remote_endpoint, contig_len, @@ -1914,8 +1874,7 @@ impl<'a> TcpSocket<'a> { if !self.assembler.is_empty() { // Print the ranges recorded in the assembler. net_trace!( - "{}:{}:{}: assembler: {}", - self.meta.handle, + "tcp:{}:{}: assembler: {}", self.local_endpoint, self.remote_endpoint, self.assembler @@ -1928,8 +1887,7 @@ impl<'a> TcpSocket<'a> { self.ack_delay_timer = match self.ack_delay_timer { AckDelayTimer::Idle => { net_trace!( - "{}:{}:{}: starting delayed ack timer", - self.meta.handle, + "tcp:{}:{}: starting delayed ack timer", self.local_endpoint, self.remote_endpoint ); @@ -1941,8 +1899,7 @@ impl<'a> TcpSocket<'a> { // For now, we send an ACK every second received packet, full-sized or not. AckDelayTimer::Waiting(_) => { net_trace!( - "{}:{}:{}: delayed ack timer already started, forcing expiry", - self.meta.handle, + "tcp:{}:{}: delayed ack timer already started, forcing expiry", self.local_endpoint, self.remote_endpoint ); @@ -1950,8 +1907,7 @@ impl<'a> TcpSocket<'a> { } AckDelayTimer::Immediate => { net_trace!( - "{}:{}:{}: delayed ack timer already force-expired", - self.meta.handle, + "tcp:{}:{}: delayed ack timer already force-expired", self.local_endpoint, self.remote_endpoint ); @@ -1969,8 +1925,7 @@ impl<'a> TcpSocket<'a> { // This is fine because smoltcp assumes that it can always transmit zero or one // packets for every packet it receives. net_trace!( - "{}:{}:{}: ACKing incoming segment", - self.meta.handle, + "tcp:{}:{}: ACKing incoming segment", self.local_endpoint, self.remote_endpoint ); @@ -2097,8 +2052,7 @@ impl<'a> TcpSocket<'a> { if self.timed_out(cx.now) { // If a timeout expires, we should abort the connection. net_debug!( - "{}:{}:{}: timeout exceeded", - self.meta.handle, + "tcp:{}:{}: timeout exceeded", self.local_endpoint, self.remote_endpoint ); @@ -2107,8 +2061,7 @@ impl<'a> TcpSocket<'a> { 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, + "tcp:{}:{}: retransmitting at t+{}", self.local_endpoint, self.remote_endpoint, retransmit_delta @@ -2134,48 +2087,42 @@ impl<'a> TcpSocket<'a> { if self.seq_to_transmit(cx) { // 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, + "tcp:{}:{}: outgoing segment will send data or flags", self.local_endpoint, self.remote_endpoint ); } 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, + "tcp:{}:{}: outgoing segment will acknowledge", self.local_endpoint, self.remote_endpoint ); } 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, + "tcp:{}:{}: outgoing segment will update window", self.local_endpoint, self.remote_endpoint ); } else if self.state == State::Closed { // If we need to abort the connection, do it. net_trace!( - "{}:{}:{}: outgoing segment will abort connection", - self.meta.handle, + "tcp:{}:{}: outgoing segment will abort connection", self.local_endpoint, self.remote_endpoint ); } 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, + "tcp:{}:{}: keep-alive timer expired", self.local_endpoint, self.remote_endpoint ); } 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, + "tcp:{}:{}: TIME-WAIT timer expired", self.local_endpoint, self.remote_endpoint ); @@ -2309,15 +2256,13 @@ impl<'a> TcpSocket<'a> { // Trace a summary of what will be sent. if is_keep_alive { net_trace!( - "{}:{}:{}: sending a keep-alive", - self.meta.handle, + "tcp:{}:{}: sending a keep-alive", self.local_endpoint, self.remote_endpoint ); } else if !repr.payload.is_empty() { net_trace!( - "{}:{}:{}: tx buffer: sending {} octets at offset {}", - self.meta.handle, + "tcp:{}:{}: tx buffer: sending {} octets at offset {}", self.local_endpoint, self.remote_endpoint, repr.payload.len(), @@ -2335,8 +2280,7 @@ impl<'a> TcpSocket<'a> { _ => "", }; net_trace!( - "{}:{}:{}: sending {}", - self.meta.handle, + "tcp:{}:{}: sending {}", self.local_endpoint, self.remote_endpoint, flags @@ -2368,16 +2312,14 @@ impl<'a> TcpSocket<'a> { AckDelayTimer::Idle => {} AckDelayTimer::Waiting(_) => { net_trace!( - "{}:{}:{}: stop delayed ack timer", - self.meta.handle, + "tcp:{}:{}: stop delayed ack timer", self.local_endpoint, self.remote_endpoint ) } AckDelayTimer::Immediate => { net_trace!( - "{}:{}:{}: stop delayed ack timer (was force-expired)", - self.meta.handle, + "tcp:{}:{}: stop delayed ack timer (was force-expired)", self.local_endpoint, self.remote_endpoint ) @@ -2459,12 +2401,6 @@ impl<'a> TcpSocket<'a> { } } -impl<'a> From> for Socket<'a> { - fn from(val: TcpSocket<'a>) -> Self { - Socket::Tcp(val) - } -} - impl<'a> fmt::Write for TcpSocket<'a> { fn write_str(&mut self, slice: &str) -> fmt::Result { let slice = slice.as_bytes(); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 88df7e069..314d43460 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -4,7 +4,7 @@ use core::task::Waker; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta}; +use crate::socket::{Context, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; @@ -21,7 +21,6 @@ pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>; /// packet buffers. #[derive(Debug)] pub struct UdpSocket<'a> { - pub(crate) meta: SocketMeta, endpoint: IpEndpoint, rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>, @@ -37,7 +36,6 @@ impl<'a> UdpSocket<'a> { /// Create an UDP socket with the given buffers. pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { UdpSocket { - meta: SocketMeta::default(), endpoint: IpEndpoint::default(), rx_buffer: rx_buffer, tx_buffer: tx_buffer, @@ -84,12 +82,6 @@ impl<'a> UdpSocket<'a> { self.tx_waker.register(waker) } - /// Return the socket handle. - #[inline] - pub fn handle(&self) -> SocketHandle { - self.meta.handle - } - /// Return the bound endpoint. #[inline] pub fn endpoint(&self) -> IpEndpoint { @@ -225,8 +217,7 @@ impl<'a> UdpSocket<'a> { let payload_buf = self.tx_buffer.enqueue(size, endpoint)?; net_trace!( - "{}:{}:{}: buffer to send {} octets", - self.meta.handle, + "udp:{}:{}: buffer to send {} octets", self.endpoint, endpoint, size @@ -250,8 +241,7 @@ impl<'a> UdpSocket<'a> { let (endpoint, payload_buf) = self.rx_buffer.dequeue()?; net_trace!( - "{}:{}:{}: receive {} buffered octets", - self.meta.handle, + "udp:{}:{}: receive {} buffered octets", self.endpoint, endpoint, payload_buf.len() @@ -276,12 +266,10 @@ impl<'a> UdpSocket<'a> { /// /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint)> { - let handle = self.meta.handle; let endpoint = self.endpoint; self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| { net_trace!( - "{}:{}:{}: peek {} buffered octets", - handle, + "udp:{}:{}: peek {} buffered octets", endpoint, remote_endpoint, payload_buf.len() @@ -338,8 +326,7 @@ impl<'a> UdpSocket<'a> { .copy_from_slice(payload); net_trace!( - "{}:{}:{}: receiving {} octets", - self.meta.handle, + "udp:{}:{}: receiving {} octets", self.endpoint, endpoint, size @@ -355,15 +342,13 @@ impl<'a> UdpSocket<'a> { where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>, { - let handle = self.handle(); let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); self.tx_buffer .dequeue_with(|remote_endpoint, payload_buf| { net_trace!( - "{}:{}:{}: sending {} octets", - handle, + "udp:{}:{}: sending {} octets", endpoint, endpoint, payload_buf.len() @@ -398,12 +383,6 @@ impl<'a> UdpSocket<'a> { } } -impl<'a> From> for Socket<'a> { - fn from(val: UdpSocket<'a>) -> Self { - Socket::Udp(val) - } -} - #[cfg(test)] mod test { use super::*; From 4e365ce9ba4641dff5d88a46a3257eb2582c8bba Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 22:17:42 +0100 Subject: [PATCH 273/566] Make SocketSet private, move to `iface`. --- src/iface/interface.rs | 4 +- src/iface/mod.rs | 3 + src/{socket/meta.rs => iface/socket_meta.rs} | 3 +- src/{socket/set.rs => iface/socket_set.rs} | 63 ++++++++++++-------- src/socket/mod.rs | 5 -- 5 files changed, 45 insertions(+), 33 deletions(-) rename src/{socket/meta.rs => iface/socket_meta.rs} (98%) rename src/{socket/set.rs => iface/socket_set.rs} (62%) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 254e177e4..37e912a6b 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -5,6 +5,8 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; +use super::socket_set::SocketSet; +use super::{SocketHandle, SocketStorage}; use crate::iface::Routes; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::iface::{NeighborAnswer, NeighborCache}; @@ -108,7 +110,7 @@ let iface = InterfaceBuilder::new(device, vec![]) )] pub fn new(device: DeviceT, sockets: SocketsT) -> Self where - SocketsT: Into>>>, + SocketsT: Into>>, { InterfaceBuilder { device: device, diff --git a/src/iface/mod.rs b/src/iface/mod.rs index bea35ca6e..547358f28 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -8,6 +8,8 @@ mod interface; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] mod neighbor; mod route; +mod socket_meta; +mod socket_set; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub(crate) use self::neighbor::Answer as NeighborAnswer; @@ -16,5 +18,6 @@ pub use self::neighbor::Cache as NeighborCache; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; +pub use socket_set::{SocketHandle, SocketStorage}; pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/socket/meta.rs b/src/iface/socket_meta.rs similarity index 98% rename from src/socket/meta.rs rename to src/iface/socket_meta.rs index a1e51d811..64cb30308 100644 --- a/src/socket/meta.rs +++ b/src/iface/socket_meta.rs @@ -1,4 +1,5 @@ -use crate::socket::{PollAt, SocketHandle}; +use super::SocketHandle; +use crate::socket::PollAt; use crate::time::{Duration, Instant}; use crate::wire::IpAddress; diff --git a/src/socket/set.rs b/src/iface/socket_set.rs similarity index 62% rename from src/socket/set.rs rename to src/iface/socket_set.rs index 66b017df4..72e274a70 100644 --- a/src/socket/set.rs +++ b/src/iface/socket_set.rs @@ -1,26 +1,35 @@ use core::fmt; use managed::ManagedSlice; +use super::socket_meta::Meta; use crate::socket::{AnySocket, Socket}; -use super::meta::Meta; +/// Opaque struct with space for storing one socket. +/// +/// This is public so you can use it to allocate space for storing +/// sockets when creating an Interface. +#[derive(Debug, Default)] +pub struct SocketStorage<'a> { + inner: Option>, +} + +impl<'a> SocketStorage<'a> { + pub const EMPTY: Self = Self { inner: None }; +} /// An item of a socket set. -/// -/// The only reason this struct is public is to allow the socket set storage -/// to be allocated externally. #[derive(Debug)] -pub struct Item<'a> { +pub(crate) struct Item<'a> { pub(crate) meta: Meta, pub(crate) socket: Socket<'a>, } -/// A handle, identifying a socket in a set. +/// A handle, identifying a socket in an Interface. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Handle(usize); +pub struct SocketHandle(usize); -impl fmt::Display for Handle { +impl fmt::Display for SocketHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "#{}", self.0) } @@ -30,38 +39,40 @@ impl fmt::Display for Handle { /// /// The lifetime `'a` is used when storing a `Socket<'a>`. #[derive(Debug)] -pub struct Set<'a> { - sockets: ManagedSlice<'a, Option>>, +pub(crate) struct SocketSet<'a> { + sockets: ManagedSlice<'a, SocketStorage<'a>>, } -impl<'a> Set<'a> { +impl<'a> SocketSet<'a> { /// Create a socket set using the provided storage. - pub fn new(sockets: SocketsT) -> Set<'a> + pub fn new(sockets: SocketsT) -> SocketSet<'a> where - SocketsT: Into>>>, + SocketsT: Into>>, { let sockets = sockets.into(); - Set { sockets } + SocketSet { sockets } } /// Add a socket to the set, and return its handle. /// /// # Panics /// This function panics if the storage is fixed-size (not a `Vec`) and is full. - pub fn add>(&mut self, socket: T) -> Handle { - fn put<'a>(index: usize, slot: &mut Option>, socket: Socket<'a>) -> Handle { + pub fn add>(&mut self, socket: T) -> SocketHandle { + fn put<'a>(index: usize, slot: &mut SocketStorage<'a>, socket: Socket<'a>) -> SocketHandle { net_trace!("[{}]: adding", index); - let handle = Handle(index); + let handle = SocketHandle(index); let mut meta = Meta::default(); meta.handle = handle; - *slot = Some(Item { socket, meta }); + *slot = SocketStorage { + inner: Some(Item { meta, socket }), + }; handle } let socket = socket.upcast(); for (index, slot) in self.sockets.iter_mut().enumerate() { - if slot.is_none() { + if slot.inner.is_none() { return put(index, slot, socket); } } @@ -70,7 +81,7 @@ impl<'a> Set<'a> { ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"), #[cfg(any(feature = "std", feature = "alloc"))] ManagedSlice::Owned(ref mut sockets) => { - sockets.push(None); + sockets.push(SocketStorage { inner: None }); let index = sockets.len() - 1; put(index, &mut sockets[index], socket) } @@ -82,8 +93,8 @@ impl<'a> Set<'a> { /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get>(&mut self, handle: Handle) -> &mut T { - match self.sockets[handle.0].as_mut() { + pub fn get>(&mut self, handle: SocketHandle) -> &mut T { + match self.sockets[handle.0].inner.as_mut() { Some(item) => { T::downcast(&mut item.socket).expect("handle refers to a socket of a wrong type") } @@ -95,9 +106,9 @@ impl<'a> Set<'a> { /// /// # Panics /// This function may panic if the handle does not belong to this socket set. - pub fn remove(&mut self, handle: Handle) -> Socket<'a> { + pub fn remove(&mut self, handle: SocketHandle) -> Socket<'a> { net_trace!("[{}]: removing", handle.0); - match self.sockets[handle.0].take() { + match self.sockets[handle.0].inner.take() { Some(item) => item.socket, None => panic!("handle does not refer to a valid socket"), } @@ -105,11 +116,11 @@ impl<'a> Set<'a> { /// Iterate every socket in this set. pub fn iter(&self) -> impl Iterator> + '_ { - self.sockets.iter().filter_map(|x| x.as_ref()) + self.sockets.iter().filter_map(|x| x.inner.as_ref()) } /// Iterate every socket in this set. pub fn iter_mut(&mut self) -> impl Iterator> + '_ { - self.sockets.iter_mut().filter_map(|x| x.as_mut()) + self.sockets.iter_mut().filter_map(|x| x.inner.as_mut()) } } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index fcfbee1f7..c387a9112 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -14,9 +14,6 @@ size for a buffer, allocate it, and let the networking stack use it. use crate::phy::DeviceCapabilities; use crate::time::Instant; -mod meta; -mod set; - #[cfg(feature = "socket-dhcpv4")] mod dhcpv4; #[cfg(feature = "socket-icmp")] @@ -31,8 +28,6 @@ mod udp; #[cfg(feature = "async")] mod waker; -pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet}; - #[cfg(feature = "socket-dhcpv4")] pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; #[cfg(feature = "socket-icmp")] From 8a3aaa12d1442a18ebcacc69ec97175adcf60242 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 25 Nov 2021 21:54:02 +0100 Subject: [PATCH 274/566] iface: return handle in sockets/sockets_mut. --- src/iface/interface.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 37e912a6b..7ef336604 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -556,13 +556,15 @@ where } /// Get an iterator to the inner sockets. - pub fn sockets(&self) -> impl Iterator> { - self.sockets.iter().map(|i| &i.socket) + pub fn sockets(&self) -> impl Iterator)> { + self.sockets.iter().map(|i| (i.meta.handle, &i.socket)) } /// Get a mutable iterator to the inner sockets. - pub fn sockets_mut(&mut self) -> impl Iterator> { - self.sockets.iter_mut().map(|i| &mut i.socket) + pub fn sockets_mut(&mut self) -> impl Iterator)> { + self.sockets + .iter_mut() + .map(|i| (i.meta.handle, &mut i.socket)) } /// Add an address to a list of subscribed multicast IP addresses. From 0ebf896fffd14cee78987a3f8abe4baa500002c4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Nov 2021 03:45:04 +0100 Subject: [PATCH 275/566] socket: refactor Context to be just InterfaceInner. --- src/iface/interface.rs | 492 +++++++++++++++++++++-------------------- src/iface/mod.rs | 2 +- src/socket/dhcpv4.rs | 137 +++++++----- src/socket/icmp.rs | 78 +++---- src/socket/mod.rs | 55 +---- src/socket/raw.rs | 55 +++-- src/socket/tcp.rs | 290 +++++++++++++----------- src/socket/udp.rs | 95 ++++---- 8 files changed, 610 insertions(+), 594 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 7ef336604..76d46c6dc 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -10,7 +10,7 @@ use super::{SocketHandle, SocketStorage}; use crate::iface::Routes; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::iface::{NeighborAnswer, NeighborCache}; -use crate::phy::{Device, DeviceCapabilities, Medium, RxToken, TxToken}; +use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; @@ -34,7 +34,9 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// the `device` mutably until they're used, which makes it impossible to call other /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. -struct InterfaceInner<'a> { +pub struct InterfaceInner<'a> { + caps: DeviceCapabilities, + now: Instant, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option>, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -136,12 +138,12 @@ let iface = InterfaceBuilder::new(device, vec![]) } /// Set the Hardware address the interface will use. See also - /// [ethernet_addr]. + /// [hardware_addr]. /// /// # Panics /// This function panics if the address is not unicast. /// - /// [ethernet_addr]: struct.Interface.html#method.ethernet_addr + /// [hardware_addr]: struct.Interface.html#method.hardware_addr #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub fn hardware_addr(mut self, addr: HardwareAddress) -> Self { InterfaceInner::check_hardware_addr(&addr); @@ -293,10 +295,14 @@ let iface = InterfaceBuilder::new(device, vec![]) ), }; + let caps = self.device.capabilities(); + Interface { device: self.device, sockets: self.sockets, inner: InterfaceInner { + now: Instant::from_secs(0), + caps, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr, ip_addrs: self.ip_addrs, @@ -574,8 +580,10 @@ where pub fn join_multicast_group>( &mut self, addr: T, - _timestamp: Instant, + timestamp: Instant, ) -> Result { + self.inner.now = timestamp; + match addr.into() { #[cfg(feature = "proto-igmp")] IpAddress::Ipv4(addr) => { @@ -589,10 +597,9 @@ where 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(&cx, tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt)?; Ok(true) } else { Ok(false) @@ -610,8 +617,10 @@ where pub fn leave_multicast_group>( &mut self, addr: T, - _timestamp: Instant, + timestamp: Instant, ) -> Result { + self.inner.now = timestamp; + match addr.into() { #[cfg(feature = "proto-igmp")] IpAddress::Ipv4(addr) => { @@ -619,10 +628,9 @@ where 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(&cx, tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt)?; Ok(true) } else { Ok(false) @@ -702,15 +710,15 @@ where /// a very common occurrence and on a production system it should not even /// be logged. pub fn poll(&mut self, timestamp: Instant) -> Result { - let cx = self.context(timestamp); + self.inner.now = timestamp; let mut readiness_may_have_changed = false; loop { - let processed_any = self.socket_ingress(&cx); - let emitted_any = self.socket_egress(&cx)?; + let processed_any = self.socket_ingress(); + let emitted_any = self.socket_egress()?; #[cfg(feature = "proto-igmp")] - self.igmp_egress(&cx, timestamp)?; + self.igmp_egress()?; if processed_any || emitted_any { readiness_may_have_changed = true; @@ -729,16 +737,19 @@ where /// /// [poll]: #method.poll /// [Instant]: struct.Instant.html - pub fn poll_at(&self, timestamp: Instant) -> Option { - let cx = self.context(timestamp); + pub fn poll_at(&mut self, timestamp: Instant) -> Option { + self.inner.now = timestamp; + + let inner = &mut self.inner; self.sockets .iter() - .filter_map(|item| { - let socket_poll_at = item.socket.poll_at(&cx); - match item.meta.poll_at(socket_poll_at, |ip_addr| { - self.inner.has_neighbor(&cx, &ip_addr) - }) { + .filter_map(move |item| { + let socket_poll_at = item.socket.poll_at(inner); + match item + .meta + .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr)) + { PollAt::Ingress => None, PollAt::Time(instant) => Some(instant), PollAt::Now => Some(Instant::from_millis(0)), @@ -755,7 +766,7 @@ where /// /// [poll]: #method.poll /// [Duration]: struct.Duration.html - pub fn poll_delay(&self, timestamp: Instant) -> Option { + pub fn poll_delay(&mut self, timestamp: Instant) -> Option { match self.poll_at(timestamp) { Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), Some(_) => Some(Duration::from_millis(0)), @@ -763,21 +774,22 @@ where } } - fn socket_ingress(&mut self, cx: &Context) -> bool { + fn socket_ingress(&mut self) -> bool { let mut processed_any = false; let Self { device, inner, sockets, + .. } = self; while let Some((rx_token, tx_token)) = device.receive() { - if let Err(err) = rx_token.consume(cx.now, |frame| match cx.caps.medium { + if let Err(err) = rx_token.consume(inner.now, |frame| match inner.caps.medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => match inner.process_ethernet(cx, sockets, &frame) { + Medium::Ethernet => match inner.process_ethernet(sockets, &frame) { Ok(response) => { processed_any = true; if let Some(packet) = response { - if let Err(err) = inner.dispatch(cx, tx_token, packet) { + if let Err(err) = inner.dispatch(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } @@ -794,11 +806,11 @@ where } }, #[cfg(feature = "medium-ip")] - Medium::Ip => match inner.process_ip(cx, sockets, &frame) { + Medium::Ip => match inner.process_ip(sockets, &frame) { Ok(response) => { processed_any = true; if let Some(packet) = response { - if let Err(err) = inner.dispatch_ip(cx, tx_token, packet) { + if let Err(err) = inner.dispatch_ip(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } @@ -810,11 +822,11 @@ where } }, #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => match inner.process_ieee802154(cx, sockets, &frame) { + Medium::Ieee802154 => match inner.process_ieee802154(sockets, &frame) { Ok(response) => { processed_any = true; if let Some(packet) = response { - if let Err(err) = inner.dispatch_ieee802154(cx, tx_token, packet) { + if let Err(err) = inner.dispatch_ieee802154(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } @@ -833,11 +845,12 @@ where processed_any } - fn socket_egress(&mut self, cx: &Context) -> Result { + fn socket_egress(&mut self) -> Result { let Self { device, inner, sockets, + .. } = self; let _caps = device.capabilities(); @@ -845,7 +858,7 @@ where for item in sockets.iter_mut() { if !item .meta - .egress_permitted(cx.now, |ip_addr| inner.has_neighbor(cx, &ip_addr)) + .egress_permitted(inner.now, |ip_addr| inner.has_neighbor(&ip_addr)) { continue; } @@ -854,44 +867,44 @@ where let mut device_result = Ok(()); macro_rules! respond { - ($response:expr) => {{ + ($inner:expr, $response:expr) => {{ 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(cx, tx_token, response); + device_result = $inner.dispatch_ip(tx_token, response); device_result }}; } let socket_result = match &mut item.socket { #[cfg(feature = "socket-raw")] - Socket::Raw(socket) => { - socket.dispatch(cx, |response| respond!(IpPacket::Raw(response))) - } + Socket::Raw(socket) => socket.dispatch(inner, |inner, response| { + respond!(inner, IpPacket::Raw(response)) + }), #[cfg(feature = "socket-icmp")] - Socket::Icmp(socket) => socket.dispatch(cx, |response| match response { + Socket::Icmp(socket) => socket.dispatch(inner, |inner, response| match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { - respond!(IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) + respond!(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) } #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { - respond!(IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) + respond!(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) } _ => Err(Error::Unaddressable), }), #[cfg(feature = "socket-udp")] - Socket::Udp(socket) => { - socket.dispatch(cx, |response| respond!(IpPacket::Udp(response))) - } + Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { + respond!(inner, IpPacket::Udp(response)) + }), #[cfg(feature = "socket-tcp")] - Socket::Tcp(socket) => { - socket.dispatch(cx, |response| respond!(IpPacket::Tcp(response))) - } + Socket::Tcp(socket) => socket.dispatch(inner, |inner, response| { + respond!(inner, IpPacket::Tcp(response)) + }), #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(socket) => { - socket.dispatch(cx, |response| respond!(IpPacket::Dhcpv4(response))) - } + Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { + respond!(inner, IpPacket::Dhcpv4(response)) + }), }; match (device_result, socket_result) { @@ -902,8 +915,10 @@ where // requests from the socket. However, without an additional rate limiting // mechanism, we would spin on every socket that has yet to discover its // neighboor. - item.meta - .neighbor_missing(cx.now, neighbor_addr.expect("non-IP response packet")); + item.meta.neighbor_missing( + inner.now, + neighbor_addr.expect("non-IP response packet"), + ); break; } (Err(err), _) | (_, Err(err)) => { @@ -923,17 +938,17 @@ where /// Depending on `igmp_report_state` and the therein contained /// timeouts, send IGMP membership reports. #[cfg(feature = "proto-igmp")] - fn igmp_egress(&mut self, cx: &Context, timestamp: Instant) -> Result { + fn igmp_egress(&mut self) -> Result { match self.inner.igmp_report_state { IgmpReportState::ToSpecificQuery { version, timeout, group, - } if timestamp >= timeout => { + } if self.inner.now >= 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(cx, tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt)?; } self.inner.igmp_report_state = IgmpReportState::Inactive; @@ -944,7 +959,7 @@ where timeout, interval, next_index, - } if timestamp >= timeout => { + } if self.inner.now >= timeout => { let addr = self .inner .ipv4_multicast_groups @@ -957,10 +972,10 @@ where 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(cx, tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt)?; } - let next_timeout = (timeout + interval).max(timestamp); + let next_timeout = (timeout + interval).max(self.inner.now); self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { version, timeout: next_timeout, @@ -979,23 +994,86 @@ where _ => Ok(false), } } +} + +impl<'a> InterfaceInner<'a> { + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn now(&self) -> Instant { + self.now + } + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn hardware_addr(&self) -> Option { + self.hardware_addr + } + + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities { + self.caps.checksum.clone() + } + + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn ip_mtu(&self) -> usize { + self.caps.ip_mtu() + } + + #[cfg(test)] + pub(crate) fn mock() -> Self { + Self { + 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, + }, + now: Instant::from_millis_const(0), + + ip_addrs: ManagedSlice::Owned(vec![]), + routes: Routes::new(&mut [][..]), + + #[cfg(feature = "proto-ipv4")] + any_ip: false, - fn context(&self, now: Instant) -> Context { - Context { - now, - caps: self.device.capabilities(), - #[cfg(all( - any(feature = "medium-ethernet", feature = "medium-ieee802154"), - feature = "socket-dhcpv4" - ))] - hardware_addr: self.inner.hardware_addr, #[cfg(feature = "medium-ieee802154")] - pan_id: self.inner.pan_id, + pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), + #[cfg(feature = "medium-ieee802154")] + sequence_no: 0, + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( + crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), + )), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: None, + + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), } } -} -impl<'a> InterfaceInner<'a> { + #[cfg(test)] + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn set_now(&mut self, now: Instant) { + self.now = now + } + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] fn check_hardware_addr(addr: &HardwareAddress) { if !addr.is_unicast() { @@ -1074,7 +1152,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ethernet")] fn process_ethernet<'frame, T: AsRef<[u8]>>( &mut self, - cx: &Context, sockets: &mut SocketSet, frame: &'frame T, ) -> Result>> { @@ -1090,17 +1167,17 @@ impl<'a> InterfaceInner<'a> { match eth_frame.ethertype() { #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Arp => self.process_arp(cx.now, ð_frame), + EthernetProtocol::Arp => self.process_arp(self.now, ð_frame), #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => { let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; - self.process_ipv4(cx, sockets, &ipv4_packet) + self.process_ipv4(sockets, &ipv4_packet) .map(|o| o.map(EthernetPacket::Ip)) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; - self.process_ipv6(cx, sockets, &ipv6_packet) + self.process_ipv6(sockets, &ipv6_packet) .map(|o| o.map(EthernetPacket::Ip)) } // Drop all other traffic. @@ -1111,7 +1188,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ip")] fn process_ip<'frame, T: AsRef<[u8]>>( &mut self, - cx: &Context, sockets: &mut SocketSet, ip_payload: &'frame T, ) -> Result>> { @@ -1119,12 +1195,12 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { let ipv4_packet = Ipv4Packet::new_checked(ip_payload)?; - self.process_ipv4(cx, sockets, &ipv4_packet) + self.process_ipv4(sockets, &ipv4_packet) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { let ipv6_packet = Ipv6Packet::new_checked(ip_payload)?; - self.process_ipv6(cx, sockets, &ipv6_packet) + self.process_ipv6(sockets, &ipv6_packet) } // Drop all other traffic. _ => Err(Error::Unrecognized), @@ -1134,7 +1210,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] fn process_ieee802154<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, - cx: &Context, sockets: &mut SocketSet, sixlowpan_payload: &'frame T, ) -> Result>> { @@ -1148,8 +1223,8 @@ impl<'a> InterfaceInner<'a> { // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. // We always accept the broadcast PAN id. - if cx.pan_id.is_some() - && ieee802154_repr.dst_pan_id != cx.pan_id + if self.pan_id.is_some() + && ieee802154_repr.dst_pan_id != self.pan_id && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) { net_debug!( @@ -1160,7 +1235,7 @@ impl<'a> InterfaceInner<'a> { } match ieee802154_frame.payload() { - Some(payload) => self.process_sixlowpan(cx, sockets, &ieee802154_repr, payload), + Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload), None => Ok(None), } } @@ -1168,7 +1243,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-sixlowpan")] fn process_sixlowpan<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, - cx: &Context, sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'frame T, @@ -1220,12 +1294,12 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| UdpSocket::downcast(&mut i.socket)) { - if !udp_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { + if !udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { continue; } match udp_socket.process( - cx, + self, &IpRepr::Ipv6(ipv6_repr), &udp_repr, udp_packet.payload(), @@ -1252,7 +1326,7 @@ impl<'a> InterfaceInner<'a> { SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { IpProtocol::Icmpv6 => { ipv6_repr.next_header = IpProtocol::Icmpv6; - self.process_icmpv6(cx, sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) + self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) } _ => { net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN"); @@ -1334,7 +1408,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-raw")] fn raw_socket_filter<'frame>( &mut self, - cx: &Context, sockets: &mut SocketSet, ip_repr: &IpRepr, ip_payload: &'frame [u8], @@ -1350,7 +1423,7 @@ impl<'a> InterfaceInner<'a> { continue; } - match raw_socket.process(cx, ip_repr, ip_payload) { + match raw_socket.process(self, 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 @@ -1365,7 +1438,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, - cx: &Context, sockets: &mut SocketSet, ipv6_packet: &Ipv6Packet<&'frame T>, ) -> Result>> { @@ -1380,13 +1452,11 @@ impl<'a> InterfaceInner<'a> { let ip_payload = ipv6_packet.payload(); #[cfg(feature = "socket-raw")] - let handled_by_raw_socket = - self.raw_socket_filter(cx, sockets, &ipv6_repr.into(), ip_payload); + let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload); #[cfg(not(feature = "socket-raw"))] let handled_by_raw_socket = false; self.process_nxt_hdr( - cx, sockets, ipv6_repr, ipv6_repr.next_header, @@ -1400,7 +1470,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn process_nxt_hdr<'frame>( &mut self, - cx: &Context, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, @@ -1408,22 +1477,18 @@ impl<'a> InterfaceInner<'a> { ip_payload: &'frame [u8], ) -> Result>> { match nxt_hdr { - IpProtocol::Icmpv6 => self.process_icmpv6(cx, sockets, ipv6_repr.into(), ip_payload), + IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload), #[cfg(feature = "socket-udp")] - IpProtocol::Udp => self.process_udp( - cx, - sockets, - ipv6_repr.into(), - handled_by_raw_socket, - ip_payload, - ), + IpProtocol::Udp => { + self.process_udp(sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload) + } #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => self.process_tcp(cx, sockets, ipv6_repr.into(), ip_payload), + IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), IpProtocol::HopByHop => { - self.process_hopbyhop(cx, sockets, ipv6_repr, handled_by_raw_socket, ip_payload) + self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload) } #[cfg(feature = "socket-raw")] @@ -1448,11 +1513,10 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, - cx: &Context, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'frame T>, ) -> Result>> { - let ipv4_repr = Ipv4Repr::parse(ipv4_packet, &cx.caps.checksum)?; + let ipv4_repr = Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)?; if !self.is_unicast_v4(ipv4_repr.src_addr) { // Discard packets with non-unicast source addresses. @@ -1464,7 +1528,7 @@ impl<'a> InterfaceInner<'a> { let ip_payload = ipv4_packet.payload(); #[cfg(feature = "socket-raw")] - let handled_by_raw_socket = self.raw_socket_filter(cx, sockets, &ip_repr, ip_payload); + let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload); #[cfg(not(feature = "socket-raw"))] let handled_by_raw_socket = false; @@ -1484,10 +1548,10 @@ impl<'a> InterfaceInner<'a> { { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_repr = - UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; + UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; let udp_payload = udp_packet.payload(); - match dhcp_socket.process(cx, &ipv4_repr, &udp_repr, udp_payload) { + match dhcp_socket.process(self, &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. @@ -1508,7 +1572,7 @@ impl<'a> InterfaceInner<'a> { || !ipv4_repr.dst_addr.is_unicast() || self .routes - .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), cx.now) + .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now) .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) { return Ok(None); @@ -1516,18 +1580,18 @@ impl<'a> InterfaceInner<'a> { } match ipv4_repr.protocol { - IpProtocol::Icmp => self.process_icmpv4(cx, sockets, ip_repr, ip_payload), + IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload), #[cfg(feature = "proto-igmp")] - IpProtocol::Igmp => self.process_igmp(cx, ipv4_repr, ip_payload), + IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload), #[cfg(feature = "socket-udp")] IpProtocol::Udp => { - self.process_udp(cx, sockets, ip_repr, handled_by_raw_socket, ip_payload) + self.process_udp(sockets, ip_repr, handled_by_raw_socket, ip_payload) } #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => self.process_tcp(cx, sockets, ip_repr, ip_payload), + IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload), _ if handled_by_raw_socket => Ok(None), @@ -1579,7 +1643,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-igmp")] fn process_igmp<'frame>( &mut self, - cx: &Context, ipv4_repr: Ipv4Repr, ip_payload: &'frame [u8], ) -> Result>> { @@ -1611,7 +1674,7 @@ impl<'a> InterfaceInner<'a> { }; self.igmp_report_state = IgmpReportState::ToGeneralQuery { version, - timeout: cx.now + interval, + timeout: self.now + interval, interval, next_index: 0, }; @@ -1623,7 +1686,7 @@ impl<'a> InterfaceInner<'a> { let timeout = max_resp_time / 4; self.igmp_report_state = IgmpReportState::ToSpecificQuery { version, - timeout: cx.now + timeout, + timeout: self.now + timeout, group: group_addr, }; } @@ -1641,7 +1704,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn process_icmpv6<'frame>( &mut self, - cx: &Context, _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], @@ -1651,7 +1713,7 @@ impl<'a> InterfaceInner<'a> { &ip_repr.src_addr(), &ip_repr.dst_addr(), &icmp_packet, - &cx.caps.checksum, + &self.caps.checksum, )?; #[cfg(feature = "socket-icmp")] @@ -1662,11 +1724,11 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) { - if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { + if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { continue; } - match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) { + match icmp_socket.process(self, &ip_repr, &icmp_repr.into()) { // The packet is valid and handled by socket. Ok(()) => handled_by_icmp_socket = true, // The socket buffer is full. @@ -1700,7 +1762,7 @@ impl<'a> InterfaceInner<'a> { // Forward any NDISC packets to the ndisc packet handler #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { - IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(cx, ipv6_repr, repr), + IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr), _ => Ok(None), }, @@ -1720,7 +1782,6 @@ impl<'a> InterfaceInner<'a> { ))] fn process_ndisc<'frame>( &mut self, - cx: &Context, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>, ) -> Result>> { @@ -1732,7 +1793,7 @@ impl<'a> InterfaceInner<'a> { } => { let ip_addr = ip_repr.src_addr.into(); if let Some(lladdr) = lladdr { - let lladdr = lladdr.parse(cx.caps.medium)?; + let lladdr = lladdr.parse(self.caps.medium)?; if !lladdr.is_unicast() || !target_addr.is_unicast() { return Err(Error::Malformed); } @@ -1741,13 +1802,13 @@ impl<'a> InterfaceInner<'a> { .neighbor_cache .as_mut() .unwrap() - .lookup(&ip_addr, cx.now) + .lookup(&ip_addr, self.now) .found() { self.neighbor_cache .as_mut() .unwrap() - .fill(ip_addr, lladdr, cx.now) + .fill(ip_addr, lladdr, self.now) } } Ok(None) @@ -1758,14 +1819,14 @@ impl<'a> InterfaceInner<'a> { .. } => { if let Some(lladdr) = lladdr { - let lladdr = lladdr.parse(cx.caps.medium)?; + let lladdr = lladdr.parse(self.caps.medium)?; if !lladdr.is_unicast() || !target_addr.is_unicast() { return Err(Error::Malformed); } self.neighbor_cache.as_mut().unwrap().fill( ip_repr.src_addr.into(), lladdr, - cx.now, + self.now, ); } @@ -1795,7 +1856,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] fn process_hopbyhop<'frame>( &mut self, - cx: &Context, sockets: &mut SocketSet, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, @@ -1823,7 +1883,6 @@ impl<'a> InterfaceInner<'a> { } } self.process_nxt_hdr( - cx, sockets, ipv6_repr, hbh_repr.next_header, @@ -1834,14 +1893,13 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] fn process_icmpv4<'frame>( - &self, - cx: &Context, + &mut self, _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Result>> { let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?; - let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &cx.caps.checksum)?; + let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)?; #[cfg(feature = "socket-icmp")] let mut handled_by_icmp_socket = false; @@ -1851,11 +1909,11 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) { - if !icmp_socket.accepts(cx, &ip_repr, &icmp_repr.into()) { + if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { continue; } - match icmp_socket.process(cx, &ip_repr, &icmp_repr.into()) { + match icmp_socket.process(self, &ip_repr, &icmp_repr.into()) { // The packet is valid and handled by socket. Ok(()) => handled_by_icmp_socket = true, // The socket buffer is full. @@ -1962,8 +2020,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] fn process_udp<'frame>( - &self, - cx: &Context, + &mut self, sockets: &mut SocketSet, ip_repr: IpRepr, handled_by_raw_socket: bool, @@ -1971,18 +2028,18 @@ impl<'a> InterfaceInner<'a> { ) -> Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_packet = UdpPacket::new_checked(ip_payload)?; - let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; + let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; let udp_payload = udp_packet.payload(); for udp_socket in sockets .iter_mut() .filter_map(|i| UdpSocket::downcast(&mut i.socket)) { - if !udp_socket.accepts(&ip_repr, &udp_repr) { + if !udp_socket.accepts(self, &ip_repr, &udp_repr) { continue; } - match udp_socket.process(cx, &ip_repr, &udp_repr, udp_payload) { + match udp_socket.process(self, &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. @@ -2024,25 +2081,24 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-tcp")] fn process_tcp<'frame>( - &self, - cx: &Context, + &mut self, sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Result>> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let tcp_packet = TcpPacket::new_checked(ip_payload)?; - let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &cx.caps.checksum)?; + let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; for tcp_socket in sockets .iter_mut() .filter_map(|i| TcpSocket::downcast(&mut i.socket)) { - if !tcp_socket.accepts(&ip_repr, &tcp_repr) { + if !tcp_socket.accepts(self, &ip_repr, &tcp_repr) { continue; } - match tcp_socket.process(cx, &ip_repr, &tcp_repr) { + match tcp_socket.process(self, &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, @@ -2063,7 +2119,7 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "medium-ethernet")] - fn dispatch(&mut self, cx: &Context, tx_token: Tx, packet: EthernetPacket) -> Result<()> + fn dispatch(&mut self, tx_token: Tx, packet: EthernetPacket) -> Result<()> where Tx: TxToken, { @@ -2077,7 +2133,7 @@ impl<'a> InterfaceInner<'a> { } => target_hardware_addr, }; - self.dispatch_ethernet(cx, tx_token, arp_repr.buffer_len(), |mut frame| { + self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { frame.set_dst_addr(dst_hardware_addr); frame.set_ethertype(EthernetProtocol::Arp); @@ -2085,24 +2141,18 @@ impl<'a> InterfaceInner<'a> { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(cx, tx_token, packet), + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet), } } #[cfg(feature = "medium-ethernet")] - fn dispatch_ethernet( - &mut self, - cx: &Context, - tx_token: Tx, - buffer_len: usize, - f: F, - ) -> Result<()> + fn dispatch_ethernet(&mut self, 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(cx.now, tx_len, |tx_buffer| { + tx_token.consume(self.now, tx_len, |tx_buffer| { debug_assert!(tx_buffer.as_ref().len() == tx_len); let mut frame = EthernetFrame::new_unchecked(tx_buffer); @@ -2137,22 +2187,22 @@ impl<'a> InterfaceInner<'a> { } } - fn has_neighbor(&self, cx: &Context, addr: &IpAddress) -> bool { - match self.route(addr, cx.now) { - Ok(_routed_addr) => match cx.caps.medium { + fn has_neighbor(&self, addr: &IpAddress) -> bool { + match self.route(addr, self.now) { + Ok(_routed_addr) => match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => self .neighbor_cache .as_ref() .unwrap() - .lookup(&_routed_addr, cx.now) + .lookup(&_routed_addr, self.now) .found(), #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => self .neighbor_cache .as_ref() .unwrap() - .lookup(&_routed_addr, cx.now) + .lookup(&_routed_addr, self.now) .found(), #[cfg(feature = "medium-ip")] Medium::Ip => true, @@ -2164,7 +2214,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] fn lookup_hardware_addr( &mut self, - cx: &Context, tx_token: Tx, src_addr: &IpAddress, dst_addr: &IpAddress, @@ -2173,7 +2222,7 @@ impl<'a> InterfaceInner<'a> { Tx: TxToken, { if dst_addr.is_broadcast() { - let hardware_addr = match cx.caps.medium { + let hardware_addr = match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST), #[cfg(feature = "medium-ieee802154")] @@ -2201,7 +2250,7 @@ impl<'a> InterfaceInner<'a> { ])) } #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(_addr) => match cx.caps.medium { + IpAddress::Ipv6(_addr) => match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ 0x33, 0x33, b[12], b[13], b[14], b[15], @@ -2219,13 +2268,13 @@ impl<'a> InterfaceInner<'a> { return Ok((hardware_addr, tx_token)); } - let dst_addr = self.route(dst_addr, cx.now)?; + let dst_addr = self.route(dst_addr, self.now)?; match self .neighbor_cache .as_mut() .unwrap() - .lookup(&dst_addr, cx.now) + .lookup(&dst_addr, self.now) { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), NeighborAnswer::RateLimited => return Err(Error::Unaddressable), @@ -2254,7 +2303,7 @@ impl<'a> InterfaceInner<'a> { target_protocol_addr: dst_addr, }; - self.dispatch_ethernet(cx, tx_token, arp_repr.buffer_len(), |mut frame| { + self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_ethertype(EthernetProtocol::Arp); @@ -2285,13 +2334,13 @@ impl<'a> InterfaceInner<'a> { solicit, )); - self.dispatch_ip(cx, tx_token, packet)?; + self.dispatch_ip(tx_token, packet)?; } _ => (), } // The request got dispatched, limit the rate on the cache. - self.neighbor_cache.as_mut().unwrap().limit_rate(cx.now); + self.neighbor_cache.as_mut().unwrap().limit_rate(self.now); Err(Error::Unaddressable) } @@ -2302,19 +2351,13 @@ impl<'a> InterfaceInner<'a> { } } - fn dispatch_ip( - &mut self, - cx: &Context, - tx_token: Tx, - packet: IpPacket, - ) -> Result<()> { + fn dispatch_ip(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; - match cx.caps.medium { + match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - cx, tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr(), @@ -2324,7 +2367,8 @@ impl<'a> InterfaceInner<'a> { (HardwareAddress::Ieee802154(_), _) => unreachable!(), }; - self.dispatch_ethernet(cx, tx_token, ip_repr.total_len(), |mut frame| { + let caps = self.caps.clone(); + self.dispatch_ethernet(tx_token, ip_repr.total_len(), |mut frame| { frame.set_dst_addr(dst_hardware_addr); match ip_repr { #[cfg(feature = "proto-ipv4")] @@ -2334,45 +2378,39 @@ impl<'a> InterfaceInner<'a> { _ => return, } - ip_repr.emit(frame.payload_mut(), &cx.caps.checksum); + ip_repr.emit(frame.payload_mut(), &caps.checksum); let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &cx.caps); + packet.emit_payload(ip_repr, payload, &caps); }) } #[cfg(feature = "medium-ip")] Medium::Ip => { let tx_len = ip_repr.total_len(); - tx_token.consume(cx.now, tx_len, |mut tx_buffer| { + tx_token.consume(self.now, tx_len, |mut tx_buffer| { debug_assert!(tx_buffer.as_ref().len() == tx_len); - ip_repr.emit(&mut tx_buffer, &cx.caps.checksum); + ip_repr.emit(&mut tx_buffer, &self.caps.checksum); let payload = &mut tx_buffer[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &cx.caps); + packet.emit_payload(ip_repr, payload, &self.caps); Ok(()) }) } #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => self.dispatch_ieee802154(cx, tx_token, packet), + Medium::Ieee802154 => self.dispatch_ieee802154(tx_token, packet), } } #[cfg(feature = "medium-ieee802154")] - fn dispatch_ieee802154( - &mut self, - cx: &Context, - tx_token: Tx, - packet: IpPacket, - ) -> Result<()> { + fn dispatch_ieee802154(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; - match cx.caps.medium { + match self.caps.medium { #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - cx, tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr(), @@ -2405,9 +2443,9 @@ impl<'a> InterfaceInner<'a> { sequence_number: Some(self.get_sequence_number()), pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: cx.pan_id, + dst_pan_id: self.pan_id, dst_addr: Some(dst_hardware_addr), - src_pan_id: cx.pan_id, + src_pan_id: self.pan_id, src_addr: ll_src_addr, }; @@ -2455,7 +2493,7 @@ impl<'a> InterfaceInner<'a> { _ => return Err(Error::Unrecognized), } - tx_token.consume(cx.now, tx_len, |mut tx_buffer| { + tx_token.consume(self.now, tx_len, |mut tx_buffer| { // 1. Create the header of 802.15.4 let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buffer); ieee_repr.emit(&mut ieee_packet); @@ -2494,7 +2532,7 @@ impl<'a> InterfaceInner<'a> { &iphc_repr.src_addr.into(), &iphc_repr.dst_addr.into(), &mut icmp_packet, - &cx.caps.checksum, + &self.caps.checksum, ); } _ => return Err(Error::Unrecognized), @@ -2682,9 +2720,8 @@ mod test { // Ensure that the unknown protocol frame does not trigger an // ICMP error response when the destination address is a // broadcast address - let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut iface.sockets, &frame), Ok(None) ); } @@ -2714,9 +2751,8 @@ mod test { // Ensure that the unknown protocol frame does not trigger an // ICMP error response when the destination address is a // broadcast address - let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv6(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv6(&mut iface.sockets, &frame), Ok(None) ); } @@ -2767,9 +2803,8 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. - let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut iface.sockets, &frame), Ok(Some(expected_repr)) ); } @@ -2892,11 +2927,10 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. - let cx = iface.context(Instant::from_secs(0)); assert_eq!( iface .inner - .process_udp(&cx, &mut iface.sockets, ip_repr, false, data), + .process_udp(&mut iface.sockets, ip_repr, false, data), Ok(Some(expected_repr)) ); @@ -2923,7 +2957,6 @@ mod test { // broadcast address and no socket is bound to the port. assert_eq!( iface.inner.process_udp( - &cx, &mut iface.sockets, ip_repr, false, @@ -2996,11 +3029,10 @@ mod test { ); // Packet should be handled by bound UDP socket - let cx = iface.context(Instant::from_secs(0)); assert_eq!( iface .inner - .process_udp(&cx, &mut iface.sockets, ip_repr, false, packet.into_inner()), + .process_udp(&mut iface.sockets, ip_repr, false, packet.into_inner()), Ok(None) ); @@ -3070,9 +3102,8 @@ mod test { }; let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); - let cx = iface.context(Instant::from_secs(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut iface.sockets, &frame), Ok(Some(expected_packet)) ); } @@ -3167,8 +3198,6 @@ 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 #[cfg(feature = "proto-ipv6")] assert_eq!( @@ -3186,7 +3215,7 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut iface.sockets, ip_repr.into(), false, payload), + .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), Ok(Some(IpPacket::Icmpv4(( expected_ip_repr, expected_icmp_repr @@ -3196,7 +3225,7 @@ mod test { assert_eq!( iface .inner - .process_udp(&cx, &mut iface.sockets, ip_repr.into(), false, payload), + .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), Ok(Some(IpPacket::Icmpv6(( expected_ip_repr, expected_icmp_repr @@ -3231,13 +3260,11 @@ mod test { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); 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(&cx, &mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3250,7 +3277,6 @@ mod test { // Ensure the address of the requestor was entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( - &cx, MockTxToken, &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr) @@ -3309,13 +3335,11 @@ 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(&cx, &mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected @@ -3325,7 +3349,6 @@ mod test { // Ensure the address of the requestor was entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( - &cx, MockTxToken, &IpAddress::Ipv6(local_ip_addr), &IpAddress::Ipv6(remote_ip_addr) @@ -3359,20 +3382,17 @@ mod test { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); 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(&cx, &mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut iface.sockets, frame.into_inner()), Ok(None) ); // Ensure the address of the requestor was NOT entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( - &cx, MockTxToken, &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), &IpAddress::Ipv4(remote_ip_addr) @@ -3410,13 +3430,11 @@ 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(&cx, &mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut iface.sockets, frame.into_inner()), Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3429,7 +3447,6 @@ mod test { // Ensure the address of the requestor was entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( - &cx, MockTxToken, &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr) @@ -3446,9 +3463,7 @@ mod test { }); // ARP cache flush after address change - assert!(!iface - .inner - .has_neighbor(&cx, &IpAddress::Ipv4(remote_ip_addr))); + assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); } #[test] @@ -3509,11 +3524,10 @@ mod test { dst_addr: ipv4_repr.src_addr, ..ipv4_repr }; - let cx = iface.context(Instant::from_secs(0)); assert_eq!( iface .inner - .process_icmpv4(&cx, &mut iface.sockets, ip_repr, icmp_data), + .process_icmpv4(&mut iface.sockets, ip_repr, icmp_data), Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) ); @@ -3606,12 +3620,10 @@ 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(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv6(&mut iface.sockets, &frame), Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))) ); } @@ -3697,8 +3709,7 @@ mod test { // loopback have been processed, including responses to // GENERAL_QUERY_BYTES. Therefore `recv_all()` would return 0 // pkts that could be checked. - let cx = iface.context(timestamp); - iface.socket_ingress(&cx); + iface.socket_ingress(); // Leave multicast groups let timestamp = Instant::now(); @@ -3778,9 +3789,8 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - let cx = iface.context(Instant::from_millis(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut iface.sockets, &frame), Ok(None) ); } @@ -3849,8 +3859,7 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - let cx = iface.context(Instant::from_millis(0)); - let frame = iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame); + let frame = iface.inner.process_ipv4(&mut iface.sockets, &frame); // because the packet could not be handled we should send an Icmp message assert!(match frame { @@ -3942,9 +3951,8 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - let cx = iface.context(Instant::from_millis(0)); assert_eq!( - iface.inner.process_ipv4(&cx, &mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut iface.sockets, &frame), Ok(None) ); diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 547358f28..11ce835c0 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -20,4 +20,4 @@ pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; pub use socket_set::{SocketHandle, SocketStorage}; -pub use self::interface::{Interface, InterfaceBuilder}; +pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 7462cbe67..10386b770 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -1,4 +1,4 @@ -use crate::socket::Context; +use crate::iface::Context; use crate::time::{Duration, Instant}; use crate::wire::dhcpv4::field as dhcpv4_field; use crate::wire::HardwareAddress; @@ -181,7 +181,7 @@ impl Dhcpv4Socket { self.ignore_naks = ignore_naks; } - pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { let t = match &self.state { ClientState::Discovering(state) => state.retry_at, ClientState::Requesting(state) => state.retry_at, @@ -192,7 +192,7 @@ impl Dhcpv4Socket { pub(crate) fn process( &mut self, - cx: &Context, + cx: &mut Context, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8], @@ -216,7 +216,7 @@ impl Dhcpv4Socket { return Ok(()); } }; - let hardware_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr { + let hardware_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr() { addr } else { return Err(Error::Malformed); @@ -254,7 +254,7 @@ impl Dhcpv4Socket { } self.state = ClientState::Requesting(RequestState { - retry_at: cx.now, + retry_at: cx.now(), retry: 0, server: ServerInfo { address: src_ip, @@ -265,7 +265,7 @@ impl Dhcpv4Socket { } (ClientState::Requesting(state), DhcpMessageType::Ack) => { if let Some((config, renew_at, expires_at)) = - Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) + Self::parse_ack(cx.now(), &dhcp_repr, self.max_lease_duration) { self.config_changed = true; self.state = ClientState::Renewing(RenewState { @@ -283,7 +283,7 @@ impl Dhcpv4Socket { } (ClientState::Renewing(state), DhcpMessageType::Ack) => { if let Some((config, renew_at, expires_at)) = - Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) + Self::parse_ack(cx.now(), &dhcp_repr, self.max_lease_duration) { state.renew_at = renew_at; state.expires_at = expires_at; @@ -379,13 +379,13 @@ impl Dhcpv4Socket { 0x12345678 } - pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> where - F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>, { // note: Dhcpv4Socket is only usable in ethernet mediums, so the // unwrap can never fail. - let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr { + let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr() { addr } else { return Err(Error::Malformed); @@ -414,7 +414,7 @@ impl Dhcpv4Socket { client_identifier: Some(ethernet_addr), server_identifier: None, parameter_request_list: Some(PARAMETER_REQUEST_LIST), - max_size: Some((cx.caps.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16), + max_size: Some((cx.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16), lease_duration: None, dns_servers: None, }; @@ -434,7 +434,7 @@ impl Dhcpv4Socket { match &mut self.state { ClientState::Discovering(state) => { - if cx.now < state.retry_at { + if cx.now() < state.retry_at { return Err(Error::Exhausted); } @@ -445,15 +445,15 @@ impl Dhcpv4Socket { dhcp_repr ); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit(cx, (ipv4_repr, udp_repr, dhcp_repr))?; // Update state AFTER the packet has been successfully sent. - state.retry_at = cx.now + DISCOVER_TIMEOUT; + state.retry_at = cx.now() + DISCOVER_TIMEOUT; self.transaction_id = next_transaction_id; Ok(()) } ClientState::Requesting(state) => { - if cx.now < state.retry_at { + if cx.now() < state.retry_at { return Err(Error::Exhausted); } @@ -474,24 +474,24 @@ impl Dhcpv4Socket { dhcp_repr ); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit(cx, (ipv4_repr, udp_repr, dhcp_repr))?; // Exponential backoff: Double every 2 retries. - state.retry_at = cx.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 <= cx.now { + if state.expires_at <= cx.now() { net_debug!("DHCP lease expired"); self.reset(); // return Ok so we get polled again return Ok(()); } - if cx.now < state.renew_at { + if cx.now() < state.renew_at { return Err(Error::Exhausted); } @@ -502,14 +502,15 @@ impl Dhcpv4Socket { net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); - emit((ipv4_repr, udp_repr, dhcp_repr))?; + emit(cx, (ipv4_repr, udp_repr, dhcp_repr))?; // In both RENEWING and REBINDING states, if the client receives no // response to its DHCPREQUEST message, the client SHOULD wait one-half // 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 = cx.now + MIN_RENEW_TIMEOUT.max((state.expires_at - cx.now) / 2); + state.renew_at = + cx.now() + MIN_RENEW_TIMEOUT.max((state.expires_at - cx.now()) / 2); self.transaction_id = next_transaction_id; Ok(()) @@ -551,17 +552,39 @@ impl Dhcpv4Socket { #[cfg(test)] mod test { + use std::ops::{Deref, DerefMut}; + use super::*; use crate::wire::EthernetAddress; // =========================================================================================// // Helper functions + struct TestSocket { + socket: Dhcpv4Socket, + cx: Context<'static>, + } + + impl Deref for TestSocket { + type Target = Dhcpv4Socket; + fn deref(&self) -> &Self::Target { + &self.socket + } + } + + impl DerefMut for TestSocket { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.socket + } + } + fn send( - socket: &mut Dhcpv4Socket, + s: &mut TestSocket, timestamp: Instant, (ip_repr, udp_repr, dhcp_repr): (Ipv4Repr, UdpRepr, DhcpRepr), ) -> Result<()> { + s.cx.set_now(timestamp); + net_trace!("send: {:?}", ip_repr); net_trace!(" {:?}", udp_repr); net_trace!(" {:?}", dhcp_repr); @@ -571,44 +594,39 @@ mod test { .emit(&mut DhcpPacket::new_unchecked(&mut payload)) .unwrap(); - let mut cx = Context::DUMMY.clone(); - cx.now = timestamp; - socket.process(&cx, &ip_repr, &udp_repr, &payload) + s.socket.process(&mut s.cx, &ip_repr, &udp_repr, &payload) } - fn recv( - socket: &mut Dhcpv4Socket, - timestamp: Instant, - reprs: &[(Ipv4Repr, UdpRepr, DhcpRepr)], - ) { - let mut cx = Context::DUMMY.clone(); - cx.now = timestamp; + fn recv(s: &mut TestSocket, timestamp: Instant, reprs: &[(Ipv4Repr, UdpRepr, DhcpRepr)]) { + s.cx.set_now(timestamp); let mut i = 0; - while socket.poll_at(&cx) <= PollAt::Time(timestamp) { - let _ = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| { - assert_eq!(ip_repr.protocol, IpProtocol::Udp); - assert_eq!( - ip_repr.payload_len, - udp_repr.header_len() + dhcp_repr.buffer_len() - ); - - // We validated the payload len, change it to 0 to make equality testing easier - ip_repr.payload_len = 0; - - net_trace!("recv: {:?}", ip_repr); - net_trace!(" {:?}", udp_repr); - net_trace!(" {:?}", dhcp_repr); - - let got_repr = (ip_repr, udp_repr, dhcp_repr); - match reprs.get(i) { - Some(want_repr) => assert_eq!(want_repr, &got_repr), - None => panic!("Too many reprs emitted"), - } - i += 1; - Ok(()) - }); + while s.socket.poll_at(&mut s.cx) <= PollAt::Time(timestamp) { + let _ = s + .socket + .dispatch(&mut s.cx, |_, (mut ip_repr, udp_repr, dhcp_repr)| { + assert_eq!(ip_repr.protocol, IpProtocol::Udp); + assert_eq!( + ip_repr.payload_len, + udp_repr.header_len() + dhcp_repr.buffer_len() + ); + + // We validated the payload len, change it to 0 to make equality testing easier + ip_repr.payload_len = 0; + + net_trace!("recv: {:?}", ip_repr); + net_trace!(" {:?}", udp_repr); + net_trace!(" {:?}", dhcp_repr); + + let got_repr = (ip_repr, udp_repr, dhcp_repr); + match reprs.get(i) { + Some(want_repr) => assert_eq!(want_repr, &got_repr), + None => panic!("Too many reprs emitted"), + } + i += 1; + Ok(()) + }); } assert_eq!(i, reprs.len()); @@ -780,13 +798,16 @@ mod test { // =========================================================================================// // Tests - fn socket() -> Dhcpv4Socket { + fn socket() -> TestSocket { let mut s = Dhcpv4Socket::new(); assert_eq!(s.poll(), Some(Event::Deconfigured)); - s + TestSocket { + socket: s, + cx: Context::mock(), + } } - fn socket_bound() -> Dhcpv4Socket { + fn socket_bound() -> TestSocket { let mut s = socket(); s.state = ClientState::Renewing(RenewState { config: Config { diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 91fb8f55e..d71e7d222 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -323,7 +323,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, cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> bool { + pub(crate) fn accepts(&self, cx: &mut 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 @@ -338,7 +338,7 @@ impl<'a> IcmpSocket<'a> { &packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), - &cx.caps.checksum, + &cx.checksum_caps(), ) { Ok(repr) => endpoint.port == repr.src_port, Err(_) => false, @@ -354,7 +354,7 @@ impl<'a> IcmpSocket<'a> { &packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), - &cx.caps.checksum, + &cx.checksum_caps(), ) { Ok(repr) => endpoint.port == repr.src_port, Err(_) => false, @@ -387,7 +387,7 @@ impl<'a> IcmpSocket<'a> { pub(crate) fn process( &mut self, - _cx: &Context, + _cx: &mut Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr, ) -> Result<()> { @@ -434,9 +434,9 @@ impl<'a> IcmpSocket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, _cx: &Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> where - F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, IcmpRepr)) -> Result<()>, { let hop_limit = self.hop_limit.unwrap_or(64); self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { @@ -457,7 +457,7 @@ impl<'a> IcmpSocket<'a> { payload_len: repr.buffer_len(), hop_limit: hop_limit, }); - emit((ip_repr, IcmpRepr::Ipv4(repr))) + emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(ipv6_addr) => { @@ -476,7 +476,7 @@ impl<'a> IcmpSocket<'a> { payload_len: repr.buffer_len(), hop_limit: hop_limit, }); - emit((ip_repr, IcmpRepr::Ipv6(repr))) + emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) } _ => Err(Error::Unaddressable), } @@ -488,7 +488,7 @@ impl<'a> IcmpSocket<'a> { Ok(()) } - pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { if self.tx_buffer.is_empty() { PollAt::Ingress } else { @@ -575,10 +575,11 @@ mod test_ipv4 { #[test] fn test_send_dispatch() { let mut socket = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); let checksum = ChecksumCapabilities::default(); assert_eq!( - socket.dispatch(&Context::DUMMY, |_| unreachable!()), + socket.dispatch(&mut cx, |_, _| unreachable!()), Err(Error::Exhausted) ); @@ -604,7 +605,7 @@ mod test_ipv4 { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { + socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); Err(Error::Unaddressable) @@ -615,7 +616,7 @@ mod test_ipv4 { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { + socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); Ok(()) @@ -629,6 +630,7 @@ mod test_ipv4 { #[test] fn test_set_hop_limit_v4() { let mut s = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; @@ -642,7 +644,7 @@ mod test_ipv4 { Ok(()) ); assert_eq!( - s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + s.dispatch(&mut cx, |_, (ip_repr, _)| { assert_eq!( ip_repr, IpRepr::Ipv4(Ipv4Repr { @@ -662,6 +664,7 @@ mod test_ipv4 { #[test] fn test_recv_process() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); assert!(!socket.can_recv()); @@ -674,16 +677,16 @@ mod test_ipv4 { ECHOV4_REPR.emit(&mut packet, &checksum); let data = &packet.into_inner()[..]; - assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); + assert!(socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); assert_eq!( - socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), + socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), Ok(()) ); assert!(socket.can_recv()); - assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); + assert!(socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); assert_eq!( - socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), + socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), Err(Error::Exhausted) ); @@ -694,6 +697,7 @@ mod test_ipv4 { #[test] fn test_accept_bad_id() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); let checksum = ChecksumCapabilities::default(); @@ -708,12 +712,13 @@ mod test_ipv4 { // Ensure that a packet with an identifier that isn't the bound // ID is not accepted - assert!(!socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &icmp_repr.into())); + assert!(!socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &icmp_repr.into())); } #[test] fn test_accepts_udp() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4)), Ok(())); let checksum = ChecksumCapabilities::default(); @@ -754,11 +759,8 @@ mod test_ipv4 { // Ensure we can accept ICMP error response to the bound // UDP port - assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into())); - assert_eq!( - socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), - Ok(()) - ); + assert!(socket.accepts(&mut cx, &ip_repr, &icmp_repr.into())); + assert_eq!(socket.process(&mut cx, &ip_repr, &icmp_repr.into()), Ok(())); assert!(socket.can_recv()); let mut bytes = [0x00; 46]; @@ -821,10 +823,11 @@ mod test_ipv6 { #[test] fn test_send_dispatch() { let mut socket = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); let checksum = ChecksumCapabilities::default(); assert_eq!( - socket.dispatch(&Context::DUMMY, |_| unreachable!()), + socket.dispatch(&mut cx, |_, _| unreachable!()), Err(Error::Exhausted) ); @@ -855,7 +858,7 @@ mod test_ipv6 { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { + socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); Err(Error::Unaddressable) @@ -866,7 +869,7 @@ mod test_ipv6 { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| { + socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); Ok(()) @@ -880,6 +883,7 @@ mod test_ipv6 { #[test] fn test_set_hop_limit() { let mut s = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); let checksum = ChecksumCapabilities::default(); let mut bytes = vec![0xff; 24]; @@ -898,7 +902,7 @@ mod test_ipv6 { Ok(()) ); assert_eq!( - s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + s.dispatch(&mut cx, |_, (ip_repr, _)| { assert_eq!( ip_repr, IpRepr::Ipv6(Ipv6Repr { @@ -918,6 +922,7 @@ mod test_ipv6 { #[test] fn test_recv_process() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); assert!(!socket.can_recv()); @@ -935,16 +940,16 @@ mod test_ipv6 { ); let data = &packet.into_inner()[..]; - assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); + assert!(socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); assert_eq!( - socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), + socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), Ok(()) ); assert!(socket.can_recv()); - assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); + assert!(socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); assert_eq!( - socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), + socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), Err(Error::Exhausted) ); @@ -955,6 +960,7 @@ mod test_ipv6 { #[test] fn test_accept_bad_id() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); let checksum = ChecksumCapabilities::default(); @@ -974,12 +980,13 @@ mod test_ipv6 { // Ensure that a packet with an identifier that isn't the bound // ID is not accepted - assert!(!socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &icmp_repr.into())); + assert!(!socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &icmp_repr.into())); } #[test] fn test_accepts_udp() { let mut socket = socket(buffer(1), buffer(1)); + let mut cx = Context::mock(); assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6)), Ok(())); let checksum = ChecksumCapabilities::default(); @@ -1020,11 +1027,8 @@ mod test_ipv6 { // Ensure we can accept ICMP error response to the bound // UDP port - assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into())); - assert_eq!( - socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()), - Ok(()) - ); + assert!(socket.accepts(&mut cx, &ip_repr, &icmp_repr.into())); + assert_eq!(socket.process(&mut cx, &ip_repr, &icmp_repr.into()), Ok(())); assert!(socket.can_recv()); let mut bytes = [0x00; 66]; diff --git a/src/socket/mod.rs b/src/socket/mod.rs index c387a9112..a98139494 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -11,7 +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::iface::Context; use crate::time::Instant; #[cfg(feature = "socket-dhcpv4")] @@ -79,7 +79,7 @@ pub enum Socket<'a> { } impl<'a> Socket<'a> { - pub(crate) fn poll_at(&self, cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, cx: &mut Context) -> PollAt { match self { #[cfg(feature = "socket-raw")] Socket::Raw(s) => s.poll_at(cx), @@ -129,54 +129,3 @@ 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(all( - any(feature = "medium-ethernet", feature = "medium-ieee802154"), - feature = "socket-dhcpv4" - ))] - pub hardware_addr: Option, - #[cfg(feature = "medium-ieee802154")] - pub pan_id: Option, - 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, - }, - #[cfg(all( - any(feature = "medium-ethernet", feature = "medium-ieee802154"), - feature = "socket-dhcpv4" - ))] - hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( - crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), - )), - now: Instant::from_millis_const(0), - - #[cfg(feature = "medium-ieee802154")] - pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), - }; -} diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 4514c97ba..b21e2d518 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -2,10 +2,11 @@ use core::cmp::min; #[cfg(feature = "async")] use core::task::Waker; +use crate::iface::Context; use crate::phy::ChecksumCapabilities; +use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; use crate::{Error, Result}; @@ -211,13 +212,18 @@ impl<'a> RawSocket<'a> { true } - pub(crate) fn process(&mut self, cx: &Context, ip_repr: &IpRepr, payload: &[u8]) -> Result<()> { + pub(crate) fn process( + &mut self, + cx: &mut 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], &cx.caps.checksum); + ip_repr.emit(&mut packet_buf[..header_len], &cx.checksum_caps()); packet_buf[header_len..].copy_from_slice(payload); net_trace!( @@ -233,9 +239,9 @@ impl<'a> RawSocket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> where - F: FnOnce((IpRepr, &[u8])) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<()>, { fn prepare<'a>( protocol: IpProtocol, @@ -278,7 +284,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, &cx.caps.checksum) { + match prepare(ip_protocol, packet_buf, &cx.checksum_caps()) { Ok((ip_repr, raw_packet)) => { net_trace!( "raw:{}:{}: sending {} octets", @@ -286,7 +292,7 @@ impl<'a> RawSocket<'a> { ip_protocol, ip_repr.buffer_len() + raw_packet.len() ); - emit((ip_repr, raw_packet)) + emit(cx, (ip_repr, raw_packet)) } Err(error) => { net_debug!( @@ -307,7 +313,7 @@ impl<'a> RawSocket<'a> { Ok(()) } - pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { if self.tx_buffer.is_empty() { PollAt::Ingress } else { @@ -418,10 +424,11 @@ mod test { #[test] fn test_send_dispatch() { let mut socket = $socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); assert!(socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |_| unreachable!()), + socket.dispatch(&mut cx, |_, _| unreachable!()), Err(Error::Exhausted) ); @@ -430,7 +437,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, ip_payload)| { assert_eq!(ip_repr, $hdr); assert_eq!(ip_payload, &$payload); Err(Error::Unaddressable) @@ -440,7 +447,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, ip_payload)| { assert_eq!(ip_repr, $hdr); assert_eq!(ip_payload, &$payload); Ok(()) @@ -453,9 +460,10 @@ mod test { #[test] fn test_recv_truncated_slice() { let mut socket = $socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); assert!(socket.accepts(&$hdr)); - assert_eq!(socket.process(&Context::DUMMY, &$hdr, &$payload), Ok(())); + assert_eq!(socket.process(&mut cx, &$hdr, &$payload), Ok(())); let mut slice = [0; 4]; assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4)); @@ -465,13 +473,14 @@ mod test { #[test] fn test_recv_truncated_packet() { let mut socket = $socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); let mut buffer = vec![0; 128]; buffer[..$packet.len()].copy_from_slice(&$packet[..]); assert!(socket.accepts(&$hdr)); assert_eq!( - socket.process(&Context::DUMMY, &$hdr, &buffer), + socket.process(&mut cx, &$hdr, &buffer), Err(Error::Truncated) ); } @@ -503,34 +512,36 @@ mod test { #[cfg(feature = "proto-ipv4")] { let mut socket = ipv4_locals::socket(buffer(0), buffer(2)); + let mut cx = Context::mock(); let mut wrong_version = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(())); + assert_eq!(socket.dispatch(&mut cx, |_, _| 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(&Context::DUMMY, |_| unreachable!()), Ok(())); + assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); } #[cfg(feature = "proto-ipv6")] { let mut socket = ipv6_locals::socket(buffer(0), buffer(2)); + let mut cx = Context::mock(); let mut wrong_version = ipv6_locals::PACKET_BYTES; Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(())); + assert_eq!(socket.dispatch(&mut cx, |_, _| 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(&Context::DUMMY, |_| unreachable!()), Ok(())); + assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); } } @@ -540,6 +551,7 @@ mod test { { let mut socket = ipv4_locals::socket(buffer(1), buffer(0)); assert!(!socket.can_recv()); + let mut cx = Context::mock(); let mut cksumd_packet = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut cksumd_packet).fill_checksum(); @@ -548,7 +560,7 @@ mod test { assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); assert_eq!( socket.process( - &Context::DUMMY, + &mut cx, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD ), @@ -559,7 +571,7 @@ mod test { assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); assert_eq!( socket.process( - &Context::DUMMY, + &mut cx, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD ), @@ -572,12 +584,13 @@ mod test { { let mut socket = ipv6_locals::socket(buffer(1), buffer(0)); assert!(!socket.can_recv()); + let mut cx = Context::mock(); assert_eq!(socket.recv(), Err(Error::Exhausted)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); assert_eq!( socket.process( - &Context::DUMMY, + &mut cx, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD ), @@ -588,7 +601,7 @@ mod test { assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); assert_eq!( socket.process( - &Context::DUMMY, + &mut cx, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD ), diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 2c17e0db7..f6602b80e 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1219,21 +1219,21 @@ impl<'a> TcpSocket<'a> { fn challenge_ack_reply( &mut self, - cx: &Context, + cx: &mut Context, ip_repr: &IpRepr, repr: &TcpRepr, ) -> Option<(IpRepr, TcpRepr<'static>)> { - if cx.now < self.challenge_ack_timer { + if cx.now() < self.challenge_ack_timer { return None; } // Rate-limit to 1 per second max. - self.challenge_ack_timer = cx.now + Duration::from_secs(1); + self.challenge_ack_timer = cx.now() + Duration::from_secs(1); return Some(self.ack_reply(ip_repr, repr)); } - pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &TcpRepr) -> bool { + pub(crate) fn accepts(&self, _cx: &mut Context, ip_repr: &IpRepr, repr: &TcpRepr) -> bool { if self.state == State::Closed { return false; } @@ -1270,11 +1270,11 @@ impl<'a> TcpSocket<'a> { pub(crate) fn process( &mut self, - cx: &Context, + cx: &mut Context, ip_repr: &IpRepr, repr: &TcpRepr, ) -> Result)>> { - debug_assert!(self.accepts(ip_repr, repr)); + debug_assert!(self.accepts(cx, ip_repr, repr)); // Consider how much the sequence number space differs from the transmit buffer space. let (sent_syn, sent_fin) = match self.state { @@ -1473,7 +1473,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(cx.now); + self.timer.set_for_close(cx.now()); } return Ok(self.challenge_ack_reply(cx, ip_repr, repr)); @@ -1508,7 +1508,7 @@ impl<'a> TcpSocket<'a> { } } - self.rtte.on_ack(cx.now, ack_number); + self.rtte.on_ack(cx.now(), ack_number); } } @@ -1580,13 +1580,13 @@ impl<'a> TcpSocket<'a> { self.remote_win_shift = 0; } self.set_state(State::SynReceived); - self.timer.set_for_idle(cx.now, 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(cx.now, 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. @@ -1596,7 +1596,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(cx.now, 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. @@ -1629,14 +1629,14 @@ impl<'a> TcpSocket<'a> { } self.set_state(State::Established); - self.timer.set_for_idle(cx.now, 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(cx.now, self.keep_alive); + self.timer.set_for_idle(cx.now(), self.keep_alive); } } @@ -1645,7 +1645,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(cx.now, 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 @@ -1654,7 +1654,7 @@ impl<'a> TcpSocket<'a> { if ack_of_fin { self.set_state(State::FinWait2); } - self.timer.set_for_idle(cx.now, 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 @@ -1664,16 +1664,16 @@ impl<'a> TcpSocket<'a> { self.rx_fin_received = true; if ack_of_fin { self.set_state(State::TimeWait); - self.timer.set_for_close(cx.now); + self.timer.set_for_close(cx.now()); } else { self.set_state(State::Closing); - self.timer.set_for_idle(cx.now, 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(cx.now, 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. @@ -1681,22 +1681,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(cx.now); + 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(cx.now); + self.timer.set_for_close(cx.now()); } else { - self.timer.set_for_idle(cx.now, 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(cx.now, self.keep_alive); + self.timer.set_for_idle(cx.now(), self.keep_alive); } // ACK packets in LAST-ACK state change it to CLOSED. @@ -1707,7 +1707,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); } else { - self.timer.set_for_idle(cx.now, self.keep_alive); + self.timer.set_for_idle(cx.now(), self.keep_alive); } } @@ -1723,7 +1723,7 @@ impl<'a> TcpSocket<'a> { } // Update remote state. - self.remote_last_ts = Some(cx.now); + 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. @@ -1892,7 +1892,7 @@ impl<'a> TcpSocket<'a> { self.remote_endpoint ); - AckDelayTimer::Waiting(cx.now + ack_delay) + AckDelayTimer::Waiting(cx.now() + ack_delay) } // RFC1122 says "in a stream of full-sized segments there SHOULD be an ACK // for at least every second segment". @@ -1942,7 +1942,7 @@ impl<'a> TcpSocket<'a> { } } - fn seq_to_transmit(&self, cx: &Context) -> bool { + fn seq_to_transmit(&self, cx: &mut Context) -> bool { let ip_header_len = match self.local_endpoint.addr { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(_) => crate::wire::IPV4_HEADER_LEN, @@ -1952,7 +1952,7 @@ impl<'a> TcpSocket<'a> { }; // Max segment size we're able to send due to MTU limitations. - let local_mss = cx.caps.ip_mtu() - ip_header_len - TCP_HEADER_LEN; + let local_mss = cx.ip_mtu() - ip_header_len - TCP_HEADER_LEN; // The effective max segment size, taking into account our and remote's limits. let effective_mss = local_mss.min(self.remote_mss); @@ -2029,9 +2029,9 @@ impl<'a> TcpSocket<'a> { } } - pub(crate) fn dispatch(&mut self, cx: &Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> where - F: FnOnce((IpRepr, TcpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<()>, { if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted); @@ -2045,11 +2045,11 @@ 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(cx.now); + self.remote_last_ts = Some(cx.now()); } // Check if any state needs to be changed because of a timer. - if self.timed_out(cx.now) { + if self.timed_out(cx.now()) { // If a timeout expires, we should abort the connection. net_debug!( "tcp:{}:{}: timeout exceeded", @@ -2058,7 +2058,7 @@ impl<'a> TcpSocket<'a> { ); self.set_state(State::Closed); } else if !self.seq_to_transmit(cx) { - if let Some(retransmit_delta) = self.timer.should_retransmit(cx.now) { + 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!( "tcp:{}:{}: retransmitting at t+{}", @@ -2076,7 +2076,7 @@ impl<'a> TcpSocket<'a> { // now for whatever reason (like zero window), this avoids an // infinite polling loop where `poll_at` returns `Now` but `dispatch` // can't actually do anything. - self.timer.set_for_idle(cx.now, self.keep_alive); + self.timer.set_for_idle(cx.now(), self.keep_alive); // Inform RTTE, so that it can avoid bogus measurements. self.rtte.on_retransmit(); @@ -2091,14 +2091,14 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - } else if self.ack_to_transmit() && self.delayed_ack_expired(cx.now) { + } else if self.ack_to_transmit() && self.delayed_ack_expired(cx.now()) { // If we have data to acknowledge, do it. net_trace!( "tcp:{}:{}: outgoing segment will acknowledge", self.local_endpoint, self.remote_endpoint ); - } else if self.window_to_update() && self.delayed_ack_expired(cx.now) { + } else if self.window_to_update() && self.delayed_ack_expired(cx.now()) { // If we have window length increase to advertise, do it. net_trace!( "tcp:{}:{}: outgoing segment will update window", @@ -2112,14 +2112,14 @@ impl<'a> TcpSocket<'a> { self.local_endpoint, self.remote_endpoint ); - } else if self.timer.should_keep_alive(cx.now) { + } else if self.timer.should_keep_alive(cx.now()) { // If we need to transmit a keep-alive packet, do it. net_trace!( "tcp:{}:{}: keep-alive timer expired", self.local_endpoint, self.remote_endpoint ); - } else if self.timer.should_close(cx.now) { + } else if self.timer.should_close(cx.now()) { // If we have spent enough time in the TIME-WAIT state, close the socket. net_trace!( "tcp:{}:{}: TIME-WAIT timer expired", @@ -2216,7 +2216,7 @@ impl<'a> TcpSocket<'a> { // 3. MSS we can send, determined by our MTU. let size = win_limit .min(self.remote_mss) - .min(cx.caps.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN); + .min(cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN); let offset = self.remote_last_seq - self.local_seq_no; repr.payload = self.tx_buffer.get_allocated(offset, size); @@ -2245,7 +2245,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(cx.now) && 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; @@ -2289,7 +2289,7 @@ impl<'a> TcpSocket<'a> { if repr.control == TcpControl::Syn { // Fill the MSS option. See RFC 6691 for an explanation of this calculation. - let max_segment_size = cx.caps.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN; + let max_segment_size = cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN; repr.max_seg_size = Some(max_segment_size as u16); } @@ -2301,11 +2301,11 @@ impl<'a> TcpSocket<'a> { // to not waste time waiting for the retransmit timer on packets that we know // for sure will not be successfully transmitted. ip_repr.set_payload_len(repr.buffer_len()); - emit((ip_repr, repr))?; + emit(cx, (ip_repr, repr))?; // We've sent something, whether useful data or a keep-alive packet, so rewind // the keep-alive timer. - self.timer.rewind_keep_alive(cx.now, self.keep_alive); + self.timer.rewind_keep_alive(cx.now(), self.keep_alive); // Reset delayed-ack timer match self.ack_delay_timer { @@ -2340,14 +2340,14 @@ impl<'a> TcpSocket<'a> { if repr.segment_len() > 0 { self.rtte - .on_send(cx.now, repr.seq_number + repr.segment_len()); + .on_send(cx.now(), repr.seq_number + repr.segment_len()); } if !self.seq_to_transmit(cx) && 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(cx.now, self.rtte.retransmission_timeout()); + .set_for_retransmit(cx.now(), self.rtte.retransmission_timeout()); } if self.state == State::Closed { @@ -2360,7 +2360,7 @@ impl<'a> TcpSocket<'a> { } #[allow(clippy::if_same_then_else)] - pub(crate) fn poll_at(&self, cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, cx: &mut 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. @@ -2418,6 +2418,7 @@ mod test { use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; use crate::wire::{IpAddress, IpCidr, IpRepr}; use core::i32; + use std::ops::{Deref, DerefMut}; use std::vec::Vec; // =========================================================================================// @@ -2487,11 +2488,31 @@ mod test { // Helper functions // =========================================================================================// + struct TestSocket { + socket: TcpSocket<'static>, + cx: Context<'static>, + } + + impl Deref for TestSocket { + type Target = TcpSocket<'static>; + fn deref(&self) -> &Self::Target { + &self.socket + } + } + + impl DerefMut for TestSocket { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.socket + } + } + fn send( - socket: &mut TcpSocket, + socket: &mut TestSocket, timestamp: Instant, repr: &TcpRepr, ) -> Result>> { + socket.cx.set_now(timestamp); + let ip_repr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_2, dst_addr: MOCK_IP_ADDR_1, @@ -2501,11 +2522,9 @@ mod test { }; net_trace!("send: {}", repr); - assert!(socket.accepts(&ip_repr, repr)); + assert!(socket.socket.accepts(&mut socket.cx, &ip_repr, repr)); - let mut cx = Context::DUMMY.clone(); - cx.now = timestamp; - match socket.process(&cx, &ip_repr, repr) { + match socket.socket.process(&mut socket.cx, &ip_repr, repr) { Ok(Some((_ip_repr, repr))) => { net_trace!("recv: {}", repr); Ok(Some(repr)) @@ -2515,23 +2534,25 @@ mod test { } } - fn recv(socket: &mut TcpSocket, timestamp: Instant, mut f: F) + fn recv(socket: &mut TestSocket, timestamp: Instant, mut f: F) where F: FnMut(Result), { - 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); - assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1); - assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2); - assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len()); - - net_trace!("recv: {}", tcp_repr); - Ok(f(Ok(tcp_repr))) - }); + socket.cx.set_now(timestamp); + + let result = socket + .socket + .dispatch(&mut socket.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); + assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1); + assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2); + assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len()); + + net_trace!("recv: {}", tcp_repr); + Ok(f(Ok(tcp_repr))) + }); match result { Ok(()) => (), Err(e) => f(Err(e)), @@ -2586,19 +2607,20 @@ mod test { }}; } - fn socket() -> TcpSocket<'static> { + fn socket() -> TestSocket { socket_with_buffer_sizes(64, 64) } - fn socket_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { + fn socket_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let rx_buffer = SocketBuffer::new(vec![0; rx_len]); let tx_buffer = SocketBuffer::new(vec![0; tx_len]); let mut socket = TcpSocket::new(rx_buffer, tx_buffer); socket.set_ack_delay(None); - socket + let cx = Context::mock(); + TestSocket { socket, cx } } - fn socket_syn_received_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { + fn socket_syn_received_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynReceived; s.local_endpoint = LOCAL_END; @@ -2610,11 +2632,11 @@ mod test { s } - fn socket_syn_received() -> TcpSocket<'static> { + fn socket_syn_received() -> TestSocket { socket_syn_received_with_buffer_sizes(64, 64) } - fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { + fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynSent; s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT); @@ -2624,11 +2646,11 @@ mod test { s } - fn socket_syn_sent() -> TcpSocket<'static> { + fn socket_syn_sent() -> TestSocket { socket_syn_sent_with_buffer_sizes(64, 64) } - fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TcpSocket<'static> { + fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TestSocket { let mut s = socket(); s.state = State::SynSent; s.local_endpoint = local; @@ -2638,7 +2660,7 @@ mod test { s } - fn socket_established_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TcpSocket<'static> { + fn socket_established_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_syn_received_with_buffer_sizes(tx_len, rx_len); s.state = State::Established; s.local_seq_no = LOCAL_SEQ + 1; @@ -2648,17 +2670,17 @@ mod test { s } - fn socket_established() -> TcpSocket<'static> { + fn socket_established() -> TestSocket { socket_established_with_buffer_sizes(64, 64) } - fn socket_fin_wait_1() -> TcpSocket<'static> { + fn socket_fin_wait_1() -> TestSocket { let mut s = socket_established(); s.state = State::FinWait1; s } - fn socket_fin_wait_2() -> TcpSocket<'static> { + fn socket_fin_wait_2() -> TestSocket { let mut s = socket_fin_wait_1(); s.state = State::FinWait2; s.local_seq_no = LOCAL_SEQ + 1 + 1; @@ -2666,7 +2688,7 @@ mod test { s } - fn socket_closing() -> TcpSocket<'static> { + fn socket_closing() -> TestSocket { let mut s = socket_fin_wait_1(); s.state = State::Closing; s.remote_last_seq = LOCAL_SEQ + 1 + 1; @@ -2674,7 +2696,7 @@ mod test { s } - fn socket_time_wait(from_closing: bool) -> TcpSocket<'static> { + fn socket_time_wait(from_closing: bool) -> TestSocket { let mut s = socket_fin_wait_2(); s.state = State::TimeWait; s.remote_seq_no = REMOTE_SEQ + 1 + 1; @@ -2687,7 +2709,7 @@ mod test { s } - fn socket_close_wait() -> TcpSocket<'static> { + fn socket_close_wait() -> TestSocket { let mut s = socket_established(); s.state = State::CloseWait; s.remote_seq_no = REMOTE_SEQ + 1 + 1; @@ -2695,13 +2717,13 @@ mod test { s } - fn socket_last_ack() -> TcpSocket<'static> { + fn socket_last_ack() -> TestSocket { let mut s = socket_close_wait(); s.state = State::LastAck; s } - fn socket_recved() -> TcpSocket<'static> { + fn socket_recved() -> TestSocket { let mut s = socket_established(); send!( s, @@ -2729,14 +2751,14 @@ mod test { // =========================================================================================// #[test] fn test_closed_reject() { - let s = socket(); + let mut s = socket(); assert_eq!(s.state, State::Closed); let tcp_repr = TcpRepr { control: TcpControl::Syn, ..SEND_TEMPL }; - assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &SEND_IP_TEMPL, &tcp_repr)); } #[test] @@ -2749,7 +2771,7 @@ mod test { control: TcpControl::Syn, ..SEND_TEMPL }; - assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &SEND_IP_TEMPL, &tcp_repr)); } #[test] @@ -2762,7 +2784,7 @@ mod test { // =========================================================================================// // Tests for the LISTEN state. // =========================================================================================// - fn socket_listen() -> TcpSocket<'static> { + fn socket_listen() -> TestSocket { let mut s = socket(); s.state = State::Listen; s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); @@ -2902,7 +2924,7 @@ mod test { #[test] fn test_listen_syn_reject_ack() { - let s = socket_listen(); + let mut s = socket_listen(); let tcp_repr = TcpRepr { control: TcpControl::Syn, @@ -2910,7 +2932,7 @@ mod test { ack_number: Some(LOCAL_SEQ), ..SEND_TEMPL }; - assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &SEND_IP_TEMPL, &tcp_repr)); assert_eq!(s.state, State::Listen); } @@ -3061,14 +3083,11 @@ mod test { }] ); assert_eq!(s.state, State::CloseWait); - sanity!( - s, - TcpSocket { - remote_last_ack: Some(REMOTE_SEQ + 1 + 6 + 1), - remote_last_win: 58, - ..socket_close_wait() - } - ); + + let mut s2 = socket_close_wait(); + s2.remote_last_ack = Some(REMOTE_SEQ + 1 + 6 + 1); + s2.remote_last_win = 58; + sanity!(s, s2); } #[test] @@ -3196,22 +3215,23 @@ mod test { fn test_connect_validation() { let mut s = socket(); assert_eq!( - s.connect((IpAddress::Unspecified, 80), LOCAL_END), + s.socket.connect((IpAddress::Unspecified, 80), LOCAL_END), Err(Error::Unaddressable) ); assert_eq!( - s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)), + s.socket.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)), Err(Error::Unaddressable) ); assert_eq!( - s.connect((MOCK_UNSPECIFIED, 0), LOCAL_END), + s.socket.connect((MOCK_UNSPECIFIED, 0), LOCAL_END), Err(Error::Unaddressable) ); assert_eq!( - s.connect((IpAddress::Unspecified, 80), LOCAL_END), + s.socket.connect((IpAddress::Unspecified, 80), LOCAL_END), Err(Error::Unaddressable) ); - s.connect(REMOTE_END, LOCAL_END) + s.socket + .connect(REMOTE_END, LOCAL_END) .expect("Connect failed with valid parameters"); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); @@ -3221,7 +3241,7 @@ mod test { fn test_connect() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.connect(REMOTE_END, LOCAL_END.port).unwrap(); + s.socket.connect(REMOTE_END, LOCAL_END.port).unwrap(); assert_eq!( s.local_endpoint, IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port) @@ -3255,24 +3275,30 @@ mod test { #[test] fn test_connect_unspecified_local() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)), Ok(())); + assert_eq!(s.socket.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)), Ok(())); s.abort(); - assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), Ok(())); + assert_eq!( + s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + Ok(()) + ); s.abort(); } #[test] fn test_connect_specified_local() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)), Ok(())); + assert_eq!(s.socket.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)), Ok(())); } #[test] fn test_connect_twice() { let mut s = socket(); - assert_eq!(s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), Ok(())); assert_eq!( - s.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + Ok(()) + ); + assert_eq!( + s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), Err(Error::Illegal) ); } @@ -3281,7 +3307,7 @@ mod test { fn test_syn_sent_sanity() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.connect(REMOTE_END, LOCAL_END).unwrap(); + s.socket.connect(REMOTE_END, LOCAL_END).unwrap(); sanity!(s, socket_syn_sent_with_local_ipendpoint(LOCAL_END)); } @@ -3536,7 +3562,7 @@ mod test { let mut s = socket_with_buffer_sizes(64, *buffer_size); s.local_seq_no = LOCAL_SEQ; assert_eq!(s.remote_win_shift, *shift_amt); - s.connect(REMOTE_END, LOCAL_END).unwrap(); + s.socket.connect(REMOTE_END, LOCAL_END).unwrap(); recv!( s, [TcpRepr { @@ -3650,7 +3676,7 @@ mod test { assert_eq!(s.rx_buffer.dequeue_many(6), &b"abcdef"[..]); } - fn setup_rfc2018_cases() -> (TcpSocket<'static>, Vec) { + fn setup_rfc2018_cases() -> (TestSocket, Vec) { // This is a utility function used by the tests for RFC 2018 cases. It configures a socket // in a particular way suitable for those cases. // @@ -6028,14 +6054,14 @@ mod test { fn test_listen_timeout() { let mut s = socket_listen(); s.set_timeout(Some(Duration::from_millis(100))); - assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Ingress); + assert_eq!(s.socket.poll_at(&mut s.cx), PollAt::Ingress); } #[test] fn test_connect_timeout() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.connect(REMOTE_END, LOCAL_END.port).unwrap(); + s.socket.connect(REMOTE_END, LOCAL_END.port).unwrap(); s.set_timeout(Some(Duration::from_millis(100))); recv!(s, time 150, Ok(TcpRepr { control: TcpControl::Syn, @@ -6048,7 +6074,7 @@ mod test { })); assert_eq!(s.state, State::SynSent); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(250)) ); recv!(s, time 250, Ok(TcpRepr { @@ -6067,11 +6093,11 @@ mod test { s.set_timeout(Some(Duration::from_millis(1000))); recv!(s, time 250, Err(Error::Exhausted)); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(1250)) ); s.send_slice(b"abcdef").unwrap(); - assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Now); + assert_eq!(s.socket.poll_at(&mut s.cx), PollAt::Now); recv!(s, time 255, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), @@ -6079,7 +6105,7 @@ mod test { ..RECV_TEMPL })); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(955)) ); recv!(s, time 955, Ok(TcpRepr { @@ -6089,7 +6115,7 @@ mod test { ..RECV_TEMPL })); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(1255)) ); recv!(s, time 1255, Ok(TcpRepr { @@ -6114,7 +6140,7 @@ mod test { })); recv!(s, time 100, Err(Error::Exhausted)); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(150)) ); send!(s, time 105, TcpRepr { @@ -6123,7 +6149,7 @@ mod test { ..SEND_TEMPL }); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(155)) ); recv!(s, time 155, Ok(TcpRepr { @@ -6134,7 +6160,7 @@ mod test { })); recv!(s, time 155, Err(Error::Exhausted)); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(205)) ); recv!(s, time 200, Err(Error::Exhausted)); @@ -6192,14 +6218,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(&Context::DUMMY), PollAt::Now); + assert_eq!(s.socket.poll_at(&mut s.cx), 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(&Context::DUMMY), PollAt::Ingress); + assert_eq!(s.socket.poll_at(&mut s.cx), PollAt::Ingress); } // =========================================================================================// @@ -6230,7 +6256,7 @@ mod test { s.set_keep_alive(Some(Duration::from_millis(100))); // drain the forced keep-alive packet - assert_eq!(s.poll_at(&Context::DUMMY), PollAt::Now); + assert_eq!(s.socket.poll_at(&mut s.cx), PollAt::Now); recv!(s, time 0, Ok(TcpRepr { seq_number: LOCAL_SEQ, ack_number: Some(REMOTE_SEQ + 1), @@ -6239,7 +6265,7 @@ mod test { })); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(100)) ); recv!(s, time 95, Err(Error::Exhausted)); @@ -6251,7 +6277,7 @@ mod test { })); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(200)) ); recv!(s, time 195, Err(Error::Exhausted)); @@ -6268,7 +6294,7 @@ mod test { ..SEND_TEMPL }); assert_eq!( - s.poll_at(&Context::DUMMY), + s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(350)) ); recv!(s, time 345, Err(Error::Exhausted)); @@ -6290,7 +6316,7 @@ mod test { s.set_hop_limit(Some(0x2a)); assert_eq!( - s.dispatch(&Context::DUMMY, |(ip_repr, _)| { + s.socket.dispatch(&mut s.cx, |_, (ip_repr, _)| { assert_eq!(ip_repr.hop_limit(), 0x2a); Ok(()) }), @@ -6879,7 +6905,7 @@ mod test { dst_port: LOCAL_PORT + 1, ..SEND_TEMPL }; - assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &SEND_IP_TEMPL, &tcp_repr)); let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, @@ -6887,12 +6913,12 @@ mod test { src_port: REMOTE_PORT + 1, ..SEND_TEMPL }; - assert!(!s.accepts(&SEND_IP_TEMPL, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &SEND_IP_TEMPL, &tcp_repr)); } #[test] fn test_doesnt_accept_wrong_ip() { - let s = socket_established(); + let mut s = socket_established(); let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, @@ -6908,7 +6934,7 @@ mod test { payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; - assert!(s.accepts(&ip_repr, &tcp_repr)); + assert!(s.socket.accepts(&mut s.cx, &ip_repr, &tcp_repr)); let ip_repr_wrong_src = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_3, @@ -6917,7 +6943,7 @@ mod test { payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; - assert!(!s.accepts(&ip_repr_wrong_src, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_src, &tcp_repr)); let ip_repr_wrong_dst = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_2, @@ -6926,7 +6952,7 @@ mod test { payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; - assert!(!s.accepts(&ip_repr_wrong_dst, &tcp_repr)); + assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_dst, &tcp_repr)); } // =========================================================================================// diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 314d43460..88cee7fc0 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -2,9 +2,10 @@ use core::cmp::min; #[cfg(feature = "async")] use core::task::Waker; +use crate::iface::Context; +use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::socket::{Context, PollAt}; use crate::storage::{PacketBuffer, PacketMetadata}; use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; @@ -291,7 +292,7 @@ impl<'a> UdpSocket<'a> { Ok((length, endpoint)) } - pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool { + pub(crate) fn accepts(&self, _cx: &mut Context, ip_repr: &IpRepr, repr: &UdpRepr) -> bool { if self.endpoint.port != repr.dst_port { return false; } @@ -308,12 +309,12 @@ impl<'a> UdpSocket<'a> { pub(crate) fn process( &mut self, - _cx: &Context, + cx: &mut Context, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8], ) -> Result<()> { - debug_assert!(self.accepts(ip_repr, repr)); + debug_assert!(self.accepts(cx, ip_repr, repr)); let size = payload.len(); @@ -338,9 +339,9 @@ impl<'a> UdpSocket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, _cx: &Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> where - F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<()>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); @@ -365,7 +366,7 @@ impl<'a> UdpSocket<'a> { payload_len: repr.header_len() + payload_buf.len(), hop_limit: hop_limit, }; - emit((ip_repr, repr, payload_buf)) + emit(cx, (ip_repr, repr, payload_buf)) })?; #[cfg(feature = "async")] @@ -374,7 +375,7 @@ impl<'a> UdpSocket<'a> { Ok(()) } - pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { + pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { if self.tx_buffer.is_empty() { PollAt::Ingress } else { @@ -484,6 +485,7 @@ mod test { #[test] fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); + assert_eq!( socket.send_slice(b"abcdef", REMOTE_END), Err(Error::Unaddressable) @@ -515,11 +517,13 @@ mod test { #[test] fn test_send_dispatch() { let mut socket = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_END), Ok(())); assert!(socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |_| unreachable!()), + socket.dispatch(&mut cx, |_, _| unreachable!()), Err(Error::Exhausted) ); @@ -531,7 +535,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -542,7 +546,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -556,31 +560,23 @@ mod test { #[test] fn test_recv_process() { let mut socket = socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert!(!socket.can_recv()); assert_eq!(socket.recv(), Err(Error::Exhausted)); - assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); assert_eq!( - socket.process( - &Context::DUMMY, - &remote_ip_repr(), - &REMOTE_UDP_REPR, - PAYLOAD - ), + socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); assert!(socket.can_recv()); - assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); assert_eq!( - socket.process( - &Context::DUMMY, - &remote_ip_repr(), - &REMOTE_UDP_REPR, - PAYLOAD - ), + socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Err(Error::Exhausted) ); assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); @@ -590,17 +586,14 @@ mod test { #[test] fn test_peek_process() { let mut socket = socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert_eq!(socket.peek(), Err(Error::Exhausted)); assert_eq!( - socket.process( - &Context::DUMMY, - &remote_ip_repr(), - &REMOTE_UDP_REPR, - PAYLOAD - ), + socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END))); @@ -611,16 +604,13 @@ mod test { #[test] fn test_recv_truncated_slice() { let mut socket = socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); assert_eq!( - socket.process( - &Context::DUMMY, - &remote_ip_repr(), - &REMOTE_UDP_REPR, - PAYLOAD - ), + socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); @@ -632,15 +622,12 @@ mod test { #[test] fn test_peek_truncated_slice() { let mut socket = socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert_eq!( - socket.process( - &Context::DUMMY, - &remote_ip_repr(), - &REMOTE_UDP_REPR, - PAYLOAD - ), + socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); @@ -655,12 +642,14 @@ mod test { #[test] fn test_set_hop_limit() { let mut s = socket(buffer(0), buffer(1)); + let mut cx = Context::mock(); + assert_eq!(s.bind(LOCAL_END), Ok(())); s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(())); assert_eq!( - s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| { + s.dispatch(&mut cx, |_, (ip_repr, _, _)| { assert_eq!( ip_repr, IpRepr::Unspecified { @@ -680,12 +669,14 @@ mod test { #[test] fn test_doesnt_accept_wrong_port() { let mut socket = socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); let mut udp_repr = REMOTE_UDP_REPR; - assert!(socket.accepts(&remote_ip_repr(), &udp_repr)); + assert!(socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr)); udp_repr.dst_port += 1; - assert!(!socket.accepts(&remote_ip_repr(), &udp_repr)); + assert!(!socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr)); } #[test] @@ -712,13 +703,15 @@ mod test { } } + let mut cx = Context::mock(); + let mut port_bound_socket = socket(buffer(1), buffer(0)); assert_eq!(port_bound_socket.bind(LOCAL_PORT), Ok(())); - assert!(port_bound_socket.accepts(&generate_bad_repr(), &REMOTE_UDP_REPR)); + assert!(port_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR)); let mut ip_bound_socket = socket(buffer(1), buffer(0)); assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(())); - assert!(!ip_bound_socket.accepts(&generate_bad_repr(), &REMOTE_UDP_REPR)); + assert!(!ip_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR)); } #[test] @@ -739,6 +732,8 @@ mod test { fn test_process_empty_payload() { let recv_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![]); let mut socket = socket(recv_buffer, buffer(0)); + let mut cx = Context::mock(); + assert_eq!(socket.bind(LOCAL_PORT), Ok(())); let repr = UdpRepr { @@ -746,7 +741,7 @@ mod test { dst_port: LOCAL_PORT, }; assert_eq!( - socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), + socket.process(&mut cx, &remote_ip_repr(), &repr, &[]), Ok(()) ); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); From 3644b94b82d9433313c75281fdc78942c2450bdf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Nov 2021 03:52:08 +0100 Subject: [PATCH 276/566] rand: use simple PRNG owned by Interface, sockets access it through Context. --- .github/workflows/test.yml | 6 ++-- Cargo.toml | 1 - examples/client.rs | 4 +-- examples/httpclient.rs | 4 +-- examples/loopback.rs | 11 ++---- src/iface/interface.rs | 36 +++++++++++++++++++ src/lib.rs | 3 -- src/rand.rs | 73 +++++++++----------------------------- src/socket/dhcpv4.rs | 8 ++--- src/socket/tcp.rs | 71 ++++++++++++++++++++++++------------ 10 files changed, 114 insertions(+), 103 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d46e011d4..7edeffcc9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: include: # Test alloc feature which requires nightly. - rust: nightly - features: alloc rand-custom-impl medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -73,8 +73,8 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - rand-custom-impl medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - rand-custom-impl defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 185b6bd73..17884dc4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,6 @@ url = "1.0" std = ["managed/std", "rand_core/std"] alloc = ["managed/alloc"] verbose = [] -rand-custom-impl = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] "medium-ieee802154" = ["socket", "proto-sixlowpan"] diff --git a/examples/client.rs b/examples/client.rs index 00e7c709b..5aa4d1b79 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -54,8 +54,8 @@ fn main() { let tcp_handle = iface.add_socket(tcp_socket); - let socket = iface.get_socket::(tcp_handle); - socket.connect((address, port), 49500).unwrap(); + let (socket, cx) = iface.get_socket_and_context::(tcp_handle); + socket.connect(cx, (address, port), 49500).unwrap(); let mut tcp_active = false; loop { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index d1370c1b4..8748c0896 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -76,14 +76,14 @@ fn main() { } } - let socket = iface.get_socket::(tcp_handle); + let (socket, cx) = iface.get_socket_and_context::(tcp_handle); state = match state { State::Connect if !socket.is_active() => { debug!("connecting"); let local_port = 49152 + rand::random::() % 16384; socket - .connect((address, url.port().unwrap_or(80)), local_port) + .connect(cx, (address, url.port().unwrap_or(80)), local_port) .unwrap(); State::Request } diff --git a/examples/loopback.rs b/examples/loopback.rs index 9b8abf330..a3f6a3552 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -37,14 +37,6 @@ mod mock { self.0.get() } } - - struct Rand; - smoltcp::rand_custom_impl!(Rand); - impl smoltcp::Rand for Rand { - fn rand_bytes(buf: &mut [u8]) { - buf.fill(0x42); - } - } } #[cfg(feature = "std")] @@ -153,12 +145,13 @@ fn main() { done = true; } - let mut socket = iface.get_socket::(client_handle); + let (mut socket, cx) = iface.get_socket_and_context::(client_handle); if !socket.is_open() { if !did_connect { debug!("connecting"); socket .connect( + cx, (IpAddress::v4(127, 0, 0, 1), 1234), (IpAddress::Unspecified, 65000), ) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 76d46c6dc..c4da9f6db 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -11,6 +11,7 @@ use crate::iface::Routes; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::iface::{NeighborAnswer, NeighborCache}; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; +use crate::rand::Rand; use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; @@ -54,6 +55,7 @@ pub struct InterfaceInner<'a> { /// When to report for (all or) the next multicast group membership via IGMP #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState, + rand: Rand, } /// A builder structure used for creating a network interface. @@ -75,6 +77,7 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + random_seed: u64, } impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> @@ -134,9 +137,21 @@ let iface = InterfaceBuilder::new(device, vec![]) routes: Routes::new(ManagedMap::Borrowed(&mut [])), #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + random_seed: 0, } } + /// Set the random seed for this interface. + /// + /// It is strongly recommended that the random seed is different on each boot, + /// to avoid problems with TCP port/sequence collisions. + /// + /// The seed doesn't have to be cryptographically secure. + pub fn random_seed(mut self, random_seed: u64) -> Self { + self.random_seed = random_seed; + self + } + /// Set the Hardware address the interface will use. See also /// [hardware_addr]. /// @@ -319,6 +334,7 @@ let iface = InterfaceBuilder::new(device, vec![]) sequence_no: self.sequence_no, #[cfg(feature = "medium-ieee802154")] pan_id: self.pan_id, + rand: Rand::new(self.random_seed), }, } } @@ -495,6 +511,20 @@ where self.sockets.get(handle) } + /// Get a socket by handle, and the socket context. + /// + /// The context is needed for some socket methods. + /// + /// # Panics + /// This function may panic if the handle does not belong to this socket set + /// or the socket has the wrong type. + pub fn get_socket_and_context>( + &mut self, + handle: SocketHandle, + ) -> (&mut T, &mut InterfaceInner<'a>) { + (self.sockets.get(handle), &mut self.inner) + } + /// Remove a socket from the set, without changing its state. /// /// # Panics @@ -1018,6 +1048,11 @@ impl<'a> InterfaceInner<'a> { self.caps.ip_mtu() } + #[allow(unused)] // unused depending on which sockets are enabled, and in tests + pub(crate) fn rand(&mut self) -> &mut Rand { + &mut self.rand + } + #[cfg(test)] pub(crate) fn mock() -> Self { Self { @@ -1044,6 +1079,7 @@ impl<'a> InterfaceInner<'a> { now: Instant::from_millis_const(0), ip_addrs: ManagedSlice::Owned(vec![]), + rand: Rand::new(1234), routes: Routes::new(&mut [][..]), #[cfg(feature = "proto-ipv4")] diff --git a/src/lib.rs b/src/lib.rs index cbe9d8b08..f86842d8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,10 +134,7 @@ use core::fmt; #[macro_use] mod macros; mod parsers; - mod rand; -#[cfg(feature = "rand-custom-impl")] -pub use crate::rand::Rand; #[cfg(any( feature = "medium-ethernet", diff --git a/src/rand.rs b/src/rand.rs index 93f5a009d..48090586d 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -1,67 +1,26 @@ #![allow(unsafe_code)] #![allow(unused)] -#[cfg(not(any(test, feature = "std", feature = "rand-custom-impl")))] -compile_error!("None of the Cargo features `std` or `rand-custom-impl` is enabled. smoltcp needs a `rand` implementation to work. If your target supports `std`, enable the `std` feature to use the OS's RNG. Otherwise, you must enable the `rand-custom-impl` Cargo feature, and supply your own custom implementation using the `smoltcp::rand_custom_impl!()` macro"); - -pub fn rand_u32() -> u32 { - let mut val = [0; 4]; - rand_bytes(&mut val); - u32::from_ne_bytes(val) +#[derive(Debug)] +pub(crate) struct Rand { + state: u64, } -/// Fill `buf` with random bytes. -pub fn rand_bytes(buf: &mut [u8]) { - extern "Rust" { - fn _smoltcp_rand(buf: &mut [u8]); +impl Rand { + pub(crate) const fn new(seed: u64) -> Self { + Self { state: seed } } - unsafe { _smoltcp_rand(buf) } -} + pub(crate) fn rand_u32(&mut self) -> u32 { + // sPCG32 from https://www.pcg-random.org/paper.html + // see also https://nullprogram.com/blog/2017/09/21/ + const M: u64 = 0xbb2efcec3c39611d; + const A: u64 = 0x7590ef39; -/// Methods required for a custom rand implementation. -/// -/// This trait is not intended to be used directly, just to supply a custom rand implementation to smoltcp. -#[cfg(feature = "rand-custom-impl")] -pub trait Rand { - /// Fill `buf` with random bytes. - fn rand_bytes(buf: &mut [u8]); -} - -/// Set the custom rand implementation. -/// -/// # Example -/// -/// ``` -/// struct Rand; -/// smoltcp::rand_custom_impl!(Rand); -/// impl smoltcp::Rand for Rand { -/// fn rand_bytes(buf: &mut [u8]) { -/// // TODO -/// } -/// } -/// -#[macro_export] -#[cfg(feature = "rand-custom-impl")] -macro_rules! rand_custom_impl { - ($t: ty) => { - #[no_mangle] - fn _smoltcp_rand(buf: &mut [u8]) { - <$t as $crate::Rand>::rand_bytes(buf) - } - }; -} + let s = self.state * M + A; + self.state = s; -#[cfg(all(feature = "std", not(feature = "rand-custom-impl"), not(test)))] -#[no_mangle] -fn _smoltcp_rand(buf: &mut [u8]) { - use rand_core::RngCore; - - rand_core::OsRng.fill_bytes(buf) -} - -#[cfg(test)] -#[no_mangle] -fn _smoltcp_rand(buf: &mut [u8]) { - panic!("Rand should not be used when testing"); + let shift = 29 - (s >> 61); + (s >> shift) as u32 + } } diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 10386b770..38736f8da 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -370,12 +370,12 @@ impl Dhcpv4Socket { } #[cfg(not(test))] - fn random_transaction_id() -> u32 { - crate::rand::rand_u32() + fn random_transaction_id(cx: &mut Context) -> u32 { + cx.rand().rand_u32() } #[cfg(test)] - fn random_transaction_id() -> u32 { + fn random_transaction_id(_cx: &mut Context) -> u32 { 0x12345678 } @@ -397,7 +397,7 @@ impl Dhcpv4Socket { // We don't directly modify self.transaction_id because sending the packet // may fail. We only want to update state after succesfully sending. - let next_transaction_id = Self::random_transaction_id(); + let next_transaction_id = Self::random_transaction_id(cx); let mut dhcp_repr = DhcpRepr { message_type: DhcpMessageType::Discover, diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index f6602b80e..e7f523897 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -708,7 +708,12 @@ impl<'a> TcpSocket<'a> { /// This function returns an error if the socket was open; see [is_open](#method.is_open). /// It also returns an error if the local or remote port is zero, or if the remote address /// is unspecified. - pub fn connect(&mut self, remote_endpoint: T, local_endpoint: U) -> Result<()> + pub fn connect( + &mut self, + cx: &mut Context, + remote_endpoint: T, + local_endpoint: U, + ) -> Result<()> where T: Into, U: Into, @@ -743,17 +748,20 @@ impl<'a> TcpSocket<'a> { self.remote_endpoint = remote_endpoint; self.set_state(State::SynSent); - let seq = Self::random_seq_no(); + let seq = Self::random_seq_no(cx); self.local_seq_no = seq; self.remote_last_seq = seq; Ok(()) } - fn random_seq_no() -> TcpSeqNumber { - #[cfg(test)] - return TcpSeqNumber(10000); - #[cfg(not(test))] - return TcpSeqNumber(crate::rand::rand_u32() as i32); + #[cfg(test)] + fn random_seq_no(_cx: &mut Context) -> TcpSeqNumber { + TcpSeqNumber(10000) + } + + #[cfg(not(test))] + fn random_seq_no(cx: &mut Context) -> TcpSeqNumber { + TcpSeqNumber(cx.rand().rand_u32() as i32) } /// Close the transmit half of the full-duplex connection. @@ -1570,7 +1578,7 @@ impl<'a> TcpSocket<'a> { self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); - self.local_seq_no = Self::random_seq_no(); + self.local_seq_no = Self::random_seq_no(cx); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; self.remote_has_sack = repr.sack_permitted; @@ -3215,23 +3223,27 @@ mod test { fn test_connect_validation() { let mut s = socket(); assert_eq!( - s.socket.connect((IpAddress::Unspecified, 80), LOCAL_END), + s.socket + .connect(&mut s.cx, (IpAddress::Unspecified, 80), LOCAL_END), Err(Error::Unaddressable) ); assert_eq!( - s.socket.connect(REMOTE_END, (MOCK_UNSPECIFIED, 0)), + s.socket + .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 0)), Err(Error::Unaddressable) ); assert_eq!( - s.socket.connect((MOCK_UNSPECIFIED, 0), LOCAL_END), + s.socket + .connect(&mut s.cx, (MOCK_UNSPECIFIED, 0), LOCAL_END), Err(Error::Unaddressable) ); assert_eq!( - s.socket.connect((IpAddress::Unspecified, 80), LOCAL_END), + s.socket + .connect(&mut s.cx, (IpAddress::Unspecified, 80), LOCAL_END), Err(Error::Unaddressable) ); s.socket - .connect(REMOTE_END, LOCAL_END) + .connect(&mut s.cx, REMOTE_END, LOCAL_END) .expect("Connect failed with valid parameters"); assert_eq!(s.local_endpoint(), LOCAL_END); assert_eq!(s.remote_endpoint(), REMOTE_END); @@ -3241,7 +3253,9 @@ mod test { fn test_connect() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.socket.connect(REMOTE_END, LOCAL_END.port).unwrap(); + s.socket + .connect(&mut s.cx, REMOTE_END, LOCAL_END.port) + .unwrap(); assert_eq!( s.local_endpoint, IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port) @@ -3275,10 +3289,15 @@ mod test { #[test] fn test_connect_unspecified_local() { let mut s = socket(); - assert_eq!(s.socket.connect(REMOTE_END, (MOCK_UNSPECIFIED, 80)), Ok(())); + assert_eq!( + s.socket + .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 80)), + Ok(()) + ); s.abort(); assert_eq!( - s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + s.socket + .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), Ok(()) ); s.abort(); @@ -3287,18 +3306,24 @@ mod test { #[test] fn test_connect_specified_local() { let mut s = socket(); - assert_eq!(s.socket.connect(REMOTE_END, (MOCK_IP_ADDR_2, 80)), Ok(())); + assert_eq!( + s.socket + .connect(&mut s.cx, REMOTE_END, (MOCK_IP_ADDR_2, 80)), + Ok(()) + ); } #[test] fn test_connect_twice() { let mut s = socket(); assert_eq!( - s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + s.socket + .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), Ok(()) ); assert_eq!( - s.socket.connect(REMOTE_END, (IpAddress::Unspecified, 80)), + s.socket + .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), Err(Error::Illegal) ); } @@ -3307,7 +3332,7 @@ mod test { fn test_syn_sent_sanity() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.socket.connect(REMOTE_END, LOCAL_END).unwrap(); + s.socket.connect(&mut s.cx, REMOTE_END, LOCAL_END).unwrap(); sanity!(s, socket_syn_sent_with_local_ipendpoint(LOCAL_END)); } @@ -3562,7 +3587,7 @@ mod test { let mut s = socket_with_buffer_sizes(64, *buffer_size); s.local_seq_no = LOCAL_SEQ; assert_eq!(s.remote_win_shift, *shift_amt); - s.socket.connect(REMOTE_END, LOCAL_END).unwrap(); + s.socket.connect(&mut s.cx, REMOTE_END, LOCAL_END).unwrap(); recv!( s, [TcpRepr { @@ -6061,7 +6086,9 @@ mod test { fn test_connect_timeout() { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; - s.socket.connect(REMOTE_END, LOCAL_END.port).unwrap(); + s.socket + .connect(&mut s.cx, REMOTE_END, LOCAL_END.port) + .unwrap(); s.set_timeout(Some(Duration::from_millis(100))); recv!(s, time 150, Ok(TcpRepr { control: TcpControl::Syn, From bf525bf2389310b1a570d62127fa6956b2e1d418 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 3 Dec 2021 10:28:21 +0100 Subject: [PATCH 277/566] ieee802154: random sequence number Set the sequence number to a random value when building the interface. --- src/iface/interface.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index c4da9f6db..6a397892f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -66,8 +66,6 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option>, #[cfg(feature = "medium-ieee802154")] - sequence_no: u8, - #[cfg(feature = "medium-ieee802154")] pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, sockets: SocketSet<'a>, @@ -126,8 +124,6 @@ let iface = InterfaceBuilder::new(device, vec![]) #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: None, - #[cfg(feature = "medium-ieee802154")] - sequence_no: 1, #[cfg(feature = "medium-ieee802154")] pan_id: None, @@ -166,15 +162,6 @@ let iface = InterfaceBuilder::new(device, vec![]) self } - /// Set the initial IEEE802.15.4 sequence number the interface will use. - /// - /// **NOTE**: this needs to be initailized randomly and not equal to 0. - #[cfg(feature = "medium-ieee802154")] - pub fn sequence_no(mut self, sequence_no: u8) -> Self { - self.sequence_no = sequence_no; - self - } - /// Set the IEEE802.15.4 PAN ID the interface will use. /// /// **NOTE**: we use the same PAN ID for destination and source. @@ -312,6 +299,21 @@ let iface = InterfaceBuilder::new(device, vec![]) let caps = self.device.capabilities(); + #[cfg(feature = "medium-ieee802154")] + let mut rand = Rand::new(self.random_seed); + #[cfg(not(feature = "medium-ieee802154"))] + let rand = Rand::new(self.random_seed); + + #[cfg(feature = "medium-ieee802154")] + let mut sequence_no; + #[cfg(feature = "medium-ieee802154")] + loop { + sequence_no = (rand.rand_u32() & 0xff) as u8; + if sequence_no != 0 { + break; + } + } + Interface { device: self.device, sockets: self.sockets, @@ -331,10 +333,10 @@ let iface = InterfaceBuilder::new(device, vec![]) #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, #[cfg(feature = "medium-ieee802154")] - sequence_no: self.sequence_no, + sequence_no, #[cfg(feature = "medium-ieee802154")] pan_id: self.pan_id, - rand: Rand::new(self.random_seed), + rand, }, } } From b538b429c37e8d3fb3f9dc67487275ae511f9008 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 6 Dec 2021 09:02:17 +0100 Subject: [PATCH 278/566] rand: fix overflow (issue #576) --- src/rand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rand.rs b/src/rand.rs index 48090586d..5c3bacd03 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -17,7 +17,7 @@ impl Rand { const M: u64 = 0xbb2efcec3c39611d; const A: u64 = 0x7590ef39; - let s = self.state * M + A; + let s = self.state.wrapping_mul(M).wrapping_add(A); self.state = s; let shift = 29 - (s >> 61); From 8256c3ce8c34f57d2eda9f2e891137c815cf5bc9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 00:36:37 +0100 Subject: [PATCH 279/566] Update docs. --- .github/workflows/test.yml | 4 ++-- README.md | 29 +++++++++++++++++++++-------- src/lib.rs | 4 ++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7edeffcc9..cd162b88a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.46, and nightly. + # Test on stable, MSRV, and nightly. # Failure is permitted on nightly. rust: - stable @@ -64,7 +64,7 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: - # Test on stable, MSRV 1.46, and nightly. + # Test on stable, MSRV, and nightly. # Failure is permitted on nightly. rust: - stable diff --git a/README.md b/README.md index 2f4b8ed87..55eb0cbe0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.53 and later. +and compiles on stable Rust 1.56 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. @@ -25,8 +25,9 @@ To set expectations right, both implemented and omitted features are listed. ### Media layer -The only supported medium is Ethernet. +There are 3 supported mediums. +* Ethernet * Regular Ethernet II frames are supported. * Unicast, broadcast and multicast packets are supported. * ARP packets (including gratuitous requests and replies) are supported. @@ -34,6 +35,11 @@ The only supported medium is Ethernet. * Cached ARP entries expire after one minute. * 802.3 frames and 802.1Q are **not** supported. * Jumbo frames are **not** supported. +* IP + * Unicast, broadcast and multicast packets are supported. +* IEEE 802.15.4 + 6LoWPAN (experimental) + * Unicast, broadcast and multicast packets are supported. + * ONLY UDP packets are supported. ### IP layer @@ -115,9 +121,9 @@ The TCP protocol is supported over IPv4 and IPv6, and server and client TCP sock * Time-wait timeout has a fixed interval of 10 s. * User timeout has a configurable interval. * Delayed acknowledgements are supported, with configurable delay. + * Nagle's algorithm is implemented. * Selective acknowledgements are **not** implemented. * Silly window syndrome avoidance is **not** implemented. - * Nagle's algorithm is **not** implemented. * Congestion control is **not** implemented. * Timestamping is **not** supported. * Urgent pointer is **ignored**. @@ -130,7 +136,7 @@ To use the _smoltcp_ library in your project, add the following to `Cargo.toml`: ```toml [dependencies] -smoltcp = "0.7.5" +smoltcp = "0.8.0" ``` The default configuration assumes a hosted environment, for ease of evaluation. @@ -138,7 +144,7 @@ You probably want to disable default features and configure them one by one: ```toml [dependencies] -smoltcp = { version = "0.7.5", default-features = false, features = ["log"] } +smoltcp = { version = "0.8.0", default-features = false, features = ["log"] } ``` ### Feature `std` @@ -166,6 +172,14 @@ the DEBUG log level. This feature is enabled by default. +### Feature `defmt` + +The `defmt` feature enables logging of events with the [defmt crate][defmt]. + +[defmt]: https://crates.io/crates/defmt + +This feature is disabled by default, and cannot be used at the same time as `log`. + ### Feature `verbose` The `verbose` feature enables logging of events where the logging itself may incur very high @@ -181,10 +195,9 @@ Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respective These features are enabled by default. -### Features `socket-raw`, `socket-udp`, and `socket-tcp` +### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4` -Enable `smoltcp::socket::RawSocket`, `smoltcp::socket::UdpSocket`, -and `smoltcp::socket::TcpSocket`, respectively. +Enable the corresponding socket type. These features are enabled by default. diff --git a/src/lib.rs b/src/lib.rs index f86842d8c..f07b0b1f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,11 +72,11 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.46 and up with any valid set of features. +//! This crate is guaranteed to compile on stable Rust 1.56 and up with any valid set of features. //! It *might* compile on older versions but that may change in any new patch release. //! //! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which -//! is higher than 1.46. +//! is higher. //! //! [wire]: wire/index.html //! [osi]: https://en.wikipedia.org/wiki/OSI_model From e88e324d44669ee26dfb81ef78040d2c7c1ec9f5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 01:19:49 +0100 Subject: [PATCH 280/566] Add v0.8 changelog. --- CHANGELOG.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94f3879a1..4f6bc2bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Version bumped to 0.8 -- Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.46 +- No unreleased changes. + +## [0.8.0] - 2021-12-11 + +- Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.56 +- Add support for IEEE 802.15.4 + 6LoWPAN medium (#469) +- Add support for IP medium (#401) +- Add `defmt` logging supprt (#455) +- Add RNG infrastructure (#547, #573) +- Add `Context` struct that must be passed to some socket methods (#500) +- Remove `SocketSet`, sockets are owned by `Interface` now. (#557, #571) +- TCP: Add Nagle's Algorithm. (#500) +- TCP crash and correctness fixes: + - Add Nagle's Algorithm. (#500) + - Window scaling fixes. (#500) + - Fix delayed ack causing ack not to be sent after 3 packets. (#530) + - Fix RTT estimation for RTTs longer than 1 second (#538) + - Fix infinite loop when remote side sets a MSS of 0 (#538) + - Fix infinite loop when retransmit when remote window is 0 (#538) + - Fix crash when receiving a FIN in SYN_SENT state (#538) + - Fix overflow crash when receiving a wrong ACK seq in SYN_RECEIVED state (#538) + - Fix overflow crash when initial sequence number is u32::MAX (#538) + - Fix infinite loop on challenge ACKs (#542) + - Reply with RST to invalid packets in SynReceived state. (#542) + - Do not abort socket when receiving some invalid packets. (#542) + - Make initial sequence number random. (#547) + - Reply with RST to ACKs with invalid ackno in SYN_SENT. (#522) +- ARP fixes to deal better with broken networks: + - Fill cache only from ARP packets, not any packets. (#544) + - Fill cache only from ARP packets directed at us. (#544) + - Reject ARP packets with a source address not in the local network. (#536, #544) + - Ignore unknown ARP packets. (#544) + - Flush neighbor cache on IP change (#564) +- UDP: Add `close()` method to unbind socket. (#475, #482) +- DHCP client improvements: + - Refactored implementation to improve reliability and RFC compliance (#459) + - Convert to socket (#459) + - Added `max_lease_duration` option (#459) + - Do not set the BROADCAST flag (#548) + - Add option to ignore NAKs (#548) +- DHCP wire: + - Fix DhcpRepr::buffer_len not accounting for lease time, router and subnet options (#478) + - Emit DNS servers in DhcpRepr (#510) + - Fix incorrect bit for BROADCAST flag (#548) +- Improve resilience against packet ingress processing errors (#281, #483) +- Implement `std::error::Error` for `smoltcp::Error` (#485) - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) -- udp: Add `close()` method to unbind socket. +- Fix incorrect timestamp in PCAP captures (#513) +- Use microseconds instead of milliseconds in Instant and Duration (#514) +- Expose inner `Device` in `PcapWriter` (#524) +- Fix assert with any_ip + broadcast dst_addr. (#533, #534) +- Simplify PcapSink trait (#535) +- Fix wrong operation order in FuzzInjector (#525, #535) ## [0.7.5] - 2021-06-28 From 12eb689864ae38b7c588644bbcea6faac2cf1977 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 01:27:24 +0100 Subject: [PATCH 281/566] Add links to changelog. --- CHANGELOG.md | 109 ++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6bc2bb9..678c26d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,64 +11,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.8.0] - 2021-12-11 - Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.56 -- Add support for IEEE 802.15.4 + 6LoWPAN medium (#469) -- Add support for IP medium (#401) -- Add `defmt` logging supprt (#455) -- Add RNG infrastructure (#547, #573) -- Add `Context` struct that must be passed to some socket methods (#500) -- Remove `SocketSet`, sockets are owned by `Interface` now. (#557, #571) -- TCP: Add Nagle's Algorithm. (#500) +- Add support for IEEE 802.15.4 + 6LoWPAN medium ([#469](https://github.com/smoltcp-rs/smoltcp/pull/469)) +- Add support for IP medium ([#401](https://github.com/smoltcp-rs/smoltcp/pull/401)) +- Add `defmt` logging supprt ([#455](https://github.com/smoltcp-rs/smoltcp/pull/455)) +- Add RNG infrastructure ([#547](https://github.com/smoltcp-rs/smoltcp/pull/547), [#573](https://github.com/smoltcp-rs/smoltcp/pull/573)) +- Add `Context` struct that must be passed to some socket methods ([#500](https://github.com/smoltcp-rs/smoltcp/pull/500)) +- Remove `SocketSet`, sockets are owned by `Interface` now. ([#557](https://github.com/smoltcp-rs/smoltcp/pull/557), [#571](https://github.com/smoltcp-rs/smoltcp/pull/571)) +- TCP: Add Nagle's Algorithm. ([#500](https://github.com/smoltcp-rs/smoltcp/pull/500)) - TCP crash and correctness fixes: - - Add Nagle's Algorithm. (#500) - - Window scaling fixes. (#500) - - Fix delayed ack causing ack not to be sent after 3 packets. (#530) - - Fix RTT estimation for RTTs longer than 1 second (#538) - - Fix infinite loop when remote side sets a MSS of 0 (#538) - - Fix infinite loop when retransmit when remote window is 0 (#538) - - Fix crash when receiving a FIN in SYN_SENT state (#538) - - Fix overflow crash when receiving a wrong ACK seq in SYN_RECEIVED state (#538) - - Fix overflow crash when initial sequence number is u32::MAX (#538) - - Fix infinite loop on challenge ACKs (#542) - - Reply with RST to invalid packets in SynReceived state. (#542) - - Do not abort socket when receiving some invalid packets. (#542) - - Make initial sequence number random. (#547) - - Reply with RST to ACKs with invalid ackno in SYN_SENT. (#522) + - Add Nagle's Algorithm. ([#500](https://github.com/smoltcp-rs/smoltcp/pull/500)) + - Window scaling fixes. ([#500](https://github.com/smoltcp-rs/smoltcp/pull/500)) + - Fix delayed ack causing ack not to be sent after 3 packets. ([#530](https://github.com/smoltcp-rs/smoltcp/pull/530)) + - Fix RTT estimation for RTTs longer than 1 second ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix infinite loop when remote side sets a MSS of 0 ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix infinite loop when retransmit when remote window is 0 ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix crash when receiving a FIN in SYN_SENT state ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix overflow crash when receiving a wrong ACK seq in SYN_RECEIVED state ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix overflow crash when initial sequence number is u32::MAX ([#538](https://github.com/smoltcp-rs/smoltcp/pull/538)) + - Fix infinite loop on challenge ACKs ([#542](https://github.com/smoltcp-rs/smoltcp/pull/542)) + - Reply with RST to invalid packets in SynReceived state. ([#542](https://github.com/smoltcp-rs/smoltcp/pull/542)) + - Do not abort socket when receiving some invalid packets. ([#542](https://github.com/smoltcp-rs/smoltcp/pull/542)) + - Make initial sequence number random. ([#547](https://github.com/smoltcp-rs/smoltcp/pull/547)) + - Reply with RST to ACKs with invalid ackno in SYN_SENT. ([#522](https://github.com/smoltcp-rs/smoltcp/pull/522)) - ARP fixes to deal better with broken networks: - - Fill cache only from ARP packets, not any packets. (#544) - - Fill cache only from ARP packets directed at us. (#544) - - Reject ARP packets with a source address not in the local network. (#536, #544) - - Ignore unknown ARP packets. (#544) - - Flush neighbor cache on IP change (#564) -- UDP: Add `close()` method to unbind socket. (#475, #482) + - Fill cache only from ARP packets, not any packets. ([#544](https://github.com/smoltcp-rs/smoltcp/pull/544)) + - Fill cache only from ARP packets directed at us. ([#544](https://github.com/smoltcp-rs/smoltcp/pull/544)) + - Reject ARP packets with a source address not in the local network. ([#536](https://github.com/smoltcp-rs/smoltcp/pull/536), [#544](https://github.com/smoltcp-rs/smoltcp/pull/544)) + - Ignore unknown ARP packets. ([#544](https://github.com/smoltcp-rs/smoltcp/pull/544)) + - Flush neighbor cache on IP change ([#564](https://github.com/smoltcp-rs/smoltcp/pull/564)) +- UDP: Add `close()` method to unbind socket. ([#475](https://github.com/smoltcp-rs/smoltcp/pull/475), [#482](https://github.com/smoltcp-rs/smoltcp/pull/482)) - DHCP client improvements: - - Refactored implementation to improve reliability and RFC compliance (#459) - - Convert to socket (#459) - - Added `max_lease_duration` option (#459) - - Do not set the BROADCAST flag (#548) - - Add option to ignore NAKs (#548) + - Refactored implementation to improve reliability and RFC compliance ([#459](https://github.com/smoltcp-rs/smoltcp/pull/459)) + - Convert to socket ([#459](https://github.com/smoltcp-rs/smoltcp/pull/459)) + - Added `max_lease_duration` option ([#459](https://github.com/smoltcp-rs/smoltcp/pull/459)) + - Do not set the BROADCAST flag ([#548](https://github.com/smoltcp-rs/smoltcp/pull/548)) + - Add option to ignore NAKs ([#548](https://github.com/smoltcp-rs/smoltcp/pull/548)) - DHCP wire: - - Fix DhcpRepr::buffer_len not accounting for lease time, router and subnet options (#478) - - Emit DNS servers in DhcpRepr (#510) - - Fix incorrect bit for BROADCAST flag (#548) -- Improve resilience against packet ingress processing errors (#281, #483) -- Implement `std::error::Error` for `smoltcp::Error` (#485) + - Fix DhcpRepr::buffer_len not accounting for lease time, router and subnet options ([#478](https://github.com/smoltcp-rs/smoltcp/pull/478)) + - Emit DNS servers in DhcpRepr ([#510](https://github.com/smoltcp-rs/smoltcp/pull/510)) + - Fix incorrect bit for BROADCAST flag ([#548](https://github.com/smoltcp-rs/smoltcp/pull/548)) +- Improve resilience against packet ingress processing errors ([#281](https://github.com/smoltcp-rs/smoltcp/pull/281), [#483](https://github.com/smoltcp-rs/smoltcp/pull/483)) +- Implement `std::error::Error` for `smoltcp::Error` ([#485](https://github.com/smoltcp-rs/smoltcp/pull/485)) - Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442)) -- Fix incorrect timestamp in PCAP captures (#513) -- Use microseconds instead of milliseconds in Instant and Duration (#514) -- Expose inner `Device` in `PcapWriter` (#524) -- Fix assert with any_ip + broadcast dst_addr. (#533, #534) -- Simplify PcapSink trait (#535) -- Fix wrong operation order in FuzzInjector (#525, #535) +- Fix incorrect timestamp in PCAP captures ([#513](https://github.com/smoltcp-rs/smoltcp/pull/513)) +- Use microseconds instead of milliseconds in Instant and Duration ([#514](https://github.com/smoltcp-rs/smoltcp/pull/514)) +- Expose inner `Device` in `PcapWriter` ([#524](https://github.com/smoltcp-rs/smoltcp/pull/524)) +- Fix assert with any_ip + broadcast dst_addr. ([#533](https://github.com/smoltcp-rs/smoltcp/pull/533), [#534](https://github.com/smoltcp-rs/smoltcp/pull/534)) +- Simplify PcapSink trait ([#535](https://github.com/smoltcp-rs/smoltcp/pull/535)) +- Fix wrong operation order in FuzzInjector ([#525](https://github.com/smoltcp-rs/smoltcp/pull/525), [#535](https://github.com/smoltcp-rs/smoltcp/pull/535)) ## [0.7.5] - 2021-06-28 -- dhcpv4: emit DNS servers in repr (#505) +- dhcpv4: emit DNS servers in repr ([#505](https://github.com/smoltcp-rs/smoltcp/pull/505)) ## [0.7.4] - 2021-06-11 -- tcp: fix "subtract sequence numbers with underflow" on remote window shrink. (#490) -- tcp: fix substract with overflow when receiving a SYNACK with unincremented ACK number. (#491) -- tcp: use nonzero initial sequence number to workaround misbehaving servers. (#492) +- tcp: fix "subtract sequence numbers with underflow" on remote window shrink. ([#490](https://github.com/smoltcp-rs/smoltcp/pull/490)) +- tcp: fix substract with overflow when receiving a SYNACK with unincremented ACK number. ([#491](https://github.com/smoltcp-rs/smoltcp/pull/491)) +- tcp: use nonzero initial sequence number to workaround misbehaving servers. ([#492](https://github.com/smoltcp-rs/smoltcp/pull/492)) ## [0.7.3] - 2021-05-29 @@ -76,13 +76,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.7.2] - 2021-05-29 -- iface: check for ipv4 subnet broadcast addrs everywhere (#462) -- dhcp: always send parameter_request_list. (#456) -- dhcp: Clear expiration time on reset. (#456) -- phy: fix FaultInjector returning a too big buffer when simulating a drop on tx (#463) -- tcp rtte: fix "attempt to multiply with overflow". (#476) -- tcp: LastAck should only change to Closed on ack of fin. (#477) -- wire/dhcpv4: account for lease time, router and subnet options in DhcpRepr::buffer_len (#478) +- iface: check for ipv4 subnet broadcast addrs everywhere ([#462](https://github.com/smoltcp-rs/smoltcp/pull/462)) +- dhcp: always send parameter_request_list. ([#456](https://github.com/smoltcp-rs/smoltcp/pull/456)) +- dhcp: Clear expiration time on reset. ([#456](https://github.com/smoltcp-rs/smoltcp/pull/456)) +- phy: fix FaultInjector returning a too big buffer when simulating a drop on tx ([#463](https://github.com/smoltcp-rs/smoltcp/pull/463)) +- tcp rtte: fix "attempt to multiply with overflow". ([#476](https://github.com/smoltcp-rs/smoltcp/pull/476)) +- tcp: LastAck should only change to Closed on ack of fin. ([#477](https://github.com/smoltcp-rs/smoltcp/pull/477)) +- wire/dhcpv4: account for lease time, router and subnet options in DhcpRepr::buffer_len ([#478](https://github.com/smoltcp-rs/smoltcp/pull/478)) ## [0.7.1] - 2021-03-27 @@ -133,6 +133,7 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.8.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.8.0 [0.7.5]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.4...v0.7.5 [0.7.4]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.3...v0.7.4 [0.7.3]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.2...v0.7.3 From 4fb1ceb7919664e5e3e917205ebcda0c0dffa9bf Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 29 Dec 2021 10:12:45 +0100 Subject: [PATCH 282/566] fixes #583 --- src/iface/interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 6a397892f..3694ddd31 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -549,7 +549,7 @@ where #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] assert!( self.device().capabilities().medium == Medium::Ethernet - || self.device().capabilities().medium == Medium::Ethernet + || self.device().capabilities().medium == Medium::Ieee802154 ); self.inner.hardware_addr.unwrap() @@ -570,7 +570,7 @@ where #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] assert!( self.device().capabilities().medium == Medium::Ethernet - || self.device().capabilities().medium == Medium::Ethernet + || self.device().capabilities().medium == Medium::Ieee802154 ); InterfaceInner::check_hardware_addr(&addr); From 0536a0da594cb154fbf3f7712baf21e5901a558e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 23 Jan 2022 16:34:34 +0100 Subject: [PATCH 283/566] Delete CODE_STYLE.md --- CODE_STYLE.md | 99 --------------------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 CODE_STYLE.md diff --git a/CODE_STYLE.md b/CODE_STYLE.md deleted file mode 100644 index 4cf2ca2a3..000000000 --- a/CODE_STYLE.md +++ /dev/null @@ -1,99 +0,0 @@ -# Code style - -smoltcp does not follow the rustfmt code style because whitequark (the original -author of smoltcp) finds automated formatters annoying and impairing readability -just as much as improving it in different cases. - -In general, format the things like the existing code and it'll be alright. -Here are a few things to watch out for, though: - -## Ordering use statements - -Use statements should be separated into two sections, uses from other crates and uses -from the current crate. The latter would ideally be sorted from most general -to most specific, but it's not very important. - -```rust -use core::cell::RefCell; - -use crate::{Error, Result}; -use crate::phy::{self, DeviceCapabilities, Device}; -``` - -## Wrapping function calls - -Avoid rightwards drift. This is fine: - -```rust -assert_eq!(iface.inner.process_ethernet(&mut socket_set, 0, frame.into_inner()), - Ok(Packet::None)); -``` - -This is also fine: - -```rust -assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, 0, - &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); -``` - -This is not: - -```rust -assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, 0, - &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); -``` - -## Wrapping function prototypes - -A function declaration might be wrapped... - - * right after `,`, - * right after `>`, - * right after `)`, - * right after `->`, - * right before and after `where`. - -Here's an artificial example, wrapped at 50 columns: - -```rust -fn dispatch_ethernet - (&mut self, tx_token: Tx, - timestamp: u64, f: F) -> - Result<()> - where Tx: TxToken, - F: FnOnce(EthernetFrame<&mut [u8]>) -{ - // ... -} -``` - -## Visually aligning tokens - -This is fine: - -```rust -struct State { - rng_seed: u32, - refilled_at: u64, - tx_bucket: u64, - rx_bucket: u64, -} -``` - -This is also fine: - -```rust -struct State { - rng_seed: u32, - refilled_at: u64, - tx_bucket: u64, - rx_bucket: u64, -} -``` - -It's OK to change between those if you touch that code anyway, -but avoid reformatting just for the sake of it. From 842c875f92cdce1fa313f5cd0a871c9a3b2a79f2 Mon Sep 17 00:00:00 2001 From: Alexandra Sandulescu Date: Thu, 27 Jan 2022 18:10:19 +0100 Subject: [PATCH 284/566] Update dependencies --- Cargo.toml | 14 +++++++------- examples/utils.rs | 5 ++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 17884dc4e..448f72d86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,18 +17,18 @@ autoexamples = false [dependencies] managed = { version = "0.8", default-features = false, features = ["map"] } -byteorder = { version = "1.0", default-features = false } -log = { version = "0.4.4", default-features = false, optional = true } -libc = { version = "0.2.18", optional = true } -bitflags = { version = "1.0", default-features = false } +byteorder = { version = "1.4", default-features = false } +log = { version = "0.4.14", default-features = false, optional = true } +libc = { version = "0.2.115", optional = true } +bitflags = { version = "1.3", default-features = false } defmt = { version = "0.3", optional = true } rand_core = { version = "0.6.3", optional = true, default-features = false } [dev-dependencies] -env_logger = "0.5" +env_logger = "0.9" getopts = "0.2" -rand = "0.3" -url = "1.0" +rand = "0.8" +url = "2.0" [features] std = ["managed/std", "rand_core/std"] diff --git a/examples/utils.rs b/examples/utils.rs index f82dd9a08..c9cc70c7a 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -23,7 +23,7 @@ pub fn setup_logging_with_clock(filter: &str, since_startup: F) where F: Fn() -> Instant + Send + Sync + 'static, { - Builder::new() + Builder::from_default_env() .format(move |buf, record| { let elapsed = since_startup(); let timestamp = format!("[{}]", elapsed); @@ -54,8 +54,7 @@ where } }) .filter(None, LevelFilter::Trace) - .parse(filter) - .parse(&env::var("RUST_LOG").unwrap_or_else(|_| "".to_owned())) + .parse_filters(filter) .init(); } From 9b5abf0e8802b963e64e317db87f903e1cf66dfe Mon Sep 17 00:00:00 2001 From: Alexandra Sandulescu Date: Fri, 28 Jan 2022 18:45:55 +0100 Subject: [PATCH 285/566] Update dependencies: ignore those updated through caret spec * allow logging environment overwrite by RUST_LOG --- Cargo.toml | 8 ++++---- examples/utils.rs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 448f72d86..f10303837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,10 @@ autoexamples = false [dependencies] managed = { version = "0.8", default-features = false, features = ["map"] } -byteorder = { version = "1.4", default-features = false } -log = { version = "0.4.14", default-features = false, optional = true } -libc = { version = "0.2.115", optional = true } -bitflags = { version = "1.3", default-features = false } +byteorder = { version = "1.0", default-features = false } +log = { version = "0.4.4", default-features = false, optional = true } +libc = { version = "0.2.18", optional = true } +bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } rand_core = { version = "0.6.3", optional = true, default-features = false } diff --git a/examples/utils.rs b/examples/utils.rs index c9cc70c7a..dcee7b4a6 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -23,7 +23,7 @@ pub fn setup_logging_with_clock(filter: &str, since_startup: F) where F: Fn() -> Instant + Send + Sync + 'static, { - Builder::from_default_env() + Builder::new() .format(move |buf, record| { let elapsed = since_startup(); let timestamp = format!("[{}]", elapsed); @@ -55,6 +55,7 @@ where }) .filter(None, LevelFilter::Trace) .parse_filters(filter) + .parse_env("RUST_LOG") .init(); } From 32bf949dc8162f340c60136477acd6b46298b5f9 Mon Sep 17 00:00:00 2001 From: Jade Date: Tue, 8 Feb 2022 18:37:16 -0500 Subject: [PATCH 286/566] Propagate phy::RawSocket send error to caller --- src/phy/raw_socket.rs | 7 ++++++- src/phy/sys/raw_socket.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index e01438f46..685fdb42a 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -111,7 +111,12 @@ impl phy::TxToken for TxToken { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); - lower.send(&buffer[..]).unwrap(); + if let Err(err) = lower.send(&buffer[..]) { + return match err.kind() { + io::ErrorKind::WouldBlock => Err(crate::Error::Exhausted), + _ => Err(crate::Error::Illegal), + }; + } result } } diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 2161b3d38..f9654d509 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -104,7 +104,7 @@ impl RawSocketDesc { 0, ); if len == -1 { - Err(io::Error::last_os_error()).unwrap() + return Err(io::Error::last_os_error()); } Ok(len as usize) } From 32974f8136c9d16d34863ccd0ca5e285d05f22ec Mon Sep 17 00:00:00 2001 From: Jade Date: Tue, 8 Feb 2022 19:57:41 -0500 Subject: [PATCH 287/566] Changed behavior of phy::RawSocket TxToken::consume to be more similar to RawSocket::receive --- src/phy/raw_socket.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 685fdb42a..49e7470a1 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -111,12 +111,10 @@ impl phy::TxToken for TxToken { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); - if let Err(err) = lower.send(&buffer[..]) { - return match err.kind() { - io::ErrorKind::WouldBlock => Err(crate::Error::Exhausted), - _ => Err(crate::Error::Illegal), - }; + match lower.send(&buffer[..]) { + Ok(_) => result, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => Err(crate::Error::Exhausted), + Err(err) => panic!("{}", err), } - result } } From 4a489b38db513246c3d2ea722b38fa1ae767a6ad Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Feb 2022 21:53:57 +0100 Subject: [PATCH 288/566] Remove unused rand_core dep. --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f10303837..49ec929d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } -rand_core = { version = "0.6.3", optional = true, default-features = false } [dev-dependencies] env_logger = "0.9" @@ -31,7 +30,7 @@ rand = "0.8" url = "2.0" [features] -std = ["managed/std", "rand_core/std"] +std = ["managed/std"] alloc = ["managed/alloc"] verbose = [] "medium-ethernet" = ["socket"] From 0401bd2a6334a1b101642d5d227a85b2f904bd6e Mon Sep 17 00:00:00 2001 From: trinity-1686a Date: Sat, 19 Mar 2022 14:14:38 +0100 Subject: [PATCH 289/566] use socklen_t instead of u32 for bind() parameter this fix compilation on Android 32b, where socklen_t is i32 --- src/phy/sys/raw_socket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index f9654d509..951f358dd 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -70,7 +70,7 @@ impl RawSocketDesc { let res = libc::bind( self.lower, &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr, - mem::size_of::() as u32, + mem::size_of::() as libc::socklen_t, ); if res == -1 { return Err(io::Error::last_os_error()); From 52628e2d4e03db7321b3b092bb0e93863b3ee39c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 02:31:08 +0100 Subject: [PATCH 290/566] tcp: immediately choose source address in connect(). --- src/iface/interface.rs | 22 +++++++++++++++++++++- src/socket/tcp.rs | 27 +++++++++------------------ src/wire/ip.rs | 11 +++++++++++ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 3694ddd31..7dbae0c4b 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1055,6 +1055,18 @@ impl<'a> InterfaceInner<'a> { &mut self.rand } + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { + let v = dst_addr.version().unwrap(); + for cidr in self.ip_addrs.iter() { + let addr = cidr.address(); + if addr.version() == Some(v) { + return Some(addr); + } + } + None + } + #[cfg(test)] pub(crate) fn mock() -> Self { Self { @@ -1080,7 +1092,15 @@ impl<'a> InterfaceInner<'a> { }, now: Instant::from_millis_const(0), - ip_addrs: ManagedSlice::Owned(vec![]), + ip_addrs: ManagedSlice::Owned(vec![ + #[cfg(feature = "proto-ipv4")] + IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(Ipv6Cidr::new( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), + 64, + )), + ]), rand: Rand::new(1234), routes: Routes::new(&mut [][..]), diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index e7f523897..35fc26135 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -718,8 +718,8 @@ impl<'a> TcpSocket<'a> { T: Into, U: Into, { - let remote_endpoint = remote_endpoint.into(); - let local_endpoint = local_endpoint.into(); + let remote_endpoint: IpEndpoint = remote_endpoint.into(); + let mut local_endpoint: IpEndpoint = local_endpoint.into(); if self.is_open() { return Err(Error::Illegal); @@ -731,17 +731,12 @@ impl<'a> TcpSocket<'a> { return Err(Error::Unaddressable); } - // If local address is not provided, use an unspecified address but a specified protocol. - // This lets us lower IpRepr later to determine IP header size and calculate MSS, - // but without committing to a specific address right away. - let local_addr = match local_endpoint.addr { - IpAddress::Unspecified => remote_endpoint.addr.as_unspecified(), - ip => ip, - }; - let local_endpoint = IpEndpoint { - addr: local_addr, - ..local_endpoint - }; + // If local address is not provided, choose it automatically. + if local_endpoint.addr.is_unspecified() { + local_endpoint.addr = cx + .get_source_address(remote_endpoint.addr) + .ok_or(Error::Unaddressable)?; + } self.reset(); self.local_endpoint = local_endpoint; @@ -1626,7 +1621,6 @@ impl<'a> TcpSocket<'a> { self.remote_mss = max_seg_size as usize; } - self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no + 1; self.remote_last_ack = Some(repr.seq_number); @@ -3256,10 +3250,7 @@ mod test { s.socket .connect(&mut s.cx, REMOTE_END, LOCAL_END.port) .unwrap(); - assert_eq!( - s.local_endpoint, - IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_END.port) - ); + assert_eq!(s.local_endpoint, LOCAL_END); recv!( s, [TcpRepr { diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 16c9de616..062ac535a 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -111,6 +111,17 @@ impl Address { Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7)) } + /// Return the protocol version. + pub fn version(&self) -> Option { + match self { + Address::Unspecified => None, + #[cfg(feature = "proto-ipv4")] + Address::Ipv4(_) => Some(Version::Ipv4), + #[cfg(feature = "proto-ipv6")] + Address::Ipv6(_) => Some(Version::Ipv6), + } + } + /// Return an address as a sequence of octets, in big-endian. pub fn as_bytes(&self) -> &[u8] { match *self { From 028b4466ff2fb5571ccc7d4bf72f4d85c0acf8b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 02:27:53 +0100 Subject: [PATCH 291/566] wire: Rename ipv4 `protocol` to `next_header`. This improves v4/v6 consistency at the cost of slightly deviating from RFC names. --- benches/bench.rs | 2 +- src/iface/interface.rs | 50 +++++++++++++++---------------- src/socket/dhcpv4.rs | 12 ++++---- src/socket/icmp.rs | 14 ++++----- src/socket/raw.rs | 12 ++++---- src/socket/tcp.rs | 20 ++++++------- src/socket/udp.rs | 10 +++---- src/wire/icmpv4.rs | 2 +- src/wire/ip.rs | 68 ++++++++++++++++++++---------------------- src/wire/ipv4.rs | 24 +++++++-------- src/wire/mod.rs | 2 +- 11 files changed, 107 insertions(+), 109 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index ed91ad59e..2738840c1 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -85,7 +85,7 @@ mod wire { let repr = Ipv4Repr { src_addr: Ipv4Address([192, 168, 1, 1]), dst_addr: Ipv4Address([192, 168, 1, 2]), - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: 100, hop_limit: 64, }; diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 7dbae0c4b..6f79d051d 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1592,7 +1592,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dhcpv4")] { - if ipv4_repr.protocol == IpProtocol::Udp && self.hardware_addr.is_some() { + if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { // First check for source and dest ports, then do `UdpRepr::parse` if they match. // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) let udp_packet = UdpPacket::new_checked(ip_payload)?; @@ -1637,7 +1637,7 @@ impl<'a> InterfaceInner<'a> { } } - match ipv4_repr.protocol { + match ipv4_repr.next_header { IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload), #[cfg(feature = "proto-igmp")] @@ -2027,7 +2027,7 @@ impl<'a> InterfaceInner<'a> { let ipv4_reply_repr = Ipv4Repr { src_addr: ipv4_repr.dst_addr, dst_addr: ipv4_repr.src_addr, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }; @@ -2040,7 +2040,7 @@ impl<'a> InterfaceInner<'a> { let ipv4_reply_repr = Ipv4Repr { src_addr: src_addr, dst_addr: ipv4_repr.src_addr, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }; @@ -2619,7 +2619,7 @@ impl<'a> InterfaceInner<'a> { src_addr: iface_addr, // Send to the group being reported dst_addr: group_addr, - protocol: IpProtocol::Igmp, + next_header: IpProtocol::Igmp, payload_len: igmp_repr.buffer_len(), hop_limit: 1, // TODO: add Router Alert IPv4 header option. See @@ -2638,7 +2638,7 @@ impl<'a> InterfaceInner<'a> { Ipv4Repr { src_addr: iface_addr, dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, - protocol: IpProtocol::Igmp, + next_header: IpProtocol::Igmp, payload_len: igmp_repr.buffer_len(), hop_limit: 1, }, @@ -2766,7 +2766,7 @@ mod test { let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Unknown(0x0c), + next_header: IpProtocol::Unknown(0x0c), payload_len: 0, hop_limit: 0x40, }); @@ -2825,7 +2825,7 @@ mod test { let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Unknown(0x0c), + next_header: IpProtocol::Unknown(0x0c), payload_len: 0, hop_limit: 0x40, }); @@ -2841,7 +2841,7 @@ mod test { header: Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Unknown(12), + next_header: IpProtocol::Unknown(12), payload_len: 0, hop_limit: 64, }, @@ -2852,7 +2852,7 @@ mod test { Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }, @@ -2942,7 +2942,7 @@ mod test { let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }); @@ -2966,7 +2966,7 @@ mod test { header: Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }, @@ -2976,7 +2976,7 @@ mod test { Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }, @@ -2995,7 +2995,7 @@ mod test { let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }); @@ -3066,7 +3066,7 @@ mod test { let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: src_ip, dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 0x40, }); @@ -3126,7 +3126,7 @@ mod test { let ipv4_repr = Ipv4Repr { src_addr: src_ipv4_addr, dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: icmpv4_repr.buffer_len(), }; @@ -3154,7 +3154,7 @@ mod test { let expected_ipv4_repr = Ipv4Repr { src_addr: our_ipv4_addr, dst_addr: src_ipv4_addr, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: expected_icmpv4_repr.buffer_len(), }; @@ -3212,7 +3212,7 @@ mod test { let ip_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, }; @@ -3251,7 +3251,7 @@ mod test { let expected_ip_repr = Ipv4Repr { src_addr: dst_addr, dst_addr: src_addr, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: expected_icmp_repr.buffer_len(), }; @@ -3561,7 +3561,7 @@ mod test { let ipv4_repr = Ipv4Repr { src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: 24, hop_limit: 64, }; @@ -3734,7 +3734,7 @@ mod test { let reports = recv_igmp(&mut iface, timestamp); assert_eq!(reports.len(), 2); for (i, group_addr) in groups.iter().enumerate() { - assert_eq!(reports[i].0.protocol, IpProtocol::Igmp); + assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); assert_eq!(reports[i].0.dst_addr, *group_addr); assert_eq!( reports[i].1, @@ -3778,7 +3778,7 @@ mod test { let leaves = recv_igmp(&mut iface, timestamp); assert_eq!(leaves.len(), 2); for (i, group_addr) in groups.iter().cloned().enumerate() { - assert_eq!(leaves[i].0.protocol, IpProtocol::Igmp); + assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); } @@ -3824,7 +3824,7 @@ mod test { let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + PAYLOAD_LEN, }; @@ -3894,7 +3894,7 @@ mod test { let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + PAYLOAD_LEN, }; @@ -3985,7 +3985,7 @@ mod test { let ipv4_repr = Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), }; diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 38736f8da..bf502dc6e 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -427,7 +427,7 @@ impl Dhcpv4Socket { let mut ipv4_repr = Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 0, // filled right before emit hop_limit: 64, }; @@ -606,7 +606,7 @@ mod test { let _ = s .socket .dispatch(&mut s.cx, |_, (mut ip_repr, udp_repr, dhcp_repr)| { - assert_eq!(ip_repr.protocol, IpProtocol::Udp); + assert_eq!(ip_repr.next_header, IpProtocol::Udp); assert_eq!( ip_repr.payload_len, udp_repr.header_len() + dhcp_repr.buffer_len() @@ -671,7 +671,7 @@ mod test { const IP_BROADCAST: Ipv4Repr = Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 0, hop_limit: 64, }; @@ -679,7 +679,7 @@ mod test { const IP_SERVER_BROADCAST: Ipv4Repr = Ipv4Repr { src_addr: SERVER_IP, dst_addr: Ipv4Address::BROADCAST, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 0, hop_limit: 64, }; @@ -687,7 +687,7 @@ mod test { const IP_RECV: Ipv4Repr = Ipv4Repr { src_addr: SERVER_IP, dst_addr: MY_IP, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 0, hop_limit: 64, }; @@ -695,7 +695,7 @@ mod test { const IP_SEND: Ipv4Repr = Ipv4Repr { src_addr: MY_IP, dst_addr: SERVER_IP, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 0, hop_limit: 64, }; diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index d71e7d222..79c0d55b0 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -453,7 +453,7 @@ impl<'a> IcmpSocket<'a> { let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::default(), dst_addr: ipv4_addr, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: repr.buffer_len(), hop_limit: hop_limit, }); @@ -549,7 +549,7 @@ mod test_ipv4 { static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: REMOTE_IPV4, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: 24, hop_limit: 0x40, }); @@ -557,7 +557,7 @@ mod test_ipv4 { static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { src_addr: REMOTE_IPV4, dst_addr: LOCAL_IPV4, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: 24, hop_limit: 0x40, }); @@ -650,7 +650,7 @@ mod test_ipv4 { IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address::UNSPECIFIED, dst_addr: REMOTE_IPV4, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: ECHOV4_REPR.buffer_len(), hop_limit: 0x2a, }) @@ -741,7 +741,7 @@ mod test_ipv4 { header: Ipv4Repr { src_addr: LOCAL_IPV4, dst_addr: REMOTE_IPV4, - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: 12, hop_limit: 0x40, }, @@ -750,7 +750,7 @@ mod test_ipv4 { let ip_repr = IpRepr::Unspecified { src_addr: REMOTE_IPV4.into(), dst_addr: LOCAL_IPV4.into(), - protocol: IpProtocol::Icmp, + next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 0x40, }; @@ -1018,7 +1018,7 @@ mod test_ipv6 { let ip_repr = IpRepr::Unspecified { src_addr: REMOTE_IPV6.into(), dst_addr: LOCAL_IPV6.into(), - protocol: IpProtocol::Icmpv6, + next_header: IpProtocol::Icmpv6, payload_len: icmp_repr.buffer_len(), hop_limit: 0x40, }; diff --git a/src/socket/raw.rs b/src/socket/raw.rs index b21e2d518..7943a8083 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -205,7 +205,7 @@ impl<'a> RawSocket<'a> { if ip_repr.version() != self.ip_version { return false; } - if ip_repr.protocol() != self.ip_protocol { + if ip_repr.next_header() != self.ip_protocol { return false; } @@ -244,7 +244,7 @@ impl<'a> RawSocket<'a> { F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<()>, { fn prepare<'a>( - protocol: IpProtocol, + next_header: IpProtocol, buffer: &'a mut [u8], _checksum_caps: &ChecksumCapabilities, ) -> Result<(IpRepr, &'a [u8])> { @@ -252,7 +252,7 @@ impl<'a> RawSocket<'a> { #[cfg(feature = "proto-ipv4")] IpVersion::Ipv4 => { let mut packet = Ipv4Packet::new_checked(buffer)?; - if packet.protocol() != protocol { + if packet.next_header() != next_header { return Err(Error::Unaddressable); } if _checksum_caps.ipv4.tx() { @@ -270,7 +270,7 @@ impl<'a> RawSocket<'a> { #[cfg(feature = "proto-ipv6")] IpVersion::Ipv6 => { let packet = Ipv6Packet::new_checked(buffer)?; - if packet.next_header() != protocol { + if packet.next_header() != next_header { return Err(Error::Unaddressable); } let packet = Ipv6Packet::new_unchecked(&*packet.into_inner()); @@ -358,7 +358,7 @@ mod test { pub const HEADER_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([10, 0, 0, 1]), dst_addr: Ipv4Address([10, 0, 0, 2]), - protocol: IpProtocol::Unknown(IP_PROTO), + next_header: IpProtocol::Unknown(IP_PROTO), payload_len: 4, hop_limit: 64, }); @@ -521,7 +521,7 @@ mod test { assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); let mut wrong_protocol = ipv4_locals::PACKET_BYTES; - Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp); + Ipv4Packet::new_unchecked(&mut wrong_protocol).set_next_header(IpProtocol::Tcp); assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 35fc26135..5d56bd1dc 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1140,7 +1140,7 @@ impl<'a> TcpSocket<'a> { let ip_reply_repr = IpRepr::Unspecified { src_addr: ip_repr.dst_addr(), dst_addr: ip_repr.src_addr(), - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: reply_repr.buffer_len(), hop_limit: 64, }; @@ -2139,7 +2139,7 @@ impl<'a> TcpSocket<'a> { let mut ip_repr = IpRepr::Unspecified { src_addr: self.local_endpoint.addr, dst_addr: self.remote_endpoint.addr, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, hop_limit: self.hop_limit.unwrap_or(64), payload_len: 0, } @@ -2443,7 +2443,7 @@ mod test { const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: 20, hop_limit: 64, }; @@ -2463,7 +2463,7 @@ mod test { const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: 20, hop_limit: 64, }; @@ -2518,7 +2518,7 @@ mod test { let ip_repr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_2, dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: repr.buffer_len(), hop_limit: 64, }; @@ -2547,7 +2547,7 @@ mod test { .dispatch(&mut socket.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); + assert_eq!(ip_repr.next_header(), IpProtocol::Tcp); assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1); assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2); assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len()); @@ -2641,7 +2641,7 @@ mod test { fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynSent; - s.local_endpoint = IpEndpoint::new(MOCK_UNSPECIFIED, LOCAL_PORT); + s.local_endpoint = LOCAL_END; s.remote_endpoint = REMOTE_END; s.local_seq_no = LOCAL_SEQ; s.remote_last_seq = LOCAL_SEQ; @@ -6948,7 +6948,7 @@ mod test { let ip_repr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_2, dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; @@ -6957,7 +6957,7 @@ mod test { let ip_repr_wrong_src = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_3, dst_addr: MOCK_IP_ADDR_1, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; @@ -6966,7 +6966,7 @@ mod test { let ip_repr_wrong_dst = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_2, dst_addr: MOCK_IP_ADDR_3, - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, }; diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 88cee7fc0..df0bc128e 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -362,7 +362,7 @@ impl<'a> UdpSocket<'a> { let ip_repr = IpRepr::Unspecified { src_addr: endpoint.addr, dst_addr: remote_endpoint.addr, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: repr.header_len() + payload_buf.len(), hop_limit: hop_limit, }; @@ -423,7 +423,7 @@ mod test { pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 64, }; @@ -446,7 +446,7 @@ mod test { (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr { src_addr: src, dst_addr: dst, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 64, }), @@ -655,7 +655,7 @@ mod test { IpRepr::Unspecified { src_addr: MOCK_IP_ADDR_1, dst_addr: MOCK_IP_ADDR_2, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 0x2a, } @@ -687,7 +687,7 @@ mod test { (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr { src_addr: src, dst_addr: dst, - protocol: IpProtocol::Udp, + next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 64, }), diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index ca3e38447..7e0f5382b 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -417,7 +417,7 @@ impl<'a> Repr<'a> { header: Ipv4Repr { src_addr: ip_packet.src_addr(), dst_addr: ip_packet.dst_addr(), - protocol: ip_packet.protocol(), + next_header: ip_packet.next_header(), payload_len: payload.len(), hop_limit: ip_packet.hop_limit(), }, diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 062ac535a..d2b5127de 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -516,7 +516,7 @@ pub enum Repr { Unspecified { src_addr: Address, dst_addr: Address, - protocol: Protocol, + next_header: Protocol, payload_len: usize, hop_limit: u8, }, @@ -574,12 +574,12 @@ impl Repr { } } - /// Return the protocol. - pub fn protocol(&self) -> Protocol { + /// Return the next header (protocol). + pub fn next_header(&self) -> Protocol { match *self { - Repr::Unspecified { protocol, .. } => protocol, + Repr::Unspecified { next_header, .. } => next_header, #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(repr) => repr.protocol, + Repr::Ipv4(repr) => repr.next_header, #[cfg(feature = "proto-ipv6")] Repr::Ipv6(repr) => repr.next_header, } @@ -658,14 +658,14 @@ impl Repr { &Repr::Unspecified { src_addr: src_addr @ Address::Unspecified, dst_addr: Address::Ipv4(dst_addr), - protocol, + next_header, payload_len, hop_limit, } | &Repr::Unspecified { src_addr: src_addr @ Address::Ipv4(_), dst_addr: Address::Ipv4(dst_addr), - protocol, + next_header, payload_len, hop_limit, } if src_addr.is_unspecified() => { @@ -683,7 +683,7 @@ impl Repr { Ok(Repr::Ipv4(Ipv4Repr { src_addr: src_addr.ok_or(Error::Unaddressable)?, dst_addr, - protocol, + next_header, payload_len, hop_limit, })) @@ -693,14 +693,14 @@ impl Repr { &Repr::Unspecified { src_addr: src_addr @ Address::Unspecified, dst_addr: Address::Ipv6(dst_addr), - protocol, + next_header: protocol, payload_len, hop_limit, } | &Repr::Unspecified { src_addr: src_addr @ Address::Ipv6(_), dst_addr: Address::Ipv6(dst_addr), - protocol, + next_header: protocol, payload_len, hop_limit, } if src_addr.is_unspecified() => { @@ -728,13 +728,13 @@ impl Repr { &Repr::Unspecified { src_addr: Address::Ipv4(src_addr), dst_addr: Address::Ipv4(dst_addr), - protocol, + next_header: protocol, payload_len, hop_limit, } => Ok(Repr::Ipv4(Ipv4Repr { src_addr: src_addr, dst_addr: dst_addr, - protocol: protocol, + next_header: protocol, payload_len: payload_len, hop_limit, })), @@ -743,7 +743,7 @@ impl Repr { &Repr::Unspecified { src_addr: Address::Ipv6(src_addr), dst_addr: Address::Ipv6(dst_addr), - protocol, + next_header: protocol, payload_len, hop_limit, } => Ok(Repr::Ipv6(Ipv6Repr { @@ -869,14 +869,14 @@ pub mod checksum { pub fn pseudo_header( src_addr: &Address, dst_addr: &Address, - protocol: Protocol, + next_header: Protocol, length: u32, ) -> u16 { match (src_addr, dst_addr) { #[cfg(feature = "proto-ipv4")] (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => { let mut proto_len = [0u8; 4]; - proto_len[1] = protocol.into(); + proto_len[1] = next_header.into(); NetworkEndian::write_u16(&mut proto_len[2..4], length as u16); combine(&[ @@ -889,7 +889,7 @@ pub mod checksum { #[cfg(feature = "proto-ipv6")] (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => { let mut proto_len = [0u8; 8]; - proto_len[7] = protocol.into(); + proto_len[7] = next_header.into(); NetworkEndian::write_u32(&mut proto_len[0..4], length); combine(&[ data(src_addr.as_bytes()), @@ -932,7 +932,7 @@ pub fn pretty_print_ip_payload>( let checksum_caps = ChecksumCapabilities::ignored(); let repr = ip_repr.into(); - match repr.protocol() { + match repr.next_header() { #[cfg(feature = "proto-ipv4")] Protocol::Icmp => { indent.increase(f)?; @@ -1033,7 +1033,7 @@ pub(crate) mod test { macro_rules! generate_common_tests { ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path, - $addr_from:path, $nxthdr:ident, $bytes_a:expr, $bytes_b:expr, + $addr_from:path, $bytes_a:expr, $bytes_b:expr, $unspecified:expr) => { mod $name { use super::*; @@ -1049,7 +1049,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: $ip_addr(ip_addr_a), dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + next_header: proto, hop_limit: 0x2a, payload_len, } @@ -1057,7 +1057,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 0x2a, payload_len })) @@ -1067,7 +1067,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: IpAddress::Unspecified, dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + next_header: proto, hop_limit: 64, payload_len } @@ -1079,7 +1079,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: IpAddress::Unspecified, dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + next_header: proto, hop_limit: 64, payload_len } @@ -1087,7 +1087,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 64, payload_len })) @@ -1097,7 +1097,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: $ip_addr($unspecified), dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + next_header: proto, hop_limit: 64, payload_len } @@ -1105,7 +1105,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 64, payload_len })) @@ -1115,7 +1115,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: $ip_addr($unspecified), dst_addr: $ip_addr(ip_addr_b), - protocol: proto, + next_header: proto, hop_limit: 64, payload_len } @@ -1123,7 +1123,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: $unspecified, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 64, payload_len })) @@ -1133,7 +1133,7 @@ pub(crate) mod test { $ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 255, payload_len }) @@ -1141,7 +1141,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 255, payload_len })) @@ -1151,7 +1151,7 @@ pub(crate) mod test { $ip_repr($repr { src_addr: $unspecified, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 255, payload_len }) @@ -1163,7 +1163,7 @@ pub(crate) mod test { $ip_repr($repr { src_addr: $unspecified, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 64, payload_len }) @@ -1171,7 +1171,7 @@ pub(crate) mod test { Ok($ip_repr($repr { src_addr: ip_addr_a, dst_addr: ip_addr_b, - $nxthdr: proto, + next_header: proto, hop_limit: 64, payload_len })) @@ -1186,7 +1186,6 @@ pub(crate) mod test { Repr::Ipv4, IpAddress::Ipv4, Ipv4Address::from_bytes, - protocol, $addr_bytes_a, $addr_bytes_b, Ipv4Address::UNSPECIFIED @@ -1199,7 +1198,6 @@ pub(crate) mod test { Repr::Ipv6, IpAddress::Ipv6, Ipv6Address::from_bytes, - next_header, $addr_bytes_a, $addr_bytes_b, Ipv6Address::UNSPECIFIED @@ -1224,7 +1222,7 @@ pub(crate) mod test { Repr::Unspecified { src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED), dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED), - protocol: IpProtocol::Icmpv6, + next_header: IpProtocol::Icmpv6, hop_limit: 0xff, payload_len: 0, } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 74140a17a..9fb3c0682 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -396,9 +396,9 @@ impl> Packet { data[field::TTL] } - /// Return the protocol field. + /// Return the next_header (protocol) field. #[inline] - pub fn protocol(&self) -> Protocol { + pub fn next_header(&self) -> Protocol { let data = self.buffer.as_ref(); Protocol::from(data[field::PROTOCOL]) } @@ -532,9 +532,9 @@ impl + AsMut<[u8]>> Packet { data[field::TTL] = value } - /// Set the protocol field. + /// Set the next header (protocol) field. #[inline] - pub fn set_protocol(&mut self, value: Protocol) { + pub fn set_next_header(&mut self, value: Protocol) { let data = self.buffer.as_mut(); data[field::PROTOCOL] = value.into() } @@ -591,7 +591,7 @@ impl> AsRef<[u8]> for Packet { pub struct Repr { pub src_addr: Address, pub dst_addr: Address, - pub protocol: Protocol, + pub next_header: Protocol, pub payload_len: usize, pub hop_limit: u8, } @@ -626,7 +626,7 @@ impl Repr { Ok(Repr { src_addr: packet.src_addr(), dst_addr: packet.dst_addr(), - protocol: packet.protocol(), + next_header: packet.next_header(), payload_len: payload_len, hop_limit: packet.hop_limit(), }) @@ -656,7 +656,7 @@ impl Repr { packet.set_dont_frag(true); packet.set_frag_offset(0); packet.set_hop_limit(self.hop_limit); - packet.set_protocol(self.protocol); + packet.set_next_header(self.next_header); packet.set_src_addr(self.src_addr); packet.set_dst_addr(self.dst_addr); @@ -681,7 +681,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { " src={} dst={} proto={} hop_limit={}", self.src_addr(), self.dst_addr(), - self.protocol(), + self.next_header(), self.hop_limit() )?; if self.version() != 4 { @@ -720,7 +720,7 @@ impl fmt::Display for Repr { write!( f, "IPv4 src={} dst={} proto={}", - self.src_addr, self.dst_addr, self.protocol + self.src_addr, self.dst_addr, self.next_header ) } } @@ -777,7 +777,7 @@ mod test { assert!(packet.dont_frag()); assert_eq!(packet.frag_offset(), 0x203 * 8); assert_eq!(packet.hop_limit(), 0x1a); - assert_eq!(packet.protocol(), Protocol::Icmp); + assert_eq!(packet.next_header(), Protocol::Icmp); assert_eq!(packet.checksum(), 0xd56e); assert_eq!(packet.src_addr(), Address([0x11, 0x12, 0x13, 0x14])); assert_eq!(packet.dst_addr(), Address([0x21, 0x22, 0x23, 0x24])); @@ -800,7 +800,7 @@ mod test { packet.set_dont_frag(true); packet.set_frag_offset(0x203 * 8); packet.set_hop_limit(0x1a); - packet.set_protocol(Protocol::Icmp); + packet.set_next_header(Protocol::Icmp); packet.set_src_addr(Address([0x11, 0x12, 0x13, 0x14])); packet.set_dst_addr(Address([0x21, 0x22, 0x23, 0x24])); packet.fill_checksum(); @@ -844,7 +844,7 @@ mod test { Repr { src_addr: Address([0x11, 0x12, 0x13, 0x14]), dst_addr: Address([0x21, 0x22, 0x23, 0x24]), - protocol: Protocol::Icmp, + next_header: Protocol::Icmp, payload_len: 4, hop_limit: 64, } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index cd021524b..f508a7b9c 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -50,7 +50,7 @@ use smoltcp::wire::*; let repr = Ipv4Repr { src_addr: Ipv4Address::new(10, 0, 0, 1), dst_addr: Ipv4Address::new(10, 0, 0, 2), - protocol: IpProtocol::Tcp, + next_header: IpProtocol::Tcp, payload_len: 10, hop_limit: 64 }; From 5989896299d0ee03c829a6e600452ebae3b82588 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 11 Dec 2021 02:45:59 +0100 Subject: [PATCH 292/566] Remove IpRepr::Unspecified and lowering. Choosing the right source IP address is now responsibility of the individual sockets. --- Cargo.toml | 1 + src/iface/interface.rs | 22 ++- src/socket/icmp.rs | 16 +- src/socket/tcp.rs | 134 ++++++++------ src/socket/udp.rs | 154 ++++++++-------- src/wire/ip.rs | 407 ++++------------------------------------- src/wire/ipv4.rs | 7 + src/wire/ipv6.rs | 7 + 8 files changed, 221 insertions(+), 527 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 49ec929d1..562befd9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } +cfg-if = "1.0.0" [dev-dependencies] env_logger = "0.9" diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 6f79d051d..268b6d481 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -923,7 +923,8 @@ where (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { respond!(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) } - _ => Err(Error::Unaddressable), + #[allow(unreachable_patterns)] + _ => unreachable!(), }), #[cfg(feature = "socket-udp")] Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { @@ -1811,7 +1812,8 @@ impl<'a> InterfaceInner<'a> { }; Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) } - _ => Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + _ => unreachable!(), }, // Ignore any echo replies. @@ -1821,7 +1823,8 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr), - _ => Ok(None), + #[allow(unreachable_patterns)] + _ => unreachable!(), }, // Don't report an error if a packet with unknown type @@ -1996,7 +1999,8 @@ impl<'a> InterfaceInner<'a> { }; match ip_repr { IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)), - _ => Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + _ => unreachable!(), } } @@ -2133,7 +2137,6 @@ impl<'a> InterfaceInner<'a> { }; Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) } - IpRepr::Unspecified { .. } => Err(Error::Unaddressable), } } @@ -2410,7 +2413,9 @@ impl<'a> InterfaceInner<'a> { } fn dispatch_ip(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { - let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; + let ip_repr = packet.ip_repr(); + assert!(!ip_repr.src_addr().is_unspecified()); + assert!(!ip_repr.dst_addr().is_unspecified()); match self.caps.medium { #[cfg(feature = "medium-ethernet")] @@ -2433,7 +2438,6 @@ impl<'a> InterfaceInner<'a> { IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), - _ => return, } ip_repr.emit(frame.payload_mut(), &caps.checksum); @@ -2463,7 +2467,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] fn dispatch_ieee802154(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { - let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; + let ip_repr = packet.ip_repr(); + assert!(!ip_repr.src_addr().is_unspecified()); + assert!(!ip_repr.dst_addr().is_unspecified()); match self.caps.medium { #[cfg(feature = "medium-ieee802154")] diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 79c0d55b0..6be720c19 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -747,13 +747,13 @@ mod test_ipv4 { }, data: data, }; - let ip_repr = IpRepr::Unspecified { - src_addr: REMOTE_IPV4.into(), - dst_addr: LOCAL_IPV4.into(), + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: REMOTE_IPV4, + dst_addr: LOCAL_IPV4, next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 0x40, - }; + }); assert!(!socket.can_recv()); @@ -1015,13 +1015,13 @@ mod test_ipv6 { }, data: data, }; - let ip_repr = IpRepr::Unspecified { - src_addr: REMOTE_IPV6.into(), - dst_addr: LOCAL_IPV6.into(), + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: REMOTE_IPV6, + dst_addr: LOCAL_IPV6, next_header: IpProtocol::Icmpv6, payload_len: icmp_repr.buffer_len(), hop_limit: 0x40, - }; + }); assert!(!socket.can_recv()); diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 5d56bd1dc..94a1f244c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1137,13 +1137,13 @@ impl<'a> TcpSocket<'a> { sack_ranges: [None, None, None], payload: &[], }; - let ip_reply_repr = IpRepr::Unspecified { - src_addr: ip_repr.dst_addr(), - dst_addr: ip_repr.src_addr(), - next_header: IpProtocol::Tcp, - payload_len: reply_repr.buffer_len(), - hop_limit: 64, - }; + let ip_reply_repr = IpRepr::new( + ip_repr.dst_addr(), + ip_repr.src_addr(), + IpProtocol::Tcp, + reply_repr.buffer_len(), + 64, + ); (ip_reply_repr, reply_repr) } @@ -2136,14 +2136,13 @@ impl<'a> TcpSocket<'a> { // Construct the lowered IP representation. // We might need this to calculate the MSS, so do it early. - let mut ip_repr = IpRepr::Unspecified { - src_addr: self.local_endpoint.addr, - dst_addr: self.remote_endpoint.addr, - next_header: IpProtocol::Tcp, - hop_limit: self.hop_limit.unwrap_or(64), - payload_len: 0, - } - .lower(&[])?; + let mut ip_repr = IpRepr::new( + self.local_endpoint.addr, + self.remote_endpoint.addr, + IpProtocol::Tcp, + 0, + self.hop_limit.unwrap_or(64), + ); // Construct the basic TCP representation, an empty ACK packet. // We'll adjust this to be more specific as needed. @@ -2417,8 +2416,7 @@ impl<'a> fmt::Write for TcpSocket<'a> { #[cfg(test)] mod test { use super::*; - use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_UNSPECIFIED}; - use crate::wire::{IpAddress, IpCidr, IpRepr}; + use crate::wire::{IpAddress, IpRepr}; use core::i32; use std::ops::{Deref, DerefMut}; use std::vec::Vec; @@ -2430,23 +2428,53 @@ mod test { const LOCAL_PORT: u16 = 80; const REMOTE_PORT: u16 = 49500; const LOCAL_END: IpEndpoint = IpEndpoint { - addr: MOCK_IP_ADDR_1, + addr: LOCAL_ADDR.into_address(), port: LOCAL_PORT, }; const REMOTE_END: IpEndpoint = IpEndpoint { - addr: MOCK_IP_ADDR_2, + addr: REMOTE_ADDR.into_address(), port: REMOTE_PORT, }; const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000); const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10001); - const SEND_IP_TEMPL: IpRepr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, - dst_addr: MOCK_IP_ADDR_2, + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4")] { + use crate::wire::Ipv4Address as IpvXAddress; + use crate::wire::Ipv4Repr as IpvXRepr; + use IpRepr::Ipv4 as IpReprIpvX; + + const LOCAL_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 1]); + const REMOTE_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 2]); + const OTHER_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 3]); + + const BASE_MSS: u16 = 1460; + } else { + use crate::wire::Ipv6Address as IpvXAddress; + use crate::wire::Ipv6Repr as IpvXRepr; + use IpRepr::Ipv6 as IpReprIpvX; + + const LOCAL_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ]); + const REMOTE_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + ]); + const OTHER_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + ]); + + const BASE_MSS: u16 = 1440; + } + } + + const SEND_IP_TEMPL: IpRepr = IpReprIpvX(IpvXRepr { + src_addr: LOCAL_ADDR, + dst_addr: REMOTE_ADDR, next_header: IpProtocol::Tcp, payload_len: 20, hop_limit: 64, - }; + }); const SEND_TEMPL: TcpRepr<'static> = TcpRepr { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, @@ -2460,13 +2488,13 @@ mod test { sack_ranges: [None, None, None], payload: &[], }; - const _RECV_IP_TEMPL: IpRepr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, - dst_addr: MOCK_IP_ADDR_2, + const _RECV_IP_TEMPL: IpRepr = IpReprIpvX(IpvXRepr { + src_addr: LOCAL_ADDR, + dst_addr: REMOTE_ADDR, next_header: IpProtocol::Tcp, payload_len: 20, hop_limit: 64, - }; + }); const RECV_TEMPL: TcpRepr<'static> = TcpRepr { src_port: LOCAL_PORT, dst_port: REMOTE_PORT, @@ -2481,11 +2509,6 @@ mod test { payload: &[], }; - #[cfg(feature = "proto-ipv6")] - const BASE_MSS: u16 = 1440; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - const BASE_MSS: u16 = 1460; - // =========================================================================================// // Helper functions // =========================================================================================// @@ -2515,13 +2538,13 @@ mod test { ) -> Result>> { socket.cx.set_now(timestamp); - let ip_repr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_1, + let ip_repr = IpReprIpvX(IpvXRepr { + src_addr: REMOTE_ADDR, + dst_addr: LOCAL_ADDR, next_header: IpProtocol::Tcp, payload_len: repr.buffer_len(), hop_limit: 64, - }; + }); net_trace!("send: {}", repr); assert!(socket.socket.accepts(&mut socket.cx, &ip_repr, repr)); @@ -2545,11 +2568,9 @@ mod test { let result = socket .socket .dispatch(&mut socket.cx, |_, (ip_repr, tcp_repr)| { - let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap(); - assert_eq!(ip_repr.next_header(), IpProtocol::Tcp); - assert_eq!(ip_repr.src_addr(), MOCK_IP_ADDR_1); - assert_eq!(ip_repr.dst_addr(), MOCK_IP_ADDR_2); + assert_eq!(ip_repr.src_addr(), LOCAL_ADDR.into()); + assert_eq!(ip_repr.dst_addr(), REMOTE_ADDR.into()); assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len()); net_trace!("recv: {}", tcp_repr); @@ -3223,12 +3244,12 @@ mod test { ); assert_eq!( s.socket - .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 0)), + .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 0)), Err(Error::Unaddressable) ); assert_eq!( s.socket - .connect(&mut s.cx, (MOCK_UNSPECIFIED, 0), LOCAL_END), + .connect(&mut s.cx, (IpvXAddress::UNSPECIFIED, 0), LOCAL_END), Err(Error::Unaddressable) ); assert_eq!( @@ -3282,7 +3303,7 @@ mod test { let mut s = socket(); assert_eq!( s.socket - .connect(&mut s.cx, REMOTE_END, (MOCK_UNSPECIFIED, 80)), + .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 80)), Ok(()) ); s.abort(); @@ -3298,8 +3319,7 @@ mod test { fn test_connect_specified_local() { let mut s = socket(); assert_eq!( - s.socket - .connect(&mut s.cx, REMOTE_END, (MOCK_IP_ADDR_2, 80)), + s.socket.connect(&mut s.cx, REMOTE_END, (REMOTE_ADDR, 80)), Ok(()) ); } @@ -6945,31 +6965,31 @@ mod test { ..SEND_TEMPL }; - let ip_repr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_1, + let ip_repr = IpReprIpvX(IpvXRepr { + src_addr: REMOTE_ADDR, + dst_addr: LOCAL_ADDR, next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, - }; + }); assert!(s.socket.accepts(&mut s.cx, &ip_repr, &tcp_repr)); - let ip_repr_wrong_src = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_3, - dst_addr: MOCK_IP_ADDR_1, + let ip_repr_wrong_src = IpReprIpvX(IpvXRepr { + src_addr: OTHER_ADDR, + dst_addr: LOCAL_ADDR, next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, - }; + }); assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_src, &tcp_repr)); - let ip_repr_wrong_dst = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_2, - dst_addr: MOCK_IP_ADDR_3, + let ip_repr_wrong_dst = IpReprIpvX(IpvXRepr { + src_addr: REMOTE_ADDR, + dst_addr: OTHER_ADDR, next_header: IpProtocol::Tcp, payload_len: tcp_repr.buffer_len(), hop_limit: 64, - }; + }); assert!(!s.socket.accepts(&mut s.cx, &ip_repr_wrong_dst, &tcp_repr)); } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index df0bc128e..c8ef6cd1e 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -359,13 +359,13 @@ impl<'a> UdpSocket<'a> { src_port: endpoint.port, dst_port: remote_endpoint.port, }; - let ip_repr = IpRepr::Unspecified { - src_addr: endpoint.addr, - dst_addr: remote_endpoint.addr, - next_header: IpProtocol::Udp, - payload_len: repr.header_len() + payload_buf.len(), - hop_limit: hop_limit, - }; + let ip_repr = IpRepr::new( + endpoint.addr, + remote_endpoint.addr, + IpProtocol::Udp, + repr.header_len() + payload_buf.len(), + hop_limit, + ); emit(cx, (ip_repr, repr, payload_buf)) })?; @@ -387,11 +387,6 @@ impl<'a> UdpSocket<'a> { #[cfg(test)] mod test { use super::*; - use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3}; - #[cfg(feature = "proto-ipv4")] - use crate::wire::Ipv4Repr; - #[cfg(feature = "proto-ipv6")] - use crate::wire::Ipv6Repr; use crate::wire::{IpAddress, IpRepr, UdpRepr}; fn buffer(packets: usize) -> UdpSocketBuffer<'static> { @@ -411,22 +406,64 @@ mod test { const LOCAL_PORT: u16 = 53; const REMOTE_PORT: u16 = 49500; + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4")] { + use crate::wire::Ipv4Address as IpvXAddress; + use crate::wire::Ipv4Repr as IpvXRepr; + use IpRepr::Ipv4 as IpReprIpvX; + + const LOCAL_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 1]); + const REMOTE_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 2]); + const OTHER_ADDR: IpvXAddress = IpvXAddress([192, 168, 1, 3]); + } else { + use crate::wire::Ipv6Address as IpvXAddress; + use crate::wire::Ipv6Repr as IpvXRepr; + use IpRepr::Ipv6 as IpReprIpvX; + + const LOCAL_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + ]); + const REMOTE_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + ]); + const OTHER_ADDR: IpvXAddress = IpvXAddress([ + 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, + ]); + } + } + pub const LOCAL_END: IpEndpoint = IpEndpoint { - addr: MOCK_IP_ADDR_1, + addr: LOCAL_ADDR.into_address(), port: LOCAL_PORT, }; pub const REMOTE_END: IpEndpoint = IpEndpoint { - addr: MOCK_IP_ADDR_2, + addr: REMOTE_ADDR.into_address(), port: REMOTE_PORT, }; - pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, - dst_addr: MOCK_IP_ADDR_2, + pub const LOCAL_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr { + src_addr: LOCAL_ADDR, + dst_addr: REMOTE_ADDR, next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 64, - }; + }); + + pub const REMOTE_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr { + src_addr: REMOTE_ADDR, + dst_addr: LOCAL_ADDR, + next_header: IpProtocol::Udp, + payload_len: 8 + 6, + hop_limit: 64, + }); + + pub const BAD_IP_REPR: IpRepr = IpReprIpvX(IpvXRepr { + src_addr: REMOTE_ADDR, + dst_addr: OTHER_ADDR, + next_header: IpProtocol::Udp, + payload_len: 8 + 6, + hop_limit: 64, + }); const LOCAL_UDP_REPR: UdpRepr = UdpRepr { src_port: LOCAL_PORT, @@ -440,28 +477,6 @@ mod test { const PAYLOAD: &[u8] = b"abcdef"; - fn remote_ip_repr() -> IpRepr { - match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_1) { - #[cfg(feature = "proto-ipv4")] - (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr { - src_addr: src, - dst_addr: dst, - next_header: IpProtocol::Udp, - payload_len: 8 + 6, - hop_limit: 64, - }), - #[cfg(feature = "proto-ipv6")] - (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr { - src_addr: src, - dst_addr: dst, - next_header: IpProtocol::Udp, - payload_len: 8 + 6, - hop_limit: 64, - }), - _ => unreachable!(), - } - } - #[test] fn test_bind_unaddressable() { let mut socket = socket(buffer(0), buffer(0)); @@ -567,16 +582,16 @@ mod test { assert!(!socket.can_recv()); assert_eq!(socket.recv(), Err(Error::Exhausted)); - assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); assert!(socket.can_recv()); - assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), Err(Error::Exhausted) ); assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); @@ -593,7 +608,7 @@ mod test { assert_eq!(socket.peek(), Err(Error::Exhausted)); assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END))); @@ -608,9 +623,9 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert!(socket.accepts(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR)); + assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); @@ -627,7 +642,7 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD), + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), Ok(()) ); @@ -652,13 +667,13 @@ mod test { s.dispatch(&mut cx, |_, (ip_repr, _, _)| { assert_eq!( ip_repr, - IpRepr::Unspecified { - src_addr: MOCK_IP_ADDR_1, - dst_addr: MOCK_IP_ADDR_2, + IpReprIpvX(IpvXRepr { + src_addr: LOCAL_ADDR, + dst_addr: REMOTE_ADDR, next_header: IpProtocol::Udp, payload_len: 8 + 6, hop_limit: 0x2a, - } + }) ); Ok(()) }), @@ -674,44 +689,22 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); let mut udp_repr = REMOTE_UDP_REPR; - assert!(socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr)); + assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &udp_repr)); udp_repr.dst_port += 1; - assert!(!socket.accepts(&mut cx, &remote_ip_repr(), &udp_repr)); + assert!(!socket.accepts(&mut cx, &REMOTE_IP_REPR, &udp_repr)); } #[test] fn test_doesnt_accept_wrong_ip() { - fn generate_bad_repr() -> IpRepr { - match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_3) { - #[cfg(feature = "proto-ipv4")] - (IpAddress::Ipv4(src), IpAddress::Ipv4(dst)) => IpRepr::Ipv4(Ipv4Repr { - src_addr: src, - dst_addr: dst, - next_header: IpProtocol::Udp, - payload_len: 8 + 6, - hop_limit: 64, - }), - #[cfg(feature = "proto-ipv6")] - (IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr { - src_addr: src, - dst_addr: dst, - next_header: IpProtocol::Udp, - payload_len: 8 + 6, - hop_limit: 64, - }), - _ => unreachable!(), - } - } - let mut cx = Context::mock(); let mut port_bound_socket = socket(buffer(1), buffer(0)); assert_eq!(port_bound_socket.bind(LOCAL_PORT), Ok(())); - assert!(port_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR)); + assert!(port_bound_socket.accepts(&mut cx, &BAD_IP_REPR, &REMOTE_UDP_REPR)); let mut ip_bound_socket = socket(buffer(1), buffer(0)); assert_eq!(ip_bound_socket.bind(LOCAL_END), Ok(())); - assert!(!ip_bound_socket.accepts(&mut cx, &generate_bad_repr(), &REMOTE_UDP_REPR)); + assert!(!ip_bound_socket.accepts(&mut cx, &BAD_IP_REPR, &REMOTE_UDP_REPR)); } #[test] @@ -740,10 +733,7 @@ mod test { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, }; - assert_eq!( - socket.process(&mut cx, &remote_ip_repr(), &repr, &[]), - Ok(()) - ); + assert_eq!(socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]), Ok(())); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index d2b5127de..9170c5451 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -506,20 +506,12 @@ impl> From<(T, u16)> for Endpoint { /// An IP packet representation. /// -/// This enum abstracts the various versions of IP packets. It either contains a concrete -/// high-level representation for some IP protocol version, or an unspecified representation, -/// which permits the `IpAddress::Unspecified` addresses. +/// This enum abstracts the various versions of IP packets. It either contains an IPv4 +/// or IPv6 concrete high-level representation. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Repr { - Unspecified { - src_addr: Address, - dst_addr: Address, - next_header: Protocol, - payload_len: usize, - hop_limit: u8, - }, #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Repr), #[cfg(feature = "proto-ipv6")] @@ -541,10 +533,42 @@ impl From for Repr { } impl Repr { + /// Create a new IpRepr, choosing the right IP version for the src/dst addrs. + /// + /// # Panics + /// + /// Panics if `src_addr` and `dst_addr` are different IP version. + pub fn new( + src_addr: Address, + dst_addr: Address, + next_header: Protocol, + payload_len: usize, + hop_limit: u8, + ) -> Self { + match (src_addr, dst_addr) { + #[cfg(feature = "proto-ipv4")] + (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr { + src_addr, + dst_addr, + next_header, + payload_len, + hop_limit, + }), + #[cfg(feature = "proto-ipv6")] + (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr { + src_addr, + dst_addr, + next_header, + payload_len, + hop_limit, + }), + _ => panic!("IP version mismatch: src={:?} dst={:?}", src_addr, dst_addr), + } + } + /// Return the protocol version. pub fn version(&self) -> Version { match *self { - Repr::Unspecified { .. } => Version::Unspecified, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] @@ -555,7 +579,6 @@ impl Repr { /// Return the source address. pub fn src_addr(&self) -> Address { match *self { - Repr::Unspecified { src_addr, .. } => src_addr, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), #[cfg(feature = "proto-ipv6")] @@ -566,7 +589,6 @@ impl Repr { /// Return the destination address. pub fn dst_addr(&self) -> Address { match *self { - Repr::Unspecified { dst_addr, .. } => dst_addr, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), #[cfg(feature = "proto-ipv6")] @@ -577,7 +599,6 @@ impl Repr { /// Return the next header (protocol). pub fn next_header(&self) -> Protocol { match *self { - Repr::Unspecified { next_header, .. } => next_header, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.next_header, #[cfg(feature = "proto-ipv6")] @@ -588,7 +609,6 @@ impl Repr { /// Return the payload length. pub fn payload_len(&self) -> usize { match *self { - Repr::Unspecified { payload_len, .. } => payload_len, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.payload_len, #[cfg(feature = "proto-ipv6")] @@ -599,10 +619,6 @@ impl Repr { /// Set the payload length. pub fn set_payload_len(&mut self, length: usize) { match *self { - Repr::Unspecified { - ref mut payload_len, - .. - } => *payload_len = length, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(Ipv4Repr { ref mut payload_len, @@ -619,7 +635,6 @@ impl Repr { /// Return the TTL value. pub fn hop_limit(&self) -> u8 { match *self { - Repr::Unspecified { hop_limit, .. } => hop_limit, #[cfg(feature = "proto-ipv4")] Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, #[cfg(feature = "proto-ipv6")] @@ -627,156 +642,9 @@ impl Repr { } } - /// Convert an unspecified representation into a concrete one, or return - /// `Err(Error::Unaddressable)` if not possible. - /// - /// # Panics - /// This function panics if source and destination addresses belong to different families, - /// or the destination address is unspecified, since this indicates a logic error. - pub fn lower(&self, fallback_src_addrs: &[Cidr]) -> Result { - macro_rules! resolve_unspecified { - ($reprty:path, $ipty:path, $iprepr:expr, $fallbacks:expr) => { - if $iprepr.src_addr.is_unspecified() { - for cidr in $fallbacks { - match cidr.address() { - $ipty(addr) => { - $iprepr.src_addr = addr; - return Ok($reprty($iprepr)); - } - _ => (), - } - } - Err(Error::Unaddressable) - } else { - Ok($reprty($iprepr)) - } - }; - } - - match self { - #[cfg(feature = "proto-ipv4")] - &Repr::Unspecified { - src_addr: src_addr @ Address::Unspecified, - dst_addr: Address::Ipv4(dst_addr), - next_header, - payload_len, - hop_limit, - } - | &Repr::Unspecified { - src_addr: src_addr @ Address::Ipv4(_), - dst_addr: Address::Ipv4(dst_addr), - next_header, - payload_len, - hop_limit, - } if src_addr.is_unspecified() => { - let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr { - Some(src_ipv4_addr) - } else { - None - }; - for cidr in fallback_src_addrs { - if let Address::Ipv4(addr) = cidr.address() { - src_addr = Some(addr); - break; - } - } - Ok(Repr::Ipv4(Ipv4Repr { - src_addr: src_addr.ok_or(Error::Unaddressable)?, - dst_addr, - next_header, - payload_len, - hop_limit, - })) - } - - #[cfg(feature = "proto-ipv6")] - &Repr::Unspecified { - src_addr: src_addr @ Address::Unspecified, - dst_addr: Address::Ipv6(dst_addr), - next_header: protocol, - payload_len, - hop_limit, - } - | &Repr::Unspecified { - src_addr: src_addr @ Address::Ipv6(_), - dst_addr: Address::Ipv6(dst_addr), - next_header: protocol, - payload_len, - hop_limit, - } if src_addr.is_unspecified() => { - let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr { - Some(src_ipv6_addr) - } else { - None - }; - for cidr in fallback_src_addrs { - if let Address::Ipv6(addr) = cidr.address() { - src_addr = Some(addr); - break; - } - } - Ok(Repr::Ipv6(Ipv6Repr { - src_addr: src_addr.ok_or(Error::Unaddressable)?, - next_header: protocol, - dst_addr, - payload_len, - hop_limit, - })) - } - - #[cfg(feature = "proto-ipv4")] - &Repr::Unspecified { - src_addr: Address::Ipv4(src_addr), - dst_addr: Address::Ipv4(dst_addr), - next_header: protocol, - payload_len, - hop_limit, - } => Ok(Repr::Ipv4(Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, - next_header: protocol, - payload_len: payload_len, - hop_limit, - })), - - #[cfg(feature = "proto-ipv6")] - &Repr::Unspecified { - src_addr: Address::Ipv6(src_addr), - dst_addr: Address::Ipv6(dst_addr), - next_header: protocol, - payload_len, - hop_limit, - } => Ok(Repr::Ipv6(Ipv6Repr { - src_addr: src_addr, - dst_addr: dst_addr, - next_header: protocol, - payload_len: payload_len, - hop_limit: hop_limit, - })), - - #[cfg(feature = "proto-ipv4")] - &Repr::Ipv4(mut repr) => { - resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs) - } - - #[cfg(feature = "proto-ipv6")] - &Repr::Ipv6(mut repr) => { - resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs) - } - - &Repr::Unspecified { .. } => { - panic!("source and destination IP address families do not match") - } - } - } - /// Return the length of a header that will be emitted from this high-level representation. - /// - /// # Panics - /// This function panics if invoked on an unspecified representation. pub fn buffer_len(&self) -> usize { match *self { - Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.buffer_len(), #[cfg(feature = "proto-ipv6")] @@ -785,16 +653,12 @@ impl Repr { } /// Emit this high-level representation into a buffer. - /// - /// # Panics - /// This function panics if invoked on an unspecified representation. pub fn emit + AsMut<[u8]>>( &self, buffer: T, _checksum_caps: &ChecksumCapabilities, ) { match *self { - Repr::Unspecified { .. } => panic!("unspecified IP representation"), #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps), #[cfg(feature = "proto-ipv6")] @@ -806,9 +670,6 @@ impl Repr { /// high-level representation. /// /// This is the same as `repr.buffer_len() + repr.payload_len()`. - /// - /// # Panics - /// This function panics if invoked on an unspecified representation. pub fn total_len(&self) -> usize { self.buffer_len() + self.payload_len() } @@ -1031,204 +892,6 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Repr}; - macro_rules! generate_common_tests { - ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path, - $addr_from:path, $bytes_a:expr, $bytes_b:expr, - $unspecified:expr) => { - mod $name { - use super::*; - - #[test] - fn test_ip_repr_lower() { - let ip_addr_a = $addr_from(&$bytes_a); - let ip_addr_b = $addr_from(&$bytes_b); - let proto = IpProtocol::Icmp; - let payload_len = 10; - - assert_eq!( - Repr::Unspecified { - src_addr: $ip_addr(ip_addr_a), - dst_addr: $ip_addr(ip_addr_b), - next_header: proto, - hop_limit: 0x2a, - payload_len, - } - .lower(&[]), - Ok($ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 0x2a, - payload_len - })) - ); - - assert_eq!( - Repr::Unspecified { - src_addr: IpAddress::Unspecified, - dst_addr: $ip_addr(ip_addr_b), - next_header: proto, - hop_limit: 64, - payload_len - } - .lower(&[]), - Err(Error::Unaddressable) - ); - - assert_eq!( - Repr::Unspecified { - src_addr: IpAddress::Unspecified, - dst_addr: $ip_addr(ip_addr_b), - next_header: proto, - hop_limit: 64, - payload_len - } - .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 64, - payload_len - })) - ); - - assert_eq!( - Repr::Unspecified { - src_addr: $ip_addr($unspecified), - dst_addr: $ip_addr(ip_addr_b), - next_header: proto, - hop_limit: 64, - payload_len - } - .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 64, - payload_len - })) - ); - - assert_eq!( - Repr::Unspecified { - src_addr: $ip_addr($unspecified), - dst_addr: $ip_addr(ip_addr_b), - next_header: proto, - hop_limit: 64, - payload_len - } - .lower(&[]), - Ok($ip_repr($repr { - src_addr: $unspecified, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 64, - payload_len - })) - ); - - assert_eq!( - $ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 255, - payload_len - }) - .lower(&[]), - Ok($ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 255, - payload_len - })) - ); - - assert_eq!( - $ip_repr($repr { - src_addr: $unspecified, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 255, - payload_len - }) - .lower(&[]), - Err(Error::Unaddressable) - ); - - assert_eq!( - $ip_repr($repr { - src_addr: $unspecified, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 64, - payload_len - }) - .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]), - Ok($ip_repr($repr { - src_addr: ip_addr_a, - dst_addr: ip_addr_b, - next_header: proto, - hop_limit: 64, - payload_len - })) - ); - } - } - }; - (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => { - generate_common_tests!( - ipv4, - Ipv4Repr, - Repr::Ipv4, - IpAddress::Ipv4, - Ipv4Address::from_bytes, - $addr_bytes_a, - $addr_bytes_b, - Ipv4Address::UNSPECIFIED - ); - }; - (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => { - generate_common_tests!( - ipv6, - Ipv6Repr, - Repr::Ipv6, - IpAddress::Ipv6, - Ipv6Address::from_bytes, - $addr_bytes_a, - $addr_bytes_b, - Ipv6Address::UNSPECIFIED - ); - }; - } - - #[cfg(feature = "proto-ipv4")] - generate_common_tests!(ipv4 - [1, 2, 3, 4], - [5, 6, 7, 8]); - - #[cfg(feature = "proto-ipv6")] - generate_common_tests!(ipv6 - [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); - - #[test] - #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))] - #[should_panic(expected = "source and destination IP address families do not match")] - fn test_lower_between_families() { - Repr::Unspecified { - src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED), - dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED), - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: 0, - } - .lower(&[]); - } - #[test] fn endpoint_unspecified() { assert!(!Endpoint::UNSPECIFIED.is_specified()); diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 9fb3c0682..006de4bb4 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -87,6 +87,13 @@ impl Address { pub const fn is_loopback(&self) -> bool { self.0[0] == 127 } + + /// Convert to an `IpAddress`. + /// + /// Same as `.into()`, but works in `const`. + pub const fn into_address(self) -> super::IpAddress { + super::IpAddress::Ipv4(self) + } } #[cfg(feature = "std")] diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 0dc97fab3..b0f8cfb2a 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -188,6 +188,13 @@ impl Address { self.0[13], self.0[14], self.0[15], ]) } + + /// Convert to an `IpAddress`. + /// + /// Same as `.into()`, but works in `const`. + pub const fn into_address(self) -> super::IpAddress { + super::IpAddress::Ipv6(self) + } } #[cfg(feature = "std")] From 53c46d78a9dcb341236d6f95c2d66cad424b635a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 22 Feb 2022 12:03:06 +0100 Subject: [PATCH 293/566] add fragmentation mechanism --- src/iface/fragmentation.rs | 553 +++++++++++++++++++++++++++++++++++++ src/iface/interface.rs | 2 + src/iface/mod.rs | 7 + src/lib.rs | 36 +++ src/socket/tcp.rs | 8 +- src/socket/udp.rs | 4 +- src/storage/assembler.rs | 65 +++-- 7 files changed, 644 insertions(+), 31 deletions(-) create mode 100644 src/iface/fragmentation.rs diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs new file mode 100644 index 000000000..eac7f3d77 --- /dev/null +++ b/src/iface/fragmentation.rs @@ -0,0 +1,553 @@ +use managed::{ManagedMap, ManagedSlice}; + +use crate::storage::Assembler; +use crate::time::Instant; +use crate::Error; +use crate::Result; + +pub trait PacketAssemblerInfo: PartialEq { + /// Calculate a new offset based on some other information. + fn calc_offset(&self, offset: usize) -> usize; +} + +#[derive(Debug, PartialEq)] +pub struct NoInfo; + +impl PacketAssemblerInfo for NoInfo { + #[inline] + fn calc_offset(&self, offset: usize) -> usize { + offset + } +} + +/// Holds different fragments of one packet, used for assembling fragmented packets. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PacketAssembler<'a, Info: PacketAssemblerInfo = NoInfo> { + buffer: ManagedSlice<'a, u8>, + assembler: AssemblerState, +} + +/// Holds the state of the assembling of one packet. +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum AssemblerState { + NotInit, + Assembling { + assembler: Assembler, + total_size: usize, + info: Info, + last_updated: Instant, + started_on: Instant, + }, +} + +impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { + /// Create a new empty buffer for fragments. + pub fn new(storage: S) -> Self + where + S: Into>, + { + let s = storage.into(); + PacketAssembler { + buffer: s, + assembler: AssemblerState::NotInit, + } + } + + /// Start with saving fragments. + /// We initialize the assembler with the total size of the final packet. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too smal for holding all the + /// fragments of a packet. + pub(crate) fn start( + &mut self, + total_size: usize, + info: Info, + start_time: Instant, + ) -> Result<()> { + match &mut self.buffer { + ManagedSlice::Borrowed(b) if b.len() < total_size => { + return Err(Error::PacketAssemblerBufferTooSmall); + } + ManagedSlice::Borrowed(_) => (), + #[cfg(any(feature = "std", feature = "alloc"))] + ManagedSlice::Owned(b) => { + b.resize(total_size, 0); + } + } + + self.assembler = AssemblerState::Assembling { + assembler: Assembler::new(total_size), + total_size, + info, + last_updated: start_time, + started_on: start_time, + }; + + Ok(()) + } + + /// Add a fragment into the packet that is being reassembled. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the + /// assembler with [Self::start]). + /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing + /// place. + /// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data. + pub(crate) fn add(&mut self, data: &[u8], offset: usize, now: Instant) -> Result { + match self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { + ref mut assembler, + total_size, + ref info, + ref mut last_updated, + .. + } => { + let offset = info.calc_offset(offset); + + if offset + data.len() > total_size { + return Err(Error::PacketAssemblerBufferTooSmall); + } + + let len = data.len(); + self.buffer[offset..][..len].copy_from_slice(data); + + match assembler.add(offset, data.len()) { + Ok(false) => { + *last_updated = now; + self.is_complete() + } + Ok(true) => Err(Error::PacketAssemblerOverlap), + // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? + Err(_) => Err(Error::PacketAssemblerTooManyHoles), + } + } + } + } + + /// Get an immutable slice of the underlying packet data. + /// This will mark the assembler state as [`AssemblerState::NotInit`] such that it can be reused. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the + /// assembler with [`Self::start`]). + /// - Returns [`Error::PacketAssemblerIncomplete`] when not all the fragments have been collected. + pub(crate) fn assemble(&mut self) -> Result<&'_ [u8]> { + let b = match self.assembler { + AssemblerState::NotInit => return Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { total_size, .. } => { + if self.is_complete()? { + let a = &self.buffer[..total_size]; + self.assembler = AssemblerState::NotInit; + a + } else { + return Err(Error::PacketAssemblerIncomplete); + } + } + }; + self.assembler = AssemblerState::NotInit; + Ok(b) + } + + /// Returns `true` when all fragments have been received, otherwise `false`. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the + /// assembler with [`Self::start`]). + pub(crate) fn is_complete(&self) -> Result { + match &self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { + assembler, + total_size, + .. + } => { + if let Some(front) = assembler.peek_front() { + Ok(front == *total_size) + } else { + Ok(false) + } + } + } + } + + /// Returns `true` when the packet assembler is free to use. + fn is_free(&self) -> bool { + self.assembler == AssemblerState::NotInit + } + + /// Returns the [`Instant`] when the packet assembler was started. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the packet assembler was not initialized. + pub fn start_time(&self) -> Result { + match self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { started_on, .. } => Ok(started_on), + } + } + + /// Returns the [`Instant`] when the packet assembler was last updated. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the packet assembler was not initialized. + pub fn last_update_time(&self) -> Result { + match self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { last_updated, .. } => Ok(last_updated), + } + } + + /// Mark this assembler as [`AssemblerState::NotInit`]. + /// This is then cleaned up by the [`PacketAssemblerSet`]. + pub fn mark_discarded(&mut self) { + self.assembler = AssemblerState::NotInit; + } +} + +/// Set holding multiple [`PacketAssembler`]. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> { + packet_buffer: ManagedSlice<'a, PacketAssembler<'a, Info>>, + index_buffer: ManagedMap<'a, Key, u8>, +} + +impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerSet<'a, K, Info> { + /// Create a new set of packet assemblers. + /// + /// # Panics + /// + /// This will panic when: + /// - The packet buffer and index buffer don't have the same size or are empty (when they are + /// both borrowed). + /// - The packet buffer is empty (when only the packet buffer is borrowed). + /// - The index buffer is empty (when only the index buffer is borrowed). + pub fn new(packet_buffer: FB, index_buffer: IB) -> Self + where + FB: Into>>, + IB: Into>, + { + let packet_buffer = packet_buffer.into(); + let index_buffer = index_buffer.into(); + + match (&packet_buffer, &index_buffer) { + (ManagedSlice::Borrowed(f), ManagedMap::Borrowed(i)) => { + if f.len() != i.len() { + panic!("The amount of places in the index buffer must be the same as the amount of possible fragments assemblers."); + } + } + #[cfg(any(feature = "std", feature = "alloc"))] + (ManagedSlice::Borrowed(f), ManagedMap::Owned(_)) => { + if f.is_empty() { + panic!("The packet buffer cannot be empty."); + } + } + #[cfg(any(feature = "std", feature = "alloc"))] + (ManagedSlice::Owned(_), ManagedMap::Borrowed(i)) => { + if i.is_empty() { + panic!("The index buffer cannot be empty."); + } + } + #[cfg(any(feature = "std", feature = "alloc"))] + (ManagedSlice::Owned(_), ManagedMap::Owned(_)) => (), + } + + Self { + packet_buffer, + index_buffer, + } + } + + /// Reserve a [`PacketAssembler`], which is linked to a specific key. + /// Returns the reserved fragments assembler. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerSetFull`] when every [`PacketAssembler`] in the buffer is used (only + /// when the non allocating version of is used). + pub(crate) fn reserve_with_key(&mut self, key: &K) -> Result<&mut PacketAssembler<'a, Info>> { + if self.packet_buffer.len() == self.index_buffer.len() { + match &mut self.packet_buffer { + ManagedSlice::Borrowed(_) => return Err(Error::PacketAssemblerSetFull), + #[cfg(any(feature = "std", feature = "alloc"))] + ManagedSlice::Owned(b) => { + b.resize_with(self.index_buffer.len() + 1, || { + PacketAssembler::new(Vec::new()) + }); + } + } + } + + let i = self + .get_free_packet_assembler() + .ok_or(Error::PacketAssemblerSetFull)?; + + // NOTE(thvdveld): this should not fail because we already checked the available space. + match self.index_buffer.insert(*key, i as u8) { + Ok(_) => Ok(&mut self.packet_buffer[i]), + Err(_) => unreachable!(), + } + } + + /// Return the first free packet assembler available from the cache. + fn get_free_packet_assembler(&self) -> Option { + self.packet_buffer + .iter() + .enumerate() + .find(|(_, b)| b.is_free()) + .map(|(i, _)| i) + } + + /// Return a mutable slice to a packet assembler. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the key was not found in the set. + pub(crate) fn get_packet_assembler_mut( + &mut self, + key: &K, + ) -> Result<&mut PacketAssembler<'a, Info>> { + if let Some(i) = self.index_buffer.get(key) { + Ok(&mut self.packet_buffer[*i as usize]) + } else { + Err(Error::PacketAssemblerSetKeyNotFound) + } + } + + /// Return the assembled packet from a packet assembler. + /// This also removes it from the set. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the `key` was not found. + /// - Returns [`Error::PacketAssemblerIncomplete`] when the fragments assembler was empty or not fully assembled. + pub(crate) fn get_assembled_packet(&mut self, key: &K) -> Result<&[u8]> { + if let Some(i) = self.index_buffer.get(key) { + let p = self.packet_buffer[*i as usize].assemble()?; + self.index_buffer.remove(key); + Ok(p) + } else { + Err(Error::PacketAssemblerSetKeyNotFound) + } + } + + /// Remove all [`PacketAssembler`]s that are marked as discared. + pub fn remove_discarded(&mut self) { + loop { + let mut key = None; + for (k, i) in self.index_buffer.iter() { + if self.packet_buffer[*i as usize].assembler == AssemblerState::NotInit { + key = Some(*k); + break; + } + } + + if let Some(k) = key { + self.index_buffer.remove(&k); + } else { + break; + } + } + } + + /// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`. + pub fn remove_when( + &mut self, + f: impl Fn(&mut PacketAssembler<'_, Info>) -> Result, + ) -> Result<()> { + for (_, i) in &mut self.index_buffer.iter() { + let frag = &mut self.packet_buffer[*i as usize]; + if f(frag)? { + frag.mark_discarded(); + } + } + self.remove_discarded(); + + Ok(()) + } +} + +#[cfg(feature = "proto-sixlowpan")] +pub mod sixlowpan { + #[derive(Debug, PartialEq)] + pub struct SixlowpanAssemblerInfo { + header_size: usize, + } + + impl SixlowpanAssemblerInfo { + pub fn new(header_size: usize) -> Self { + SixlowpanAssemblerInfo { header_size } + } + } + + impl super::PacketAssemblerInfo for SixlowpanAssemblerInfo { + #[inline] + fn calc_offset(&self, offset: usize) -> usize { + match offset { + 0 => 0, + offset => offset - self.header_size, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] + struct Key { + id: usize, + } + + #[test] + fn packet_assembler_not_init() { + let mut p_assembler = PacketAssembler::::new(vec![]); + let data = b"Hello World!"; + assert_eq!( + p_assembler.add(&data[..], data.len(), Instant::now()), + Err(Error::PacketAssemblerNotInit) + ); + + assert_eq!( + p_assembler.is_complete(), + Err(Error::PacketAssemblerNotInit) + ); + assert_eq!(p_assembler.assemble(), Err(Error::PacketAssemblerNotInit)); + } + + #[test] + fn packet_assembler_buffer_too_small() { + let mut storage = [0u8; 1]; + let mut p_assembler = PacketAssembler::::new(&mut storage[..]); + + assert_eq!( + p_assembler.start(2, NoInfo, Instant::now()), + Err(Error::PacketAssemblerBufferTooSmall) + ); + assert_eq!(p_assembler.start(1, NoInfo, Instant::now()), Ok(())); + + let data = b"Hello World!"; + assert_eq!( + p_assembler.add(&data[..], data.len(), Instant::now()), + Err(Error::PacketAssemblerBufferTooSmall) + ); + } + + #[test] + fn packet_assembler_overlap() { + let mut storage = [0u8; 5]; + let mut p_assembler = PacketAssembler::new(&mut storage[..]); + + p_assembler.start(5, NoInfo, Instant::now()).unwrap(); + let data = b"Rust"; + + p_assembler.add(&data[..], 0, Instant::now()).unwrap(); + + assert_eq!( + p_assembler.add(&data[..], 1, Instant::now()), + Err(Error::PacketAssemblerOverlap), + ); + } + + #[test] + fn packet_assembler_assemble() { + let mut storage = [0u8; 12]; + let mut p_assembler = PacketAssembler::new(&mut storage[..]); + + let data = b"Hello World!"; + + p_assembler + .start(data.len(), NoInfo, Instant::now()) + .unwrap(); + + p_assembler.add(b"Hello ", 0, Instant::now()).unwrap(); + assert_eq!( + p_assembler.assemble(), + Err(Error::PacketAssemblerIncomplete) + ); + + p_assembler + .add(b"World!", b"Hello ".len(), Instant::now()) + .unwrap(); + + assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..])); + } + + #[test] + fn packet_assembler_set() { + let key = Key { id: 1 }; + + let mut set = + PacketAssemblerSet::<'_, _, NoInfo>::new(vec![], std::collections::BTreeMap::new()); + + if let Err(e) = set.get_packet_assembler_mut(&key) { + assert_eq!(e, Error::PacketAssemblerSetKeyNotFound); + } + + assert!(set.reserve_with_key(&key).is_ok()); + } + + #[test] + fn packet_assembler_set_borrowed() { + let mut buf = [0u8, 127]; + let mut packet_assembler_cache = [PacketAssembler::<'_, NoInfo>::new(&mut buf[..])]; + let mut packet_index_cache = [None]; + + let key = Key { id: 1 }; + + let mut set = + PacketAssemblerSet::new(&mut packet_assembler_cache[..], &mut packet_index_cache[..]); + + if let Err(e) = set.get_packet_assembler_mut(&key) { + assert_eq!(e, Error::PacketAssemblerSetKeyNotFound); + } + + assert!(set.reserve_with_key(&key).is_ok()); + } + + #[test] + fn packet_assembler_set_assembling_many() { + let mut buf = [0u8, 127]; + let mut packet_assembler_cache = [PacketAssembler::new(&mut buf[..])]; + let mut packet_index_cache = [None]; + + let mut set = + PacketAssemblerSet::new(&mut packet_assembler_cache[..], &mut packet_index_cache[..]); + + let key = Key { id: 0 }; + set.reserve_with_key(&key).unwrap(); + set.get_packet_assembler_mut(&key) + .unwrap() + .start(0, NoInfo, Instant::now()) + .unwrap(); + set.get_assembled_packet(&key).unwrap(); + + let key = Key { id: 1 }; + set.reserve_with_key(&key).unwrap(); + set.get_packet_assembler_mut(&key) + .unwrap() + .start(0, NoInfo, Instant::now()) + .unwrap(); + set.get_assembled_packet(&key).unwrap(); + + let key = Key { id: 2 }; + set.reserve_with_key(&key).unwrap(); + set.get_packet_assembler_mut(&key) + .unwrap() + .start(0, NoInfo, Instant::now()) + .unwrap(); + set.get_assembled_packet(&key).unwrap(); + } +} diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 268b6d481..a4a13381d 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -5,6 +5,8 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; +#[cfg(feature = "proto-sixlowpan")] +use super::fragmentation::{sixlowpan::SixlowpanAssemblerInfo, PacketAssemblerSet}; use super::socket_set::SocketSet; use super::{SocketHandle, SocketStorage}; use crate::iface::Routes; diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 11ce835c0..151073e4d 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,6 +4,8 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ +#[cfg(feature = "proto-sixlowpan")] +mod fragmentation; mod interface; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] mod neighbor; @@ -20,4 +22,9 @@ pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; pub use socket_set::{SocketHandle, SocketStorage}; +#[cfg(feature = "proto-sixlowpan")] +pub use self::fragmentation::{ + sixlowpan::SixlowpanAssemblerInfo, PacketAssembler, PacketAssemblerSet as FragmentsCache, +}; + pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; diff --git a/src/lib.rs b/src/lib.rs index f07b0b1f1..8ecaccdee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -186,6 +186,25 @@ pub enum Error { /// An incoming packet was recognized but contradicted internal state. /// E.g. a TCP packet addressed to a socket that doesn't exist. Dropped, + /// An incoming fragment arrived too late. + ReassemblyTimeout, + + /// The packet assembler is not initialized, thus it cannot know what the final size of the + /// packet would be. + PacketAssemblerNotInit, + /// The buffer of the assembler is to small and thus the final packet wont fit into it. + PacketAssemblerBufferTooSmall, + /// The packet assembler did not receive all the fragments for assembling the final packet. + PacketAssemblerIncomplete, + /// There are too many holes in the packet assembler (should be fixed in the future?). + PacketAssemblerTooManyHoles, + /// There was an overlap when adding data to the packet assembler. + PacketAssemblerOverlap, + + /// The packet assembler set has no place for assembling a new stream of fragments. + PacketAssemblerSetFull, + /// The key was not found in the packet assembler set. + PacketAssemblerSetKeyNotFound, /// An incoming packet was recognized but some parts are not supported by smoltcp. /// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC. @@ -211,6 +230,23 @@ impl fmt::Display for Error { Error::Fragmented => write!(f, "fragmented packet"), Error::Malformed => write!(f, "malformed packet"), Error::Dropped => write!(f, "dropped by socket"), + Error::ReassemblyTimeout => write!(f, "incoming fragment arrived too late"), + Error::PacketAssemblerNotInit => write!(f, "packet assembler was not initialized"), + Error::PacketAssemblerBufferTooSmall => { + write!(f, "packet assembler buffer too small for final packet") + } + Error::PacketAssemblerIncomplete => write!(f, "packet assembler incomplete"), + Error::PacketAssemblerTooManyHoles => write!( + f, + "packet assembler has too many holes (internal smoltcp error)" + ), + Error::PacketAssemblerOverlap => { + write!(f, "overlap when adding data to packet assembler") + } + Error::PacketAssemblerSetFull => write!(f, "packet assembler set is full"), + Error::PacketAssemblerSetKeyNotFound => { + write!(f, "packet assembler set does not find key") + } Error::NotSupported => write!(f, "not supported by smoltcp"), } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 94a1f244c..0f9734a41 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -261,7 +261,7 @@ impl Timer { Timer::Idle { .. } | Timer::FastRetransmit { .. } => { *self = Timer::Retransmit { expires_at: timestamp + delay, - delay: delay, + delay, } } Timer::Retransmit { expires_at, delay } if timestamp >= expires_at => { @@ -414,8 +414,8 @@ impl<'a> TcpSocket<'a> { timer: Timer::new(), rtte: RttEstimator::default(), assembler: Assembler::new(rx_buffer.capacity()), - tx_buffer: tx_buffer, - rx_buffer: rx_buffer, + tx_buffer, + rx_buffer, rx_fin_received: false, timeout: None, keep_alive: None, @@ -1829,7 +1829,7 @@ impl<'a> TcpSocket<'a> { // Try adding payload octets to the assembler. match self.assembler.add(payload_offset, payload_len) { - Ok(()) => { + Ok(_) => { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. net_trace!( diff --git a/src/socket/udp.rs b/src/socket/udp.rs index c8ef6cd1e..b2457fbce 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -38,8 +38,8 @@ impl<'a> UdpSocket<'a> { pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { UdpSocket { endpoint: IpEndpoint::default(), - rx_buffer: rx_buffer, - tx_buffer: tx_buffer, + rx_buffer, + tx_buffer, hop_limit: None, #[cfg(feature = "async")] rx_waker: WakerRegistration::new(), diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 316beeda3..3ae8d81ad 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -94,8 +94,7 @@ const CONTIG_COUNT: usize = 4; /// A buffer (re)assembler. /// /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer. -#[derive(Debug)] -#[cfg_attr(test, derive(PartialEq, Eq, Clone))] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Assembler { #[cfg(not(any(feature = "std", feature = "alloc")))] @@ -139,6 +138,16 @@ impl Assembler { self.contigs[0] } + /// Return length of the front contiguous range without removing it from the assembler + pub fn peek_front(&self) -> Option { + let front = self.front(); + if front.has_hole() { + None + } else { + Some(front.data_size) + } + } + fn back(&self) -> Contig { self.contigs[self.contigs.len() - 1] } @@ -182,10 +191,12 @@ impl Assembler { Ok(&mut self.contigs[at]) } - /// Add a new contiguous range to the assembler, and return `Ok(())`, + /// Add a new contiguous range to the assembler, and return `Ok(bool)`, /// or return `Err(())` if too many discontiguities are already recorded. - pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result<(), TooManyHolesError> { + /// Returns `Ok(true)` when there was an overlap. + pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result { let mut index = 0; + let mut overlap = size; while index != self.contigs.len() && size != 0 { let contig = self.contigs[index]; @@ -196,6 +207,7 @@ impl Assembler { // The range being added covers the entire hole in this contig, merge it // into the previous config. self.contigs[index - 1].expand_data_by(contig.total_size()); + overlap -= contig.total_size(); self.remove_contig_at(index); index += 0; } else if offset == 0 && size < contig.hole_size && index > 0 { @@ -204,10 +216,12 @@ impl Assembler { // the previous contig. self.contigs[index - 1].expand_data_by(size); self.contigs[index].shrink_hole_by(size); + overlap -= size; index += 1; } else if offset <= contig.hole_size && offset + size >= contig.hole_size { // The range being added covers both a part of the hole and a part of the data // in this contig, shrink the hole in this contig. + overlap -= contig.hole_size - offset; self.contigs[index].shrink_hole_to(offset); index += 1; } else if offset + size >= contig.hole_size { @@ -219,6 +233,7 @@ impl Assembler { { let inserted = self.add_contig_at(index)?; *inserted = Contig::hole_and_data(offset, size); + overlap -= size; } // Previous contigs[index] got moved to contigs[index+1] self.contigs[index + 1].shrink_hole_by(offset + size); @@ -237,7 +252,7 @@ impl Assembler { } debug_assert!(size == 0); - Ok(()) + Ok(overlap != 0) } /// Remove a contiguous range from the front of the assembler and `Some(data_size)`, @@ -280,8 +295,8 @@ pub struct AssemblerIter<'a> { impl<'a> AssemblerIter<'a> { fn new(assembler: &'a Assembler, offset: usize) -> AssemblerIter<'a> { AssemblerIter { - assembler: assembler, - offset: offset, + assembler, + offset, index: 0, left: 0, right: 0, @@ -348,84 +363,84 @@ mod test { #[test] fn test_empty_add_full() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(())); + assert_eq!(assr.add(0, 16), Ok(false)); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_empty_add_front() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 4), Ok(())); + assert_eq!(assr.add(0, 4), Ok(false)); assert_eq!(assr, contigs![(0, 4), (12, 0)]); } #[test] fn test_empty_add_back() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(12, 4), Ok(())); + assert_eq!(assr.add(12, 4), Ok(false)); assert_eq!(assr, contigs![(12, 4)]); } #[test] fn test_empty_add_mid() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(4, 8), Ok(())); + assert_eq!(assr.add(4, 8), Ok(false)); assert_eq!(assr, contigs![(4, 8), (4, 0)]); } #[test] fn test_partial_add_front() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 4), Ok(())); + assert_eq!(assr.add(0, 4), Ok(false)); assert_eq!(assr, contigs![(0, 12), (4, 0)]); } #[test] fn test_partial_add_back() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(12, 4), Ok(())); + assert_eq!(assr.add(12, 4), Ok(false)); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_front_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 8), Ok(())); + assert_eq!(assr.add(0, 8), Ok(true)); assert_eq!(assr, contigs![(0, 12), (4, 0)]); } #[test] fn test_partial_add_front_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(2, 6), Ok(())); + assert_eq!(assr.add(2, 6), Ok(true)); assert_eq!(assr, contigs![(2, 10), (4, 0)]); } #[test] fn test_partial_add_back_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(8, 8), Ok(())); + assert_eq!(assr.add(8, 8), Ok(true)); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_back_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(10, 4), Ok(())); + assert_eq!(assr.add(10, 4), Ok(true)); assert_eq!(assr, contigs![(4, 10), (2, 0)]); } #[test] fn test_partial_add_both_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 16), Ok(())); + assert_eq!(assr.add(0, 16), Ok(true)); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_partial_add_both_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(2, 12), Ok(())); + assert_eq!(assr.add(2, 12), Ok(true)); assert_eq!(assr, contigs![(2, 12), (2, 0)]); } @@ -433,7 +448,7 @@ mod test { fn test_rejected_add_keeps_state() { let mut assr = Assembler::new(CONTIG_COUNT * 20); for c in 1..=CONTIG_COUNT - 1 { - assert_eq!(assr.add(c * 10, 3), Ok(())); + assert_eq!(assr.add(c * 10, 3), Ok(false)); } // Maximum of allowed holes is reached let assr_before = assr.clone(); @@ -471,7 +486,7 @@ mod test { #[test] fn test_iter_full() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(())); + assert_eq!(assr.add(0, 16), Ok(false)); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 26)]); } @@ -479,7 +494,7 @@ mod test { #[test] fn test_iter_offset() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(())); + assert_eq!(assr.add(0, 16), Ok(false)); let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(100, 116)]); } @@ -487,7 +502,7 @@ mod test { #[test] fn test_iter_one_front() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 4), Ok(())); + assert_eq!(assr.add(0, 4), Ok(false)); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 14)]); } @@ -495,7 +510,7 @@ mod test { #[test] fn test_iter_one_back() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(12, 4), Ok(())); + assert_eq!(assr.add(12, 4), Ok(false)); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(22, 26)]); } @@ -503,7 +518,7 @@ mod test { #[test] fn test_iter_one_mid() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(4, 8), Ok(())); + assert_eq!(assr.add(4, 8), Ok(false)); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(14, 22)]); } From 3a1e84e78105279c38eb92172ce466fc5345d3c4 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 22 Feb 2022 12:14:29 +0100 Subject: [PATCH 294/566] allow temporary unused --- src/iface/fragmentation.rs | 2 ++ src/iface/interface.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index eac7f3d77..0175cdf05 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + use managed::{ManagedMap, ManagedSlice}; use crate::storage::Assembler; diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a4a13381d..377ec2b20 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -5,6 +5,7 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; +#[allow(unused)] #[cfg(feature = "proto-sixlowpan")] use super::fragmentation::{sixlowpan::SixlowpanAssemblerInfo, PacketAssemblerSet}; use super::socket_set::SocketSet; From ea1bd3fc8eb4928111daa7f7b14b34303770d5a8 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 10:24:29 +0100 Subject: [PATCH 295/566] remove duplicate line --- src/iface/fragmentation.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 0175cdf05..2ab24df13 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -154,7 +154,6 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { } } }; - self.assembler = AssemblerState::NotInit; Ok(b) } From 6f9dca3ef9a2b99f9c312bb01e8be1ff74549556 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 10:24:41 +0100 Subject: [PATCH 296/566] limit the WIP reassemblies --- src/iface/fragmentation.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 2ab24df13..1d1885779 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -221,7 +221,7 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> { packet_buffer: ManagedSlice<'a, PacketAssembler<'a, Info>>, - index_buffer: ManagedMap<'a, Key, u8>, + index_buffer: ManagedMap<'a, Key, usize>, } impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerSet<'a, K, Info> { @@ -237,7 +237,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS pub fn new(packet_buffer: FB, index_buffer: IB) -> Self where FB: Into>>, - IB: Into>, + IB: Into>, { let packet_buffer = packet_buffer.into(); let index_buffer = index_buffer.into(); @@ -278,15 +278,17 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS /// - Returns [`Error::PacketAssemblerSetFull`] when every [`PacketAssembler`] in the buffer is used (only /// when the non allocating version of is used). pub(crate) fn reserve_with_key(&mut self, key: &K) -> Result<&mut PacketAssembler<'a, Info>> { + // Check how many WIP reassemblies we have. + // The limit is currently set to 255. + if self.index_buffer.len() == u8::MAX as usize { + return Err(Error::PacketAssemblerSetFull); + } + if self.packet_buffer.len() == self.index_buffer.len() { match &mut self.packet_buffer { ManagedSlice::Borrowed(_) => return Err(Error::PacketAssemblerSetFull), #[cfg(any(feature = "std", feature = "alloc"))] - ManagedSlice::Owned(b) => { - b.resize_with(self.index_buffer.len() + 1, || { - PacketAssembler::new(Vec::new()) - }); - } + ManagedSlice::Owned(b) => (), } } @@ -295,7 +297,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS .ok_or(Error::PacketAssemblerSetFull)?; // NOTE(thvdveld): this should not fail because we already checked the available space. - match self.index_buffer.insert(*key, i as u8) { + match self.index_buffer.insert(*key, i) { Ok(_) => Ok(&mut self.packet_buffer[i]), Err(_) => unreachable!(), } From f298ac6db46763f8ad7a88ecb3a85a928bda43c0 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 10:32:53 +0100 Subject: [PATCH 297/566] don't error when there was an overlap (just log it) --- src/iface/fragmentation.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 1d1885779..3725c5232 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -121,11 +121,13 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { self.buffer[offset..][..len].copy_from_slice(data); match assembler.add(offset, data.len()) { - Ok(false) => { + Ok(overlap) => { + if overlap { + net_debug!("packet was added, but there was an overlap."); + } *last_updated = now; self.is_complete() } - Ok(true) => Err(Error::PacketAssemblerOverlap), // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? Err(_) => Err(Error::PacketAssemblerTooManyHoles), } From bd3ea4710323e617385ec4b4fb8169e5272cfdfb Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 10:51:15 +0100 Subject: [PATCH 298/566] fix reserve with key for owned cache --- src/iface/fragmentation.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 3725c5232..411cb7efb 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -306,7 +306,13 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS } /// Return the first free packet assembler available from the cache. - fn get_free_packet_assembler(&self) -> Option { + fn get_free_packet_assembler(&mut self) -> Option { + match &mut self.packet_buffer { + ManagedSlice::Borrowed(_) => (), + #[cfg(any(feature = "std", feature = "alloc"))] + ManagedSlice::Owned(b) => b.push(PacketAssembler::new(vec![])), + } + self.packet_buffer .iter() .enumerate() @@ -462,7 +468,7 @@ mod tests { assert_eq!( p_assembler.add(&data[..], 1, Instant::now()), - Err(Error::PacketAssemblerOverlap), + Ok(true), ); } From def33ce4ee51c852823cd4000d48377d02d3d72a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 10:57:16 +0100 Subject: [PATCH 299/566] cargo fmt --- src/iface/fragmentation.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 411cb7efb..fc860be83 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -466,10 +466,7 @@ mod tests { p_assembler.add(&data[..], 0, Instant::now()).unwrap(); - assert_eq!( - p_assembler.add(&data[..], 1, Instant::now()), - Ok(true), - ); + assert_eq!(p_assembler.add(&data[..], 1, Instant::now()), Ok(true)); } #[test] From 5dce1768f5a42f167fb0350f2dbc813172883388 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 21 Mar 2022 11:19:31 +0100 Subject: [PATCH 300/566] remove `PacketAssemblerInfo` --- src/iface/fragmentation.rs | 101 +++++++++---------------------------- src/iface/interface.rs | 2 +- src/iface/mod.rs | 4 +- 3 files changed, 25 insertions(+), 82 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index fc860be83..19b59610f 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -7,44 +7,28 @@ use crate::time::Instant; use crate::Error; use crate::Result; -pub trait PacketAssemblerInfo: PartialEq { - /// Calculate a new offset based on some other information. - fn calc_offset(&self, offset: usize) -> usize; -} - -#[derive(Debug, PartialEq)] -pub struct NoInfo; - -impl PacketAssemblerInfo for NoInfo { - #[inline] - fn calc_offset(&self, offset: usize) -> usize { - offset - } -} - /// Holds different fragments of one packet, used for assembling fragmented packets. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PacketAssembler<'a, Info: PacketAssemblerInfo = NoInfo> { +pub struct PacketAssembler<'a> { buffer: ManagedSlice<'a, u8>, - assembler: AssemblerState, + assembler: AssemblerState, } /// Holds the state of the assembling of one packet. #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum AssemblerState { +enum AssemblerState { NotInit, Assembling { assembler: Assembler, total_size: usize, - info: Info, last_updated: Instant, started_on: Instant, }, } -impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { +impl<'a> PacketAssembler<'a> { /// Create a new empty buffer for fragments. pub fn new(storage: S) -> Self where @@ -64,12 +48,7 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { /// /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too smal for holding all the /// fragments of a packet. - pub(crate) fn start( - &mut self, - total_size: usize, - info: Info, - start_time: Instant, - ) -> Result<()> { + pub(crate) fn start(&mut self, total_size: usize, start_time: Instant) -> Result<()> { match &mut self.buffer { ManagedSlice::Borrowed(b) if b.len() < total_size => { return Err(Error::PacketAssemblerBufferTooSmall); @@ -84,7 +63,6 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { self.assembler = AssemblerState::Assembling { assembler: Assembler::new(total_size), total_size, - info, last_updated: start_time, started_on: start_time, }; @@ -107,12 +85,9 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { AssemblerState::Assembling { ref mut assembler, total_size, - ref info, ref mut last_updated, .. } => { - let offset = info.calc_offset(offset); - if offset + data.len() > total_size { return Err(Error::PacketAssemblerBufferTooSmall); } @@ -221,12 +196,12 @@ impl<'a, Info: PacketAssemblerInfo> PacketAssembler<'a, Info> { /// Set holding multiple [`PacketAssembler`]. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> { - packet_buffer: ManagedSlice<'a, PacketAssembler<'a, Info>>, +pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy> { + packet_buffer: ManagedSlice<'a, PacketAssembler<'a>>, index_buffer: ManagedMap<'a, Key, usize>, } -impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerSet<'a, K, Info> { +impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { /// Create a new set of packet assemblers. /// /// # Panics @@ -238,7 +213,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS /// - The index buffer is empty (when only the index buffer is borrowed). pub fn new(packet_buffer: FB, index_buffer: IB) -> Self where - FB: Into>>, + FB: Into>>, IB: Into>, { let packet_buffer = packet_buffer.into(); @@ -279,7 +254,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS /// /// - Returns [`Error::PacketAssemblerSetFull`] when every [`PacketAssembler`] in the buffer is used (only /// when the non allocating version of is used). - pub(crate) fn reserve_with_key(&mut self, key: &K) -> Result<&mut PacketAssembler<'a, Info>> { + pub(crate) fn reserve_with_key(&mut self, key: &K) -> Result<&mut PacketAssembler<'a>> { // Check how many WIP reassemblies we have. // The limit is currently set to 255. if self.index_buffer.len() == u8::MAX as usize { @@ -325,10 +300,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS /// # Errors /// /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the key was not found in the set. - pub(crate) fn get_packet_assembler_mut( - &mut self, - key: &K, - ) -> Result<&mut PacketAssembler<'a, Info>> { + pub(crate) fn get_packet_assembler_mut(&mut self, key: &K) -> Result<&mut PacketAssembler<'a>> { if let Some(i) = self.index_buffer.get(key) { Ok(&mut self.packet_buffer[*i as usize]) } else { @@ -375,7 +347,7 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS /// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`. pub fn remove_when( &mut self, - f: impl Fn(&mut PacketAssembler<'_, Info>) -> Result, + f: impl Fn(&mut PacketAssembler<'_>) -> Result, ) -> Result<()> { for (_, i) in &mut self.index_buffer.iter() { let frag = &mut self.packet_buffer[*i as usize]; @@ -389,30 +361,6 @@ impl<'a, K: Eq + Ord + Clone + Copy, Info: PacketAssemblerInfo> PacketAssemblerS } } -#[cfg(feature = "proto-sixlowpan")] -pub mod sixlowpan { - #[derive(Debug, PartialEq)] - pub struct SixlowpanAssemblerInfo { - header_size: usize, - } - - impl SixlowpanAssemblerInfo { - pub fn new(header_size: usize) -> Self { - SixlowpanAssemblerInfo { header_size } - } - } - - impl super::PacketAssemblerInfo for SixlowpanAssemblerInfo { - #[inline] - fn calc_offset(&self, offset: usize) -> usize { - match offset { - 0 => 0, - offset => offset - self.header_size, - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -424,7 +372,7 @@ mod tests { #[test] fn packet_assembler_not_init() { - let mut p_assembler = PacketAssembler::::new(vec![]); + let mut p_assembler = PacketAssembler::new(vec![]); let data = b"Hello World!"; assert_eq!( p_assembler.add(&data[..], data.len(), Instant::now()), @@ -441,13 +389,13 @@ mod tests { #[test] fn packet_assembler_buffer_too_small() { let mut storage = [0u8; 1]; - let mut p_assembler = PacketAssembler::::new(&mut storage[..]); + let mut p_assembler = PacketAssembler::new(&mut storage[..]); assert_eq!( - p_assembler.start(2, NoInfo, Instant::now()), + p_assembler.start(2, Instant::now()), Err(Error::PacketAssemblerBufferTooSmall) ); - assert_eq!(p_assembler.start(1, NoInfo, Instant::now()), Ok(())); + assert_eq!(p_assembler.start(1, Instant::now()), Ok(())); let data = b"Hello World!"; assert_eq!( @@ -461,7 +409,7 @@ mod tests { let mut storage = [0u8; 5]; let mut p_assembler = PacketAssembler::new(&mut storage[..]); - p_assembler.start(5, NoInfo, Instant::now()).unwrap(); + p_assembler.start(5, Instant::now()).unwrap(); let data = b"Rust"; p_assembler.add(&data[..], 0, Instant::now()).unwrap(); @@ -476,9 +424,7 @@ mod tests { let data = b"Hello World!"; - p_assembler - .start(data.len(), NoInfo, Instant::now()) - .unwrap(); + p_assembler.start(data.len(), Instant::now()).unwrap(); p_assembler.add(b"Hello ", 0, Instant::now()).unwrap(); assert_eq!( @@ -497,8 +443,7 @@ mod tests { fn packet_assembler_set() { let key = Key { id: 1 }; - let mut set = - PacketAssemblerSet::<'_, _, NoInfo>::new(vec![], std::collections::BTreeMap::new()); + let mut set = PacketAssemblerSet::<'_, _>::new(vec![], std::collections::BTreeMap::new()); if let Err(e) = set.get_packet_assembler_mut(&key) { assert_eq!(e, Error::PacketAssemblerSetKeyNotFound); @@ -510,7 +455,7 @@ mod tests { #[test] fn packet_assembler_set_borrowed() { let mut buf = [0u8, 127]; - let mut packet_assembler_cache = [PacketAssembler::<'_, NoInfo>::new(&mut buf[..])]; + let mut packet_assembler_cache = [PacketAssembler::<'_>::new(&mut buf[..])]; let mut packet_index_cache = [None]; let key = Key { id: 1 }; @@ -538,7 +483,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, NoInfo, Instant::now()) + .start(0, Instant::now()) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -546,7 +491,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, NoInfo, Instant::now()) + .start(0, Instant::now()) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -554,7 +499,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, NoInfo, Instant::now()) + .start(0, Instant::now()) .unwrap(); set.get_assembled_packet(&key).unwrap(); } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 377ec2b20..5dd1dd07b 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -7,7 +7,7 @@ use managed::{ManagedMap, ManagedSlice}; #[allow(unused)] #[cfg(feature = "proto-sixlowpan")] -use super::fragmentation::{sixlowpan::SixlowpanAssemblerInfo, PacketAssemblerSet}; +use super::fragmentation::PacketAssemblerSet; use super::socket_set::SocketSet; use super::{SocketHandle, SocketStorage}; use crate::iface::Routes; diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 151073e4d..62be2d475 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -23,8 +23,6 @@ pub use self::route::{Route, Routes}; pub use socket_set::{SocketHandle, SocketStorage}; #[cfg(feature = "proto-sixlowpan")] -pub use self::fragmentation::{ - sixlowpan::SixlowpanAssemblerInfo, PacketAssembler, PacketAssemblerSet as FragmentsCache, -}; +pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as FragmentsCache}; pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; From f290eda70f9c6ddab3b14edc1a81ce5132a51ef5 Mon Sep 17 00:00:00 2001 From: Bryan Benson Date: Mon, 4 Apr 2022 19:41:56 -0700 Subject: [PATCH 301/566] Correct net_trace! bug in UdpSocket dispatch where the local endpoint was used instead of endpoint. Also changed other endpoint naming to add clarity that it is the remote endpoint. --- src/socket/udp.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/socket/udp.rs b/src/socket/udp.rs index b2457fbce..6f06090e2 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -207,20 +207,20 @@ impl<'a> UdpSocket<'a> { /// `Err(Error::Unaddressable)` if local or remote port, or remote address are unspecified, /// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity /// to ever send this packet. - pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> { + pub fn send(&mut self, size: usize, remote_endpoint: IpEndpoint) -> Result<&mut [u8]> { if self.endpoint.port == 0 { return Err(Error::Unaddressable); } - if !endpoint.is_specified() { + if !remote_endpoint.is_specified() { return Err(Error::Unaddressable); } - let payload_buf = self.tx_buffer.enqueue(size, endpoint)?; + let payload_buf = self.tx_buffer.enqueue(size, remote_endpoint)?; net_trace!( "udp:{}:{}: buffer to send {} octets", self.endpoint, - endpoint, + remote_endpoint, size ); Ok(payload_buf) @@ -229,8 +229,8 @@ impl<'a> UdpSocket<'a> { /// Enqueue a packet to be sent to a given remote endpoint, and fill it from a slice. /// /// See also [send](#method.send). - pub fn send_slice(&mut self, data: &[u8], endpoint: IpEndpoint) -> Result<()> { - self.send(data.len(), endpoint)?.copy_from_slice(data); + pub fn send_slice(&mut self, data: &[u8], remote_endpoint: IpEndpoint) -> Result<()> { + self.send(data.len(), remote_endpoint)?.copy_from_slice(data); Ok(()) } @@ -239,15 +239,15 @@ impl<'a> UdpSocket<'a> { /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> { - let (endpoint, payload_buf) = self.rx_buffer.dequeue()?; + let (remote_endpoint, payload_buf) = self.rx_buffer.dequeue()?; net_trace!( "udp:{}:{}: receive {} buffered octets", self.endpoint, - endpoint, + remote_endpoint, payload_buf.len() ); - Ok((payload_buf, endpoint)) + Ok((payload_buf, remote_endpoint)) } /// Dequeue a packet received from a remote endpoint, copy the payload into the given slice, @@ -318,18 +318,18 @@ impl<'a> UdpSocket<'a> { let size = payload.len(); - let endpoint = IpEndpoint { + let remote_endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port, }; self.rx_buffer - .enqueue(size, endpoint)? + .enqueue(size, remote_endpoint)? .copy_from_slice(payload); net_trace!( "udp:{}:{}: receiving {} octets", self.endpoint, - endpoint, + remote_endpoint, size ); @@ -351,7 +351,7 @@ impl<'a> UdpSocket<'a> { net_trace!( "udp:{}:{}: sending {} octets", endpoint, - endpoint, + remote_endpoint, payload_buf.len() ); From 8f5e400a868917b6fafda6a3a08efeb89baa0ba4 Mon Sep 17 00:00:00 2001 From: Bryan Benson Date: Mon, 4 Apr 2022 19:56:07 -0700 Subject: [PATCH 302/566] Fix rustfmt - will squash. --- src/socket/udp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 6f06090e2..6bb1322d6 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -230,7 +230,8 @@ impl<'a> UdpSocket<'a> { /// /// See also [send](#method.send). pub fn send_slice(&mut self, data: &[u8], remote_endpoint: IpEndpoint) -> Result<()> { - self.send(data.len(), remote_endpoint)?.copy_from_slice(data); + self.send(data.len(), remote_endpoint)? + .copy_from_slice(data); Ok(()) } From d2f66d619ac0db41d140ab19b6f201fa4f4c2d67 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Wed, 20 Apr 2022 18:42:50 +0200 Subject: [PATCH 303/566] tcp socket: keep settings on reset, fix #601 --- src/socket/tcp.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 0f9734a41..fe6c8ae6d 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -639,9 +639,6 @@ impl<'a> TcpSocket<'a> { self.tx_buffer.clear(); self.rx_buffer.clear(); self.rx_fin_received = false; - self.keep_alive = None; - self.timeout = None; - self.hop_limit = None; self.listen_address = IpAddress::default(); self.local_endpoint = IpEndpoint::default(); self.remote_endpoint = IpEndpoint::default(); @@ -655,12 +652,9 @@ impl<'a> TcpSocket<'a> { self.remote_win_shift = rx_cap_log2.saturating_sub(16) as u8; self.remote_mss = DEFAULT_MSS; self.remote_last_ts = None; - self.ack_delay = Some(ACK_DELAY_DEFAULT); self.ack_delay_timer = AckDelayTimer::Idle; self.challenge_ack_timer = Instant::from_secs(0); - self.nagle = true; - #[cfg(feature = "async")] { self.rx_waker.wake(); @@ -6360,6 +6354,11 @@ mod test { }), Ok(()) ); + + // assert that user-configurable settings are kept, + // see https://github.com/smoltcp-rs/smoltcp/issues/601. + s.reset(); + assert_eq!(s.hop_limit(), Some(0x2a)); } #[test] From 8ba88303784c0ef138da89899d12e2c11ec84151 Mon Sep 17 00:00:00 2001 From: Anuvrat Date: Sun, 24 Apr 2022 13:10:01 +0530 Subject: [PATCH 304/566] Corrects minor spelling errors --- README.md | 2 +- fuzz/fuzz_targets/ieee802154_header.rs | 2 +- src/iface/fragmentation.rs | 4 ++-- src/iface/interface.rs | 6 ++--- src/lib.rs | 2 +- src/socket/icmp.rs | 2 +- src/socket/mod.rs | 2 +- src/socket/raw.rs | 4 ++-- src/socket/tcp.rs | 32 +++++++++++++------------- src/storage/packet_buffer.rs | 2 +- src/time.rs | 4 ++-- src/wire/ieee802154.rs | 2 +- src/wire/ipv6.rs | 4 ++-- src/wire/ipv6fragment.rs | 2 +- src/wire/ipv6routing.rs | 14 +++++------ src/wire/mld.rs | 4 ++-- src/wire/ndiscoption.rs | 2 +- src/wire/sixlowpan.rs | 12 +++++----- src/wire/tcp.rs | 2 +- 19 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 55eb0cbe0..e105cfb47 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ _smoltcp_, being a freestanding networking stack, needs to be able to transmit a raw frames. For testing purposes, we will use a regular OS, and run _smoltcp_ in a userspace process. Only Linux is supported (right now). -On \*nix OSes, transmiting and receiving raw frames normally requires superuser privileges, but +On \*nix OSes, transmitting and receiving raw frames normally requires superuser privileges, but on Linux it is possible to create a _persistent tap interface_ that can be manipulated by a specific user: diff --git a/fuzz/fuzz_targets/ieee802154_header.rs b/fuzz/fuzz_targets/ieee802154_header.rs index af54f88fd..c50839122 100644 --- a/fuzz/fuzz_targets/ieee802154_header.rs +++ b/fuzz/fuzz_targets/ieee802154_header.rs @@ -5,7 +5,7 @@ use smoltcp::wire::{Ieee802154Frame, Ieee802154Repr}; fuzz_target!(|data: &[u8]| { if let Ok(ref frame) = Ieee802154Frame::new_checked(data) { if let Ok(repr) = Ieee802154Repr::parse(frame) { - // The buffer len returns only the lenght required for emitting the header + // The buffer len returns only the length required for emitting the header // and does not take into account the length of the payload. let mut buffer = vec![0; repr.buffer_len()]; diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 19b59610f..512d7195a 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -46,7 +46,7 @@ impl<'a> PacketAssembler<'a> { /// /// # Errors /// - /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too smal for holding all the + /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too small for holding all the /// fragments of a packet. pub(crate) fn start(&mut self, total_size: usize, start_time: Instant) -> Result<()> { match &mut self.buffer { @@ -325,7 +325,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { } } - /// Remove all [`PacketAssembler`]s that are marked as discared. + /// Remove all [`PacketAssembler`]s that are marked as discarded. pub fn remove_discarded(&mut self) { loop { let mut key = None; diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 5dd1dd07b..7eac70e11 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -33,7 +33,7 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// The device independent part of an Ethernet network interface. /// -/// Separating the device from the data required for prorcessing and dispatching makes +/// Separating the device from the data required for processing and dispatching makes /// it possible to borrow them independently. For example, the tx and rx tokens borrow /// the `device` mutably until they're used, which makes it impossible to call other /// methods on the `Interface` in this time (since its `device` field is borrowed @@ -472,7 +472,7 @@ fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize { // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for // more details. // - // Since the entire network layer packet must fit within the minumum + // Since the entire network layer packet must fit within the minimum // MTU supported, the payload must not exceed the following: // // - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size @@ -950,7 +950,7 @@ where // `NeighborCache` already takes care of rate limiting the neighbor discovery // requests from the socket. However, without an additional rate limiting // mechanism, we would spin on every socket that has yet to discover its - // neighboor. + // neighbor. item.meta.neighbor_missing( inner.now, neighbor_addr.expect("non-IP response packet"), diff --git a/src/lib.rs b/src/lib.rs index 8ecaccdee..cce09e55f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! Unlike the higher layers, the wire layer APIs will not be used by a typical application. //! They however are the bedrock of _smoltcp_, and everything else is built on top of them. //! -//! The wire layer APIs are designed by the principle "make illegal states irrepresentable". +//! The wire layer APIs are designed by the principle "make illegal states ir-representable". //! If a wire layer object can be constructed, then it can also be parsed from or emitted to //! a lower level. //! diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 6be720c19..f8862efde 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -54,7 +54,7 @@ pub type IcmpSocketBuffer<'a> = PacketBuffer<'a, IpAddress>; /// A ICMP socket /// /// An ICMP socket is bound to a specific [IcmpEndpoint] which may -/// be a sepecific UDP port to listen for ICMP error messages related +/// be a specific UDP port to listen for ICMP error messages related /// to the port or a specific ICMP identifier value. See [bind] for /// more details. /// diff --git a/src/socket/mod.rs b/src/socket/mod.rs index a98139494..d8415c0e3 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -46,7 +46,7 @@ pub(crate) use self::waker::WakerRegistration; #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) enum PollAt { - /// The socket needs to be polled immidiately. + /// The socket needs to be polled immediately. Now, /// The socket needs to be polled at given [Instant][struct.Instant]. Time(Instant), diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 7943a8083..9319c94af 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -151,7 +151,7 @@ impl<'a> RawSocket<'a> { /// If the buffer is filled in a way that does not match the socket's /// IP version or protocol, the packet will be silently dropped. /// - /// **Note:** The IP header is parsed and reserialized, and may not match + /// **Note:** The IP header is parsed and re-serialized, and may not match /// the header actually transmitted bit for bit. pub fn send(&mut self, size: usize) -> Result<&mut [u8]> { let packet_buf = self.tx_buffer.enqueue(size, ())?; @@ -177,7 +177,7 @@ impl<'a> RawSocket<'a> { /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. /// - /// **Note:** The IP header is parsed and reserialized, and may not match + /// **Note:** The IP header is parsed and re-serialized, and may not match /// the header actually received bit for bit. pub fn recv(&mut self) -> Result<&[u8]> { let ((), packet_buf) = self.rx_buffer.dequeue()?; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index fe6c8ae6d..c7950d2d7 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -362,11 +362,11 @@ pub struct TcpSocket<'a> { remote_mss: usize, /// The timestamp of the last packet received. remote_last_ts: Option, - /// The sequence number of the last packet recived, used for sACK + /// The sequence number of the last packet received, used for sACK local_rx_last_seq: Option, - /// The ACK number of the last packet recived. + /// The ACK number of the last packet received. local_rx_last_ack: Option, - /// The number of packets recived directly after + /// The number of packets received directly after /// each other which have the same ACK number. local_rx_dup_acks: u8, @@ -1198,7 +1198,7 @@ impl<'a> TcpSocket<'a> { // number has advanced, or there was no previous sACK. // // While the RFC says we SHOULD keep a list of reported sACK ranges, and iterate - // through those, that is currently infeasable. Instead, we offer the range with + // through those, that is currently infeasible. Instead, we offer the range with // the lowest sequence number (if one exists) to hint at what segments would // most quickly advance the acknowledgement number. reply_repr.sack_ranges[0] = self @@ -1279,7 +1279,7 @@ impl<'a> TcpSocket<'a> { State::SynSent | State::SynReceived => (true, false), // In FIN-WAIT-1, LAST-ACK, or CLOSING, we've just sent a FIN. State::FinWait1 | State::LastAck | State::Closing => (false, true), - // In all other states we've already got acknowledgemetns for + // In all other states we've already got acknowledgements for // all of the control flags we sent. _ => (false, false), }; @@ -1485,7 +1485,7 @@ impl<'a> TcpSocket<'a> { if repr.control != TcpControl::Rst { if let Some(ack_number) = repr.ack_number { // Sequence number corresponding to the first byte in `tx_buffer`. - // This normally equals `local_seq_no`, but is 1 higher if we ahve sent a SYN, + // This normally equals `local_seq_no`, but is 1 higher if we have sent a SYN, // as the SYN occupies 1 sequence number "before" the data. let tx_buffer_start_seq = self.local_seq_no + (sent_syn as usize); @@ -1752,11 +1752,11 @@ impl<'a> TcpSocket<'a> { // Detect and react to duplicate ACKs by: // 1. Check if duplicate ACK and change self.local_rx_dup_acks accordingly - // 2. If exactly 3 duplicate ACKs recived, set for fast retransmit + // 2. If exactly 3 duplicate ACKs received, set for fast retransmit // 3. Update the last received ACK (self.local_rx_last_ack) match self.local_rx_last_ack { // Duplicate ACK if payload empty and ACK doesn't move send window -> - // Increment duplicate ACK count and set for retransmit if we just recived + // Increment duplicate ACK count and set for retransmit if we just received // the third duplicate ACK Some(ref last_rx_ack) if repr.payload.is_empty() @@ -1788,7 +1788,7 @@ impl<'a> TcpSocket<'a> { ); } } - // No duplicate ACK -> Reset state and update last recived ACK + // No duplicate ACK -> Reset state and update last received ACK _ => { if self.local_rx_dup_acks > 0 { self.local_rx_dup_acks = 0; @@ -5385,7 +5385,7 @@ mod test { }); // Send a long string of text divided into several packets - // because of previously recieved "window_len" + // because of previously received "window_len" s.send_slice(b"xxxxxxyyyyyywwwwwwzzzzzz").unwrap(); // This packet is lost recv!(s, time 1000, Ok(TcpRepr { @@ -5467,7 +5467,7 @@ mod test { _ => false, }); - // ACK all recived segments + // ACK all received segments send!(s, time 1120, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1 + (6 * 4)), @@ -5487,7 +5487,7 @@ mod test { ..RECV_TEMPL })); - // Normal ACK of previously recieved segment + // Normal ACK of previously received segment send!( s, TcpRepr { @@ -5541,7 +5541,7 @@ mod test { assert_eq!( s.local_rx_dup_acks, 0, - "duplicate ACK counter is not reset when reciving data" + "duplicate ACK counter is not reset when receiving data" ); } @@ -5550,7 +5550,7 @@ mod test { let mut s = socket_established(); s.remote_mss = 6; - // Normal ACK of previously recived segment + // Normal ACK of previously received segment send!(s, time 0, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1), @@ -5620,10 +5620,10 @@ mod test { assert_eq!( s.local_rx_dup_acks, 0, - "duplicate ACK counter is not reset when reciving ACK which updates send window" + "duplicate ACK counter is not reset when receiving ACK which updates send window" ); - // ACK all recived segments + // ACK all received segments send!(s, time 1120, TcpRepr { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1 + (6 * 4)), diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 4a5080bdf..9653455bb 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -180,7 +180,7 @@ impl<'a, H> PacketBuffer<'a, H> { } /// Peek at a single packet from the buffer without removing it, and return a reference to - /// its payload as well as its header, or return `Err(Error:Exhaused)` if the buffer is empty. + /// its payload as well as its header, or return `Err(Error:Exhausted)` if the buffer is empty. /// /// This function otherwise behaves identically to [dequeue](#method.dequeue). pub fn peek(&mut self) -> Result<(&H, &[u8])> { diff --git a/src/time.rs b/src/time.rs index e0fb05f50..6a9961bdd 100644 --- a/src/time.rs +++ b/src/time.rs @@ -4,7 +4,7 @@ The `time` module contains structures used to represent both absolute and relative time. - [Instant] is used to represent absolute time. - - [Duration] is used to represet relative time. + - [Duration] is used to represent relative time. [Instant]: struct.Instant.html [Duration]: struct.Duration.html @@ -177,7 +177,7 @@ pub struct Duration { impl Duration { pub const ZERO: Duration = Duration::from_micros(0); - /// Create a new `Duration` from a number of microeconds. + /// Create a new `Duration` from a number of microseconds. pub const fn from_micros(micros: u64) -> Duration { Duration { micros } } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 2793a0951..04c30ef39 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -479,7 +479,7 @@ impl> Frame { index } - /// Return the lenght of the key identifier field. + /// Return the length of the key identifier field. fn key_identifier_length(&self) -> Option { Some(match self.key_identifier_mode() { 0 => 0, diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index b0f8cfb2a..bed4fb301 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -158,7 +158,7 @@ impl Address { } } - /// Helper function used to mask an addres given a prefix. + /// Helper function used to mask an address given a prefix. /// /// # Panics /// This function panics if `mask` is greater than 128. @@ -245,7 +245,7 @@ impl fmt::Display for Address { State::Tail } // Continue iterating without writing any characters until - // we hit anothing non-zero value. + // we hit a non-zero value. (0, &State::Tail) => State::Tail, // When the state is Head or Tail write a u16 in hexadecimal // without the leading colon if the value is not 0. diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index 24dc97110..690348e51 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -166,7 +166,7 @@ pub struct Repr { /// The offset of the data following this header, relative to the start of the Fragmentable /// Part of the original packet. pub frag_offset: u16, - /// Whethere are not there are more fragments following this header + /// When there are more fragments following this header pub more_frags: bool, /// The identification for every packet that is fragmented. pub ident: u32, diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index fdb155ada..8914a1dad 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -225,7 +225,7 @@ impl> Header { /// Getter methods for the RPL Source Routing Header routing type. impl> Header { - /// Return the number of prefix octects elided from addresses[1..n-1]. + /// Return the number of prefix octets elided from addresses[1..n-1]. /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -234,7 +234,7 @@ impl> Header { data[field::CMPR] >> 4 } - /// Return the number of prefix octects elided from the last address (`addresses[n]`). + /// Return the number of prefix octets elided from the last address (`addresses[n]`). /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -243,7 +243,7 @@ impl> Header { data[field::CMPR] & 0xf } - /// Return the number of octects used for padding after `addresses[n]`. + /// Return the number of octets used for padding after `addresses[n]`. /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -334,7 +334,7 @@ impl + AsMut<[u8]>> Header { /// Setter methods for the RPL Source Routing Header routing type. impl + AsMut<[u8]>> Header { - /// Set the number of prefix octects elided from addresses[1..n-1]. + /// Set the number of prefix octets elided from addresses[1..n-1]. /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -344,7 +344,7 @@ impl + AsMut<[u8]>> Header { data[field::CMPR] = raw; } - /// Set the number of prefix octects elided from the last address (`addresses[n]`). + /// Set the number of prefix octets elided from the last address (`addresses[n]`). /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -354,7 +354,7 @@ impl + AsMut<[u8]>> Header { data[field::CMPR] = raw; } - /// Set the number of octects used for padding after `addresses[n]`. + /// Set the number of octets used for padding after `addresses[n]`. /// /// # Panics /// This function may panic if this header is not the RPL Source Routing Header routing type. @@ -600,7 +600,7 @@ mod test { Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..3]).check_len() ); - // less than specfied length field + // less than specified length field assert_eq!( Err(Error::Truncated), Header::new(&BYTES_TYPE2[..23]).check_len() diff --git a/src/wire/mld.rs b/src/wire/mld.rs index c77e1dae7..83125b6d4 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -216,7 +216,7 @@ impl> AddressRecord { RecordType::from(data[field::RECORD_TYPE]) } - /// Return the length of the auxilary data. + /// Return the length of the auxiliary data. #[inline] pub fn aux_data_len(&self) -> u8 { let data = self.buffer.as_ref(); @@ -259,7 +259,7 @@ impl + AsRef<[u8]>> AddressRecord { data[field::RECORD_TYPE] = rty.into(); } - /// Return the length of the auxilary data. + /// Return the length of the auxiliary data. #[inline] pub fn set_aux_data_len(&mut self, len: u8) { let data = self.buffer.as_mut(); diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 5c598b31c..e29abb7e4 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -70,7 +70,7 @@ mod field { // 8-bit identifier of the type of option. pub const TYPE: usize = 0; - // 8-bit unsigned integer. Length of the option, in units of 8 octests. + // 8-bit unsigned integer. Length of the option, in units of 8 octets. pub const LENGTH: usize = 1; // Minimum length of an option. pub const MIN_OPT_LEN: usize = 8; diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index ae605f743..af9a47767 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -814,7 +814,7 @@ pub mod iphc { _ => 1, }; - // Add the lenght of the source address + // Add the length of the source address len += if self.src_addr == ipv6::Address::UNSPECIFIED { 0 } else if self.src_addr.is_link_local() { @@ -897,7 +897,7 @@ pub mod iphc { packet.set_dispatch_field(); - // SETTING THE TRAFIC FLOW + // SETTING THE TRAFFIC FLOW // TODO(thvdveld): needs more work. packet.set_tf_field(0b11); @@ -1258,7 +1258,7 @@ pub mod nhc { len } - /// Emit a high-level representaiton into a LOWPAN_NHC Extension Header packet. + /// Emit a high-level representation into a LOWPAN_NHC Extension Header packet. pub fn emit + AsMut<[u8]>>(&self, packet: &mut ExtensionHeaderPacket) { packet.set_dispatch_field(); packet.set_extension_header_id(self.ext_header_id); @@ -1394,7 +1394,7 @@ pub mod nhc { let start = self.nhc_fields_start() + self.ports_size(); Some(NetworkEndian::read_u16(&data[start..start + 2])) } else { - // The checksum is ellided and needs to be recomputed on the 6LoWPAN termination point. + // The checksum is elided and needs to be recomputed on the 6LoWPAN termination point. None } } @@ -1529,7 +1529,7 @@ pub mod nhc { return Err(Error::Checksum); } } else { - net_trace!("Currently we do not support ellided checksums."); + net_trace!("Currently we do not support elided checksums."); return Err(Error::Unrecognized); }; @@ -1726,7 +1726,7 @@ mod test { //match ext_packet.extension_header_id() { //nhc::ExtensionHeaderId::RoutingHeader => { - //// We are not intersted in the Next Header protocol. + //// We are not interested in the Next Header protocol. //let proto = ipv6::Protocol::Unknown(0); //let mut new_payload = [0; 8]; diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index bea318908..74d4a4549 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -841,7 +841,7 @@ impl<'a> Repr<'a> { TcpOption::MaxSegmentSize(value) => max_seg_size = Some(value), TcpOption::WindowScale(value) => { // RFC 1323: Thus, the shift count must be limited to 14 (which allows windows - // of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt + // of 2**30 = 1 Gigabyte). If a Window Scale option is received with a shift.cnt // value exceeding 14, the TCP should log the error but use 14 instead of the // specified value. window_scale = if value > 14 { From f5eff5ee8bc76f1e2e47b3ee5b0f20c52c6384be Mon Sep 17 00:00:00 2001 From: olinex Date: Fri, 29 Apr 2022 10:58:06 +0800 Subject: [PATCH 305/566] fix ipv6 contains_addr function in wire --- src/wire/ipv6.rs | 89 ++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index b0f8cfb2a..202cebe8f 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -331,8 +331,7 @@ impl Cidr { return true; } - let shift = 128 - self.prefix_len; - self.address.mask(shift) == addr.mask(shift) + self.address.mask(self.prefix_len) == addr.mask(self.prefix_len) } /// Query whether the subnetwork described by this IPV6 CIDR block contains @@ -882,96 +881,114 @@ mod test { #[test] fn test_cidr() { - let cidr = Cidr::new(LINK_LOCAL_ADDR, 64); + // fe80::1/56 + // 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + let cidr = Cidr::new(LINK_LOCAL_ADDR, 56); let inside_subnet = [ + // fe80::2 [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, ], + // fe80::1122:3344:5566:7788 [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, - 0x77, 0x88, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, ], + // fe80::ff00:0:0:0 [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ], + // fe80::ff [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, ], ]; let outside_subnet = [ + // fe80:0:0:101::1 [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ::1 [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ff02::1 [ - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ff02::2 [ - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, ], ]; let subnets = [ + // fe80::ffff:ffff:ffff:ffff/65 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], 65, ), + // fe80::1/128 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], 128, ), + // fe80::1234:5678/96 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x34, 0x56, 0x78, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, ], 96, ), ]; let not_subnets = [ + // fe80::101:ffff:ffff:ffff:ffff/55 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 63, + 55, ), + // fe80::101:ffff:ffff:ffff:ffff/56 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 64, + 56, ), + // fe80::101:ffff:ffff:ffff:ffff/57 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 65, + 57, ), + // ::1/128 ( [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], 128, ), From 5f19c7a326e16681ffe518543a70c3fd11d10d2f Mon Sep 17 00:00:00 2001 From: olinex Date: Fri, 29 Apr 2022 10:58:06 +0800 Subject: [PATCH 306/566] fix ipv6 contains_addr function in wire --- src/wire/ipv6.rs | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index b0f8cfb2a..8400da9a5 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -331,8 +331,7 @@ impl Cidr { return true; } - let shift = 128 - self.prefix_len; - self.address.mask(shift) == addr.mask(shift) + self.address.mask(self.prefix_len) == addr.mask(self.prefix_len) } /// Query whether the subnetwork described by this IPV6 CIDR block contains @@ -882,21 +881,28 @@ mod test { #[test] fn test_cidr() { - let cidr = Cidr::new(LINK_LOCAL_ADDR, 64); + // fe80::1/56 + // 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + let cidr = Cidr::new(LINK_LOCAL_ADDR, 56); let inside_subnet = [ + // fe80::2 [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, ], + // fe80::1122:3344:5566:7788 [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, ], + // fe80::ff00:0:0:0 [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ], + // fe80::ff [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, @@ -904,18 +910,22 @@ mod test { ]; let outside_subnet = [ + // fe80:0:0:101::1 [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ::1 [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ff02::1 [ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ], + // ff02::2 [ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, @@ -923,6 +933,7 @@ mod test { ]; let subnets = [ + // fe80::ffff:ffff:ffff:ffff/65 ( [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -930,6 +941,7 @@ mod test { ], 65, ), + // fe80::1/128 ( [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -937,6 +949,7 @@ mod test { ], 128, ), + // fe80::1234:5678/96 ( [ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, @@ -947,27 +960,31 @@ mod test { ]; let not_subnets = [ + // fe80::101:ffff:ffff:ffff:ffff/55 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 63, + 55, ), + // fe80::101:ffff:ffff:ffff:ffff/56 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 64, + 56, ), + // fe80::101:ffff:ffff:ffff:ffff/57 ( [ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ], - 65, + 57, ), + // ::1/128 ( [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, From 7979b7ab1ad34fa50d23161e78bd668e91a10813 Mon Sep 17 00:00:00 2001 From: ngc0202 Date: Fri, 6 May 2022 20:10:55 -0400 Subject: [PATCH 307/566] Return tuntap err instead of unwrap --- src/phy/sys/tuntap_interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phy/sys/tuntap_interface.rs b/src/phy/sys/tuntap_interface.rs index c0dae80cd..494313e2d 100644 --- a/src/phy/sys/tuntap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -103,7 +103,7 @@ impl TunTapInterfaceDesc { buffer.len(), ); if len == -1 { - Err(io::Error::last_os_error()).unwrap() + return Err(io::Error::last_os_error()); } Ok(len as usize) } From b283356679b6d8b7295937673c67a12d1167d5c1 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 12 May 2022 14:22:44 +0200 Subject: [PATCH 308/566] fix mtu calculation for raw raw_socket Previously, the ethernet frame header was added twice to the MTU. The first time in `interface_mtu` and a second time after calling `interface_mtu` in the raw socket `new` function. I removed the one in the `interface_mtu` function. --- src/phy/raw_socket.rs | 2 +- src/phy/sys/raw_socket.rs | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 49e7470a1..b266156aa 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -43,7 +43,7 @@ impl RawSocket { Ok(RawSocket { medium, lower: Rc::new(RefCell::new(lower)), - mtu: mtu, + mtu, }) } } diff --git a/src/phy/sys/raw_socket.rs b/src/phy/sys/raw_socket.rs index 951f358dd..f37fe960f 100644 --- a/src/phy/sys/raw_socket.rs +++ b/src/phy/sys/raw_socket.rs @@ -1,6 +1,5 @@ use super::*; use crate::phy::Medium; -use crate::wire::EthernetFrame; use std::os::unix::io::{AsRawFd, RawFd}; use std::{io, mem}; @@ -48,11 +47,7 @@ impl RawSocketDesc { } pub fn interface_mtu(&mut self) -> io::Result { - // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) - // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. - let ip_mtu = - ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?; - Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len()) + ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize) } pub fn bind_interface(&mut self) -> io::Result<()> { From 1b2458ae313a7f1ba022c586b04c4e6522b44046 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 12 May 2022 19:28:39 +0200 Subject: [PATCH 309/566] Add 0.8.1 changelog. --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 678c26d74..b008df1f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - No unreleased changes. +## [0.8.1] - 2022-05-12 + +- Remove unused `rand_core` dep. ([#589](https://github.com/smoltcp-rs/smoltcp/pull/589)) +- Use socklen_t instead of u32 for RawSocket bind() parameter. Fixes build on 32bit Android. ([#593](https://github.com/smoltcp-rs/smoltcp/pull/593)) +- Propagate phy::RawSocket send errors to caller ([#588](https://github.com/smoltcp-rs/smoltcp/pull/588)) +- Fix Interface set_hardware_addr, get_hardware_addr for ieee802154/6lowpan. ([#584](https://github.com/smoltcp-rs/smoltcp/pull/584)) + ## [0.8.0] - 2021-12-11 - Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.56 @@ -133,6 +140,7 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) [Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[0.8.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.8.0 [0.7.5]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.4...v0.7.5 [0.7.4]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.3...v0.7.4 From eb41d077e0ad8a6ef80c75803d53e0b993d05b91 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 03:30:32 +0200 Subject: [PATCH 310/566] Remove IpAddress::Unspecified, assign src addr in sockets. This continues work started in #579, with the goal of "remove unspecified variants from wire". "unspecified" variants are bad, they've been a source of bugs in the past. The issue with them is that depending on the context it may or may not make sense for the value to be unspecified. It's better to not have them, and then use Option only where the value can really be unspecified. This removes lots of `Address::Unspecified => unreachable!()` and similar match arms, which shows the unreachable variant was actively unwanted in many places. This fixes the "unspecified src addr" panic: - Picking src addr is now the resposibility of the sockets, not the interface. All sockets now emit IpReprs with properly assigned src addr. - Removed the `assert!(!ip_repr.src_addr().is_unspecified());`. This assert is WRONG even if now sockets pick the source address, because there ARE cases where we indeed want to send a packet with zero src addr, for example in DHCP. --- CHANGELOG.md | 5 +- examples/loopback.rs | 6 +- examples/ping.rs | 2 - src/iface/interface.rs | 44 +++- src/iface/route.rs | 1 - src/socket/icmp.rs | 70 ++--- src/socket/raw.rs | 1 - src/socket/tcp.rs | 568 +++++++++++++---------------------------- src/socket/udp.rs | 35 ++- src/wire/ip.rs | 195 +++++++------- src/wire/mod.rs | 5 +- 11 files changed, 396 insertions(+), 536 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b008df1f6..c16795be7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- No unreleased changes. +- Remove IpRepr::Unspecified (#579) +- Remove IpVersion::Unspecified +- Remove IpAddress::Unspecified +- When sending packets with a raw socket, the source IP address is sent unmodified (it was previously replaced with the interface's address if it was unspecified). ## [0.8.1] - 2022-05-12 diff --git a/examples/loopback.rs b/examples/loopback.rs index a3f6a3552..183e0c50c 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -150,11 +150,7 @@ fn main() { if !did_connect { debug!("connecting"); socket - .connect( - cx, - (IpAddress::v4(127, 0, 0, 1), 1234), - (IpAddress::Unspecified, 65000), - ) + .connect(cx, (IpAddress::v4(127, 0, 0, 1), 1234), 65000) .unwrap(); did_connect = true; } diff --git a/examples/ping.rs b/examples/ping.rs index 66fad82f2..46685c703 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -195,7 +195,6 @@ fn main() { &device_caps.checksum, ); } - _ => unimplemented!(), } waiting_queue.insert(seq_no, timestamp); @@ -239,7 +238,6 @@ fn main() { received ); } - _ => unimplemented!(), } } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 7eac70e11..ea2aec51a 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -641,6 +641,7 @@ where } } // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] _ => Err(Error::Unaddressable), } } @@ -672,6 +673,7 @@ where } } // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] _ => Err(Error::Unaddressable), } } @@ -693,6 +695,7 @@ where .iter() .filter_map(|cidr| match cidr.address() { IpAddress::Ipv4(addr) => Some(addr), + #[allow(unreachable_patterns)] _ => None, }) .next() @@ -1061,16 +1064,46 @@ impl<'a> InterfaceInner<'a> { #[allow(unused)] // unused depending on which sockets are enabled pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { - let v = dst_addr.version().unwrap(); + let v = dst_addr.version(); for cidr in self.ip_addrs.iter() { let addr = cidr.address(); - if addr.version() == Some(v) { + if addr.version() == v { return Some(addr); } } None } + #[cfg(feature = "proto-ipv4")] + #[allow(unused)] + pub(crate) fn get_source_address_ipv4( + &mut self, + _dst_addr: Ipv4Address, + ) -> Option { + for cidr in self.ip_addrs.iter() { + #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled + if let IpCidr::Ipv4(cidr) = cidr { + return Some(cidr.address()); + } + } + None + } + + #[cfg(feature = "proto-ipv6")] + #[allow(unused)] + pub(crate) fn get_source_address_ipv6( + &mut self, + _dst_addr: Ipv6Address, + ) -> Option { + for cidr in self.ip_addrs.iter() { + #[allow(irrefutable_let_patterns)] // if only ipv6 is enabled + if let IpCidr::Ipv6(cidr) = cidr { + return Some(cidr.address()); + } + } + None + } + #[cfg(test)] pub(crate) fn mock() -> Self { Self { @@ -1207,6 +1240,7 @@ impl<'a> InterfaceInner<'a> { key == Ipv4Address::MULTICAST_ALL_SYSTEMS || self.ipv4_multicast_groups.get(&key).is_some() } + #[allow(unreachable_patterns)] _ => false, } } @@ -2301,7 +2335,6 @@ impl<'a> InterfaceInner<'a> { if dst_addr.is_multicast() { let b = dst_addr.as_bytes(); let hardware_addr = match *dst_addr { - IpAddress::Unspecified => unreachable!(), #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(_addr) => { HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ @@ -2401,6 +2434,7 @@ impl<'a> InterfaceInner<'a> { self.dispatch_ip(tx_token, packet)?; } + #[allow(unreachable_patterns)] _ => (), } // The request got dispatched, limit the rate on the cache. @@ -2417,7 +2451,6 @@ impl<'a> InterfaceInner<'a> { fn dispatch_ip(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { let ip_repr = packet.ip_repr(); - assert!(!ip_repr.src_addr().is_unspecified()); assert!(!ip_repr.dst_addr().is_unspecified()); match self.caps.medium { @@ -2471,7 +2504,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] fn dispatch_ieee802154(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { let ip_repr = packet.ip_repr(); - assert!(!ip_repr.src_addr().is_unspecified()); assert!(!ip_repr.dst_addr().is_unspecified()); match self.caps.medium { @@ -2518,6 +2550,7 @@ impl<'a> InterfaceInner<'a> { let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), + #[allow(unreachable_patterns)] _ => return Err(Error::Unaddressable), }; @@ -2608,6 +2641,7 @@ impl<'a> InterfaceInner<'a> { Ok(()) }) } + #[allow(unreachable_patterns)] _ => Err(Error::NotSupported), } } diff --git a/src/iface/route.rs b/src/iface/route.rs index a6704cc23..ed87b5255 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -134,7 +134,6 @@ impl<'a> Routes<'a> { IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)), #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)), - _ => unimplemented!(), }; for (prefix, route) in self diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index f8862efde..4cfdfbfa6 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -11,10 +11,10 @@ use crate::{Error, Result}; use crate::wire::IcmpRepr; #[cfg(feature = "proto-ipv4")] -use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Address, Ipv4Repr}; +use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] -use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Address, Ipv6Repr}; -use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr}; +use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Repr}; +use crate::wire::{IpAddress, IpListenEndpoint, IpProtocol, IpRepr}; use crate::wire::{UdpPacket, UdpRepr}; /// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for @@ -26,7 +26,7 @@ use crate::wire::{UdpPacket, UdpRepr}; pub enum Endpoint { Unspecified, Ident(u16), - Udp(IpEndpoint), + Udp(IpListenEndpoint), } impl Endpoint { @@ -80,7 +80,7 @@ impl<'a> IcmpSocket<'a> { IcmpSocket { rx_buffer: rx_buffer, tx_buffer: tx_buffer, - endpoint: Endpoint::default(), + endpoint: Default::default(), hop_limit: None, #[cfg(feature = "async")] rx_waker: WakerRegistration::new(), @@ -170,14 +170,14 @@ impl<'a> IcmpSocket<'a> { /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata}; /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); - /// use smoltcp::wire::IpEndpoint; + /// use smoltcp::wire::IpListenEndpoint; /// use smoltcp::socket::IcmpEndpoint; /// /// let mut icmp_socket = // ... /// # IcmpSocket::new(rx_buffer, tx_buffer); /// /// // Bind to ICMP error responses for UDP packets sent from port 53. - /// let endpoint = IpEndpoint::from(53); + /// let endpoint = IpListenEndpoint::from(53); /// icmp_socket.bind(IcmpEndpoint::Udp(endpoint)).unwrap(); /// ``` /// @@ -332,7 +332,7 @@ impl<'a> IcmpSocket<'a> { ( &Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }), - ) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => { + ) if endpoint.addr.is_none() || endpoint.addr == Some(ip_repr.dst_addr()) => { let packet = UdpPacket::new_unchecked(data); match UdpRepr::parse( &packet, @@ -348,7 +348,7 @@ impl<'a> IcmpSocket<'a> { ( &Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }), - ) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => { + ) if endpoint.addr.is_none() || endpoint.addr == Some(ip_repr.dst_addr()) => { let packet = UdpPacket::new_unchecked(data); match UdpRepr::parse( &packet, @@ -447,12 +447,16 @@ impl<'a> IcmpSocket<'a> { ); match *remote_endpoint { #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(ipv4_addr) => { + IpAddress::Ipv4(dst_addr) => { + let src_addr = match cx.get_source_address_ipv4(dst_addr) { + Some(addr) => addr, + None => return Err(Error::Unaddressable), + }; let packet = Icmpv4Packet::new_unchecked(&*packet_buf); let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: ipv4_addr, + src_addr, + dst_addr, next_header: IpProtocol::Icmp, payload_len: repr.buffer_len(), hop_limit: hop_limit, @@ -460,25 +464,27 @@ impl<'a> IcmpSocket<'a> { emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) } #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(ipv6_addr) => { + IpAddress::Ipv6(dst_addr) => { + let src_addr = match cx.get_source_address_ipv6(dst_addr) { + Some(addr) => addr, + None => return Err(Error::Unaddressable), + }; let packet = Icmpv6Packet::new_unchecked(&*packet_buf); - let src_addr = Ipv6Address::default(); let repr = Icmpv6Repr::parse( &src_addr.into(), - &ipv6_addr.into(), + &dst_addr.into(), &packet, &ChecksumCapabilities::ignored(), )?; let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_addr, - dst_addr: ipv6_addr, + src_addr, + dst_addr, next_header: IpProtocol::Icmpv6, payload_len: repr.buffer_len(), hop_limit: hop_limit, }); emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) } - _ => Err(Error::Unaddressable), } })?; @@ -531,10 +537,10 @@ mod tests_common { mod test_ipv4 { use super::tests_common::*; - use crate::wire::Icmpv4DstUnreachable; + use crate::wire::{Icmpv4DstUnreachable, IpEndpoint, Ipv4Address}; - const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + const REMOTE_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 2]); + const LOCAL_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 1]); const LOCAL_END_V4: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv4(LOCAL_IPV4), port: LOCAL_PORT, @@ -547,7 +553,7 @@ mod test_ipv4 { }; static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address::UNSPECIFIED, + src_addr: LOCAL_IPV4, dst_addr: REMOTE_IPV4, next_header: IpProtocol::Icmp, payload_len: 24, @@ -566,7 +572,7 @@ mod test_ipv4 { fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); assert_eq!( - socket.send_slice(b"abcdef", IpAddress::default()), + socket.send_slice(b"abcdef", IpAddress::Ipv4(Ipv4Address::default())), Err(Error::Unaddressable) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(())); @@ -648,7 +654,7 @@ mod test_ipv4 { assert_eq!( ip_repr, IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address::UNSPECIFIED, + src_addr: LOCAL_IPV4, dst_addr: REMOTE_IPV4, next_header: IpProtocol::Icmp, payload_len: ECHOV4_REPR.buffer_len(), @@ -719,7 +725,7 @@ mod test_ipv4 { fn test_accepts_udp() { let mut socket = socket(buffer(1), buffer(1)); let mut cx = Context::mock(); - assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4)), Ok(())); + assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4.into())), Ok(())); let checksum = ChecksumCapabilities::default(); @@ -778,12 +784,12 @@ mod test_ipv4 { mod test_ipv6 { use super::tests_common::*; - use crate::wire::Icmpv6DstUnreachable; + use crate::wire::{Icmpv6DstUnreachable, IpEndpoint, Ipv6Address}; const REMOTE_IPV6: Ipv6Address = - Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - const LOCAL_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); + const LOCAL_IPV6: Ipv6Address = + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); const LOCAL_END_V6: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv6(LOCAL_IPV6), port: LOCAL_PORT, @@ -795,7 +801,7 @@ mod test_ipv6 { }; static LOCAL_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::UNSPECIFIED, + src_addr: LOCAL_IPV6, dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, payload_len: 24, @@ -814,7 +820,7 @@ mod test_ipv6 { fn test_send_unaddressable() { let mut socket = socket(buffer(0), buffer(1)); assert_eq!( - socket.send_slice(b"abcdef", IpAddress::default()), + socket.send_slice(b"abcdef", IpAddress::Ipv6(Ipv6Address::default())), Err(Error::Unaddressable) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV6.into()), Ok(())); @@ -906,7 +912,7 @@ mod test_ipv6 { assert_eq!( ip_repr, IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::UNSPECIFIED, + src_addr: LOCAL_IPV6, dst_addr: REMOTE_IPV6, next_header: IpProtocol::Icmpv6, payload_len: ECHOV6_REPR.buffer_len(), @@ -987,7 +993,7 @@ mod test_ipv6 { fn test_accepts_udp() { let mut socket = socket(buffer(1), buffer(1)); let mut cx = Context::mock(); - assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6)), Ok(())); + assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6.into())), Ok(())); let checksum = ChecksumCapabilities::default(); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 9319c94af..ba5b9ab4f 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -277,7 +277,6 @@ impl<'a> RawSocket<'a> { let ipv6_repr = Ipv6Repr::parse(&packet)?; Ok((IpRepr::Ipv6(ipv6_repr), packet.payload())) } - IpVersion::Unspecified => unreachable!(), } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index c7950d2d7..663cda38f 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2,6 +2,7 @@ // the parts of RFC 1122 that discuss TCP. Consult RFC 7414 when implementing // a new feature. +use core::fmt::Display; #[cfg(feature = "async")] use core::task::Waker; use core::{cmp, fmt, mem}; @@ -12,10 +13,15 @@ use crate::socket::{Context, PollAt}; use crate::storage::{Assembler, RingBuffer}; use crate::time::{Duration, Instant}; use crate::wire::{ - IpAddress, IpEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber, TCP_HEADER_LEN, + IpAddress, IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber, + TCP_HEADER_LEN, }; use crate::{Error, Result}; +macro_rules! tcp_trace { + ($($arg:expr),*) => (net_log!(trace, $($arg),*)); +} + /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -106,7 +112,7 @@ impl RttEstimator { self.rto_count = 0; let rto = self.retransmission_timeout().total_millis(); - net_trace!( + tcp_trace!( "rtte: sample={:?} rtt={:?} dev={:?} rto={:?}", new_rtt, self.rtt, @@ -124,7 +130,7 @@ impl RttEstimator { self.max_seq_sent = Some(seq); if self.timestamp.is_none() { self.timestamp = Some((timestamp, seq)); - net_trace!("rtte: sampling at seq={:?}", seq); + tcp_trace!("rtte: sampling at seq={:?}", seq); } } } @@ -140,7 +146,7 @@ impl RttEstimator { fn on_retransmit(&mut self) { if self.timestamp.is_some() { - net_trace!("rtte: abort sampling due to retransmit"); + tcp_trace!("rtte: abort sampling due to retransmit"); } self.timestamp = None; self.rto_count = self.rto_count.saturating_add(1); @@ -154,7 +160,7 @@ impl RttEstimator { self.rto_count = 0; self.rtt = RTTE_MAX_RTO.min(self.rtt * 2); let rto = self.retransmission_timeout().total_millis(); - net_trace!( + tcp_trace!( "rtte: too many retransmissions, increasing: rtt={:?} dev={:?} rto={:?}", self.rtt, self.deviation, @@ -300,6 +306,19 @@ enum AckDelayTimer { Immediate, } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct Tuple { + local: IpEndpoint, + remote: IpEndpoint, +} + +impl Display for Tuple { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.local, self.remote) + } +} + /// A Transmission Control Protocol socket. /// /// A TCP socket may passively listen for connections or actively connect to another endpoint. @@ -323,17 +342,9 @@ pub struct TcpSocket<'a> { hop_limit: Option, /// Address passed to listen(). Listen address is set when listen() is called and /// used every time the socket is reset back to the LISTEN state. - listen_address: IpAddress, - /// Current local endpoint. This is used for both filtering the incoming packets and - /// setting the source address. When listening or initiating connection on/from - /// an unspecified address, this field is updated with the chosen source address before - /// any packets are sent. - local_endpoint: IpEndpoint, - /// Current remote endpoint. This is used for both filtering the incoming packets and - /// setting the destination address. If the remote endpoint is unspecified, it means that - /// aborting the connection will not send an RST, and, in TIME-WAIT state, will not - /// send an ACK. - remote_endpoint: IpEndpoint, + listen_endpoint: IpListenEndpoint, + /// Current 4-tuple (local and remote endpoints). + tuple: Option, /// The sequence number corresponding to the beginning of the transmit buffer. /// I.e. an ACK(local_seq_no+n) packet removes n bytes from the transmit buffer. local_seq_no: TcpSeqNumber, @@ -420,9 +431,8 @@ impl<'a> TcpSocket<'a> { timeout: None, keep_alive: None, hop_limit: None, - listen_address: IpAddress::default(), - local_endpoint: IpEndpoint::default(), - remote_endpoint: IpEndpoint::default(), + listen_endpoint: IpListenEndpoint::default(), + tuple: None, local_seq_no: TcpSeqNumber::default(), remote_seq_no: TcpSeqNumber::default(), remote_last_seq: TcpSeqNumber::default(), @@ -610,16 +620,16 @@ impl<'a> TcpSocket<'a> { self.hop_limit = hop_limit } - /// Return the local endpoint. + /// Return the local endpoint, or None if not connected. #[inline] - pub fn local_endpoint(&self) -> IpEndpoint { - self.local_endpoint + pub fn local_endpoint(&self) -> Option { + Some(self.tuple?.local) } - /// Return the remote endpoint. + /// Return the remote endpoint, or None if not connected. #[inline] - pub fn remote_endpoint(&self) -> IpEndpoint { - self.remote_endpoint + pub fn remote_endpoint(&self) -> Option { + Some(self.tuple?.remote) } /// Return the connection state, in terms of the TCP state machine. @@ -639,9 +649,8 @@ impl<'a> TcpSocket<'a> { self.tx_buffer.clear(); self.rx_buffer.clear(); self.rx_fin_received = false; - self.listen_address = IpAddress::default(); - self.local_endpoint = IpEndpoint::default(); - self.remote_endpoint = IpEndpoint::default(); + self.listen_endpoint = IpListenEndpoint::default(); + self.tuple = None; self.local_seq_no = TcpSeqNumber::default(); self.remote_seq_no = TcpSeqNumber::default(); self.remote_last_seq = TcpSeqNumber::default(); @@ -669,7 +678,7 @@ impl<'a> TcpSocket<'a> { /// if the port in the given endpoint is zero. pub fn listen(&mut self, local_endpoint: T) -> Result<()> where - T: Into, + T: Into, { let local_endpoint = local_endpoint.into(); if local_endpoint.port == 0 { @@ -681,9 +690,8 @@ impl<'a> TcpSocket<'a> { } self.reset(); - self.listen_address = local_endpoint.addr; - self.local_endpoint = local_endpoint; - self.remote_endpoint = IpEndpoint::default(); + self.listen_endpoint = local_endpoint; + self.tuple = None; self.set_state(State::Listen); Ok(()) } @@ -710,15 +718,15 @@ impl<'a> TcpSocket<'a> { ) -> Result<()> where T: Into, - U: Into, + U: Into, { let remote_endpoint: IpEndpoint = remote_endpoint.into(); - let mut local_endpoint: IpEndpoint = local_endpoint.into(); + let local_endpoint: IpListenEndpoint = local_endpoint.into(); if self.is_open() { return Err(Error::Illegal); } - if !remote_endpoint.is_specified() { + if remote_endpoint.port == 0 || remote_endpoint.addr.is_unspecified() { return Err(Error::Unaddressable); } if local_endpoint.port == 0 { @@ -726,15 +734,30 @@ impl<'a> TcpSocket<'a> { } // If local address is not provided, choose it automatically. - if local_endpoint.addr.is_unspecified() { - local_endpoint.addr = cx - .get_source_address(remote_endpoint.addr) - .ok_or(Error::Unaddressable)?; + let local_endpoint = IpEndpoint { + addr: match local_endpoint.addr { + Some(addr) => { + if addr.is_unspecified() { + return Err(Error::Unaddressable); + } + addr + } + None => cx + .get_source_address(remote_endpoint.addr) + .ok_or(Error::Unaddressable)?, + }, + port: local_endpoint.port, + }; + + if local_endpoint.addr.version() != remote_endpoint.addr.version() { + return Err(Error::Illegal); } self.reset(); - self.local_endpoint = local_endpoint; - self.remote_endpoint = remote_endpoint; + self.tuple = Some(Tuple { + local: local_endpoint, + remote: remote_endpoint, + }); self.set_state(State::SynSent); let seq = Self::random_seq_no(cx); @@ -936,10 +959,8 @@ impl<'a> TcpSocket<'a> { let (size, result) = f(&mut self.tx_buffer); if size > 0 { #[cfg(any(test, feature = "verbose"))] - net_trace!( - "tcp:{}:{}: tx buffer: enqueueing {} octets (now {})", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "tx buffer: enqueueing {} octets (now {})", size, _old_length + size ); @@ -997,10 +1018,8 @@ impl<'a> TcpSocket<'a> { self.remote_seq_no += size; if size > 0 { #[cfg(any(test, feature = "verbose"))] - net_trace!( - "tcp:{}:{}: rx buffer: dequeueing {} octets (now {})", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "rx buffer: dequeueing {} octets (now {})", size, _old_length - size ); @@ -1048,12 +1067,7 @@ impl<'a> TcpSocket<'a> { let buffer = self.rx_buffer.get_allocated(0, size); if !buffer.is_empty() { #[cfg(any(test, feature = "verbose"))] - net_trace!( - "tcp:{}:{}: rx buffer: peeking at {} octets", - self.local_endpoint, - self.remote_endpoint, - buffer.len() - ); + tcp_trace!("rx buffer: peeking at {} octets", buffer.len()); } Ok(buffer) } @@ -1087,22 +1101,7 @@ impl<'a> TcpSocket<'a> { fn set_state(&mut self, state: State) { if self.state != state { - if self.remote_endpoint.addr.is_unspecified() { - net_trace!( - "tcp:{}: state={}=>{}", - self.local_endpoint, - self.state, - state - ); - } else { - net_trace!( - "tcp:{}:{}: state={}=>{}", - self.local_endpoint, - self.remote_endpoint, - self.state, - state - ); - } + tcp_trace!("state={}=>{}", self.state, state); } self.state = state; @@ -1242,27 +1241,20 @@ impl<'a> TcpSocket<'a> { return false; } - // Reject packets with a wrong destination. - if self.local_endpoint.port != repr.dst_port { - return false; - } - if !self.local_endpoint.addr.is_unspecified() - && self.local_endpoint.addr != ip_repr.dst_addr() - { - return false; - } - - // Reject packets from a source to which we aren't connected. - if self.remote_endpoint.port != 0 && self.remote_endpoint.port != repr.src_port { - return false; - } - if !self.remote_endpoint.addr.is_unspecified() - && self.remote_endpoint.addr != ip_repr.src_addr() - { - return false; + if let Some(tuple) = &self.tuple { + // Reject packets not matching the 4-tuple + ip_repr.dst_addr() == tuple.local.addr + && repr.dst_port == tuple.local.port + && ip_repr.src_addr() == tuple.remote.addr + && repr.src_port == tuple.remote.port + } else { + // We're listening, reject packets not matching the listen endpoint. + let addr_ok = match self.listen_endpoint.addr { + Some(addr) => ip_repr.dst_addr() == addr, + None => true, + }; + addr_ok && repr.dst_port != 0 && repr.dst_port == self.listen_endpoint.port } - - true } pub(crate) fn process( @@ -1290,21 +1282,12 @@ impl<'a> TcpSocket<'a> { // An RST received in response to initial SYN is acceptable if it acknowledges // the initial SYN. (State::SynSent, TcpControl::Rst, None) => { - net_debug!( - "tcp:{}:{}: unacceptable RST (expecting RST|ACK) \ - in response to initial SYN", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("unacceptable RST (expecting RST|ACK) in response to initial SYN"); return Err(Error::Dropped); } (State::SynSent, TcpControl::Rst, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { - net_debug!( - "tcp:{}:{}: unacceptable RST|ACK in response to initial SYN", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("unacceptable RST|ACK in response to initial SYN"); return Err(Error::Dropped); } } @@ -1316,21 +1299,13 @@ impl<'a> TcpSocket<'a> { (State::Listen, _, Some(_)) => unreachable!(), // Every packet after the initial SYN must be an acknowledgement. (_, _, None) => { - net_debug!( - "tcp:{}:{}: expecting an ACK", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("expecting an ACK"); return Err(Error::Dropped); } // SYN|ACK in the SYN-SENT state must have the exact ACK number. (State::SynSent, TcpControl::Syn, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { - net_debug!( - "tcp:{}:{}: unacceptable SYN|ACK in response to initial SYN", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("unacceptable SYN|ACK in response to initial SYN"); return Ok(Some(Self::rst_reply(ip_repr, repr))); } } @@ -1342,37 +1317,25 @@ impl<'a> TcpSocket<'a> { // does it, we do too. if ack_number == self.local_seq_no + 1 { net_debug!( - "tcp:{}:{}: expecting a SYN|ACK, received an ACK with the right ack_number, ignoring.", - self.local_endpoint, - self.remote_endpoint + "expecting a SYN|ACK, received an ACK with the right ack_number, ignoring." ); return Err(Error::Dropped); } net_debug!( - "tcp:{}:{}: expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST.", - self.local_endpoint, - self.remote_endpoint + "expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST." ); return Ok(Some(Self::rst_reply(ip_repr, repr))); } // Anything else in the SYN-SENT state is invalid. (State::SynSent, _, _) => { - net_debug!( - "tcp:{}:{}: expecting a SYN|ACK", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("expecting a SYN|ACK"); return Err(Error::Dropped); } // ACK in the SYN-RECEIVED state must have the exact ACK number, or we RST it. (State::SynReceived, _, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { - net_debug!( - "tcp:{}:{}: unacceptable ACK in response to SYN|ACK", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("unacceptable ACK in response to SYN|ACK"); return Ok(Some(Self::rst_reply(ip_repr, repr))); } } @@ -1391,9 +1354,7 @@ impl<'a> TcpSocket<'a> { if ack_number < ack_min { net_debug!( - "tcp:{}:{}: duplicate ACK ({} not in {}...{})", - self.local_endpoint, - self.remote_endpoint, + "duplicate ACK ({} not in {}...{})", ack_number, ack_min, ack_max @@ -1403,9 +1364,7 @@ impl<'a> TcpSocket<'a> { if ack_number > ack_max { net_debug!( - "tcp:{}:{}: unacceptable ACK ({} not in {}...{})", - self.local_endpoint, - self.remote_endpoint, + "unacceptable ACK ({} not in {}...{})", ack_number, ack_min, ack_max @@ -1430,30 +1389,19 @@ impl<'a> TcpSocket<'a> { if window_start == window_end && segment_start != segment_end { net_debug!( - "tcp:{}:{}: non-zero-length segment with zero receive window, \ - will only send an ACK", - self.local_endpoint, - self.remote_endpoint + "non-zero-length segment with zero receive window, will only send an ACK" ); segment_in_window = false; } if segment_start == segment_end && segment_end == window_start - 1 { - net_debug!( - "tcp:{}:{}: received a keep-alive or window probe packet, \ - will send an ACK", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("received a keep-alive or window probe packet, will send an ACK"); segment_in_window = false; } else if !((window_start <= segment_start && segment_start <= window_end) && (window_start <= segment_end && segment_end <= window_end)) { net_debug!( - "tcp:{}:{}: segment not in receive window \ - ({}..{} not intersecting {}..{}), will send challenge ACK", - self.local_endpoint, - self.remote_endpoint, + "segment not in receive window ({}..{} not intersecting {}..{}), will send challenge ACK", segment_start, segment_end, window_start, @@ -1496,11 +1444,7 @@ impl<'a> TcpSocket<'a> { // space if all of that data is acknowledged. if sent_fin && self.tx_buffer.len() + 1 == ack_len { ack_len -= 1; - net_trace!( - "tcp:{}:{}: received ACK of FIN", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("received ACK of FIN"); ack_of_fin = true; } } @@ -1526,47 +1470,35 @@ impl<'a> TcpSocket<'a> { // RSTs in SYN-RECEIVED flip the socket back to the LISTEN state. (State::SynReceived, TcpControl::Rst) => { - net_trace!( - "tcp:{}:{}: received RST", - self.local_endpoint, - self.remote_endpoint - ); - self.local_endpoint.addr = self.listen_address; - self.remote_endpoint = IpEndpoint::default(); + tcp_trace!("received RST"); + self.tuple = None; self.set_state(State::Listen); return Ok(None); } // RSTs in any other state close the socket. (_, TcpControl::Rst) => { - net_trace!( - "tcp:{}:{}: received RST", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("received RST"); self.set_state(State::Closed); - self.local_endpoint = IpEndpoint::default(); - self.remote_endpoint = IpEndpoint::default(); + self.tuple = None; return Ok(None); } // SYN packets in the LISTEN state change it to SYN-RECEIVED. (State::Listen, TcpControl::Syn) => { - net_trace!("tcp:{}: received SYN", self.local_endpoint); + tcp_trace!("received SYN"); if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { - net_trace!( - "tcp:{}:{}: received SYNACK with zero MSS, ignoring", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("received SYNACK with zero MSS, ignoring"); return Ok(None); } self.remote_mss = max_seg_size as usize } - self.local_endpoint = IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port); - self.remote_endpoint = IpEndpoint::new(ip_repr.src_addr(), repr.src_port); + self.tuple = Some(Tuple { + local: IpEndpoint::new(ip_repr.dst_addr(), repr.dst_port), + remote: IpEndpoint::new(ip_repr.src_addr(), repr.src_port), + }); self.local_seq_no = Self::random_seq_no(cx); self.remote_seq_no = repr.seq_number + 1; self.remote_last_seq = self.local_seq_no; @@ -1598,18 +1530,10 @@ impl<'a> TcpSocket<'a> { // SYN|ACK packets in the SYN-SENT state change it to ESTABLISHED. (State::SynSent, TcpControl::Syn) => { - net_trace!( - "tcp:{}:{}: received SYN|ACK", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("received SYN|ACK"); if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { - net_trace!( - "tcp:{}:{}: received SYNACK with zero MSS, ignoring", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("received SYNACK with zero MSS, ignoring"); return Ok(None); } self.remote_mss = max_seg_size as usize; @@ -1700,20 +1624,14 @@ impl<'a> TcpSocket<'a> { if ack_of_fin { // Clear the remote endpoint, or we'll send an RST there. self.set_state(State::Closed); - self.local_endpoint = IpEndpoint::default(); - self.remote_endpoint = IpEndpoint::default(); + self.tuple = None; } else { self.timer.set_for_idle(cx.now(), self.keep_alive); } } _ => { - net_debug!( - "tcp:{}:{}: unexpected packet {}", - self.local_endpoint, - self.remote_endpoint, - repr - ); + net_debug!("unexpected packet {}", repr); return Err(Error::Dropped); } } @@ -1732,10 +1650,8 @@ impl<'a> TcpSocket<'a> { if ack_len > 0 { // Dequeue acknowledged octets. debug_assert!(self.tx_buffer.len() >= ack_len); - net_trace!( - "tcp:{}:{}: tx buffer: dequeueing {} octets (now {})", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "tx buffer: dequeueing {} octets (now {})", ack_len, self.tx_buffer.len() - ack_len ); @@ -1767,9 +1683,7 @@ impl<'a> TcpSocket<'a> { self.local_rx_dup_acks = self.local_rx_dup_acks.saturating_add(1); net_debug!( - "tcp:{}:{}: received duplicate ACK for seq {} (duplicate nr {}{})", - self.local_endpoint, - self.remote_endpoint, + "received duplicate ACK for seq {} (duplicate nr {}{})", ack_number, self.local_rx_dup_acks, if self.local_rx_dup_acks == u8::max_value() { @@ -1781,22 +1695,14 @@ impl<'a> TcpSocket<'a> { if self.local_rx_dup_acks == 3 { self.timer.set_for_fast_retransmit(); - net_debug!( - "tcp:{}:{}: started fast retransmit", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("started fast retransmit"); } } // No duplicate ACK -> Reset state and update last received ACK _ => { if self.local_rx_dup_acks > 0 { self.local_rx_dup_acks = 0; - net_debug!( - "tcp:{}:{}: reset duplicate ACK count", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("reset duplicate ACK count"); } self.local_rx_last_ack = Some(ack_number); } @@ -1826,10 +1732,8 @@ impl<'a> TcpSocket<'a> { Ok(_) => { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. - net_trace!( - "tcp:{}:{}: rx buffer: receiving {} octets at offset {}", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "rx buffer: receiving {} octets at offset {}", payload_len, payload_offset ); @@ -1840,9 +1744,7 @@ impl<'a> TcpSocket<'a> { } Err(_) => { net_debug!( - "tcp:{}:{}: assembler: too many holes to add {} octets at offset {}", - self.local_endpoint, - self.remote_endpoint, + "assembler: too many holes to add {} octets at offset {}", payload_len, payload_offset ); @@ -1853,10 +1755,8 @@ impl<'a> TcpSocket<'a> { if let Some(contig_len) = self.assembler.remove_front() { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Enqueue the contiguous data octets in front of the buffer. - net_trace!( - "tcp:{}:{}: rx buffer: enqueueing {} octets (now {})", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "rx buffer: enqueueing {} octets (now {})", contig_len, self.rx_buffer.len() + contig_len ); @@ -1869,12 +1769,7 @@ impl<'a> TcpSocket<'a> { if !self.assembler.is_empty() { // Print the ranges recorded in the assembler. - net_trace!( - "tcp:{}:{}: assembler: {}", - self.local_endpoint, - self.remote_endpoint, - self.assembler - ); + tcp_trace!("assembler: {}", self.assembler); } // Handle delayed acks @@ -1882,11 +1777,7 @@ impl<'a> TcpSocket<'a> { if self.ack_to_transmit() || self.window_to_update() { self.ack_delay_timer = match self.ack_delay_timer { AckDelayTimer::Idle => { - net_trace!( - "tcp:{}:{}: starting delayed ack timer", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("starting delayed ack timer"); AckDelayTimer::Waiting(cx.now() + ack_delay) } @@ -1894,19 +1785,11 @@ impl<'a> TcpSocket<'a> { // for at least every second segment". // For now, we send an ACK every second received packet, full-sized or not. AckDelayTimer::Waiting(_) => { - net_trace!( - "tcp:{}:{}: delayed ack timer already started, forcing expiry", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("delayed ack timer already started, forcing expiry"); AckDelayTimer::Immediate } AckDelayTimer::Immediate => { - net_trace!( - "tcp:{}:{}: delayed ack timer already force-expired", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("delayed ack timer already force-expired"); AckDelayTimer::Immediate } }; @@ -1920,11 +1803,7 @@ impl<'a> TcpSocket<'a> { // Note that we change the transmitter state here. // This is fine because smoltcp assumes that it can always transmit zero or one // packets for every packet it receives. - net_trace!( - "tcp:{}:{}: ACKing incoming segment", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("ACKing incoming segment"); Ok(Some(self.ack_reply(ip_repr, repr))) } else { Ok(None) @@ -1939,12 +1818,11 @@ impl<'a> TcpSocket<'a> { } fn seq_to_transmit(&self, cx: &mut Context) -> bool { - let ip_header_len = match self.local_endpoint.addr { + let ip_header_len = match self.tuple.unwrap().local.addr { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(_) => crate::wire::IPV4_HEADER_LEN, #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(_) => crate::wire::IPV6_HEADER_LEN, - IpAddress::Unspecified => unreachable!(), }; // Max segment size we're able to send due to MTU limitations. @@ -2029,7 +1907,7 @@ impl<'a> TcpSocket<'a> { where F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<()>, { - if !self.remote_endpoint.is_specified() { + if self.tuple.is_none() { return Err(Error::Exhausted); } @@ -2047,21 +1925,12 @@ impl<'a> TcpSocket<'a> { // Check if any state needs to be changed because of a timer. if self.timed_out(cx.now()) { // If a timeout expires, we should abort the connection. - net_debug!( - "tcp:{}:{}: timeout exceeded", - self.local_endpoint, - self.remote_endpoint - ); + net_debug!("timeout exceeded"); self.set_state(State::Closed); } else if !self.seq_to_transmit(cx) { 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!( - "tcp:{}:{}: retransmitting at t+{}", - self.local_endpoint, - self.remote_endpoint, - retransmit_delta - ); + net_debug!("retransmitting at t+{}", retransmit_delta); // Rewind "last sequence number sent", as if we never // had sent them. This will cause all data in the queue @@ -2082,57 +1951,36 @@ impl<'a> TcpSocket<'a> { // Decide whether we're sending a packet. if self.seq_to_transmit(cx) { // If we have data to transmit and it fits into partner's window, do it. - net_trace!( - "tcp:{}:{}: outgoing segment will send data or flags", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("outgoing segment will send data or flags"); } else if self.ack_to_transmit() && self.delayed_ack_expired(cx.now()) { // If we have data to acknowledge, do it. - net_trace!( - "tcp:{}:{}: outgoing segment will acknowledge", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("outgoing segment will acknowledge"); } else if self.window_to_update() && self.delayed_ack_expired(cx.now()) { // If we have window length increase to advertise, do it. - net_trace!( - "tcp:{}:{}: outgoing segment will update window", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("outgoing segment will update window"); } else if self.state == State::Closed { // If we need to abort the connection, do it. - net_trace!( - "tcp:{}:{}: outgoing segment will abort connection", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("outgoing segment will abort connection"); } else if self.timer.should_keep_alive(cx.now()) { // If we need to transmit a keep-alive packet, do it. - net_trace!( - "tcp:{}:{}: keep-alive timer expired", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("keep-alive timer expired"); } else if self.timer.should_close(cx.now()) { // If we have spent enough time in the TIME-WAIT state, close the socket. - net_trace!( - "tcp:{}:{}: TIME-WAIT timer expired", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("TIME-WAIT timer expired"); self.reset(); return Err(Error::Exhausted); } else { return Err(Error::Exhausted); } + // NOTE(unwrap): we check tuple is not None the first thing in this function. + let tuple = self.tuple.unwrap(); + // Construct the lowered IP representation. // We might need this to calculate the MSS, so do it early. let mut ip_repr = IpRepr::new( - self.local_endpoint.addr, - self.remote_endpoint.addr, + tuple.local.addr, + tuple.remote.addr, IpProtocol::Tcp, 0, self.hop_limit.unwrap_or(64), @@ -2141,8 +1989,8 @@ impl<'a> TcpSocket<'a> { // Construct the basic TCP representation, an empty ACK packet. // We'll adjust this to be more specific as needed. let mut repr = TcpRepr { - src_port: self.local_endpoint.port, - dst_port: self.remote_endpoint.port, + src_port: tuple.local.port, + dst_port: tuple.remote.port, control: TcpControl::None, seq_number: self.remote_last_seq, ack_number: Some(self.remote_seq_no + self.rx_buffer.len()), @@ -2250,16 +2098,10 @@ impl<'a> TcpSocket<'a> { // Trace a summary of what will be sent. if is_keep_alive { - net_trace!( - "tcp:{}:{}: sending a keep-alive", - self.local_endpoint, - self.remote_endpoint - ); + tcp_trace!("sending a keep-alive"); } else if !repr.payload.is_empty() { - net_trace!( - "tcp:{}:{}: tx buffer: sending {} octets at offset {}", - self.local_endpoint, - self.remote_endpoint, + tcp_trace!( + "tx buffer: sending {} octets at offset {}", repr.payload.len(), self.remote_last_seq - self.local_seq_no ); @@ -2274,12 +2116,7 @@ impl<'a> TcpSocket<'a> { (TcpControl::None, Some(_)) => "ACK", _ => "", }; - net_trace!( - "tcp:{}:{}: sending {}", - self.local_endpoint, - self.remote_endpoint, - flags - ); + tcp_trace!("sending {}", flags); } if repr.control == TcpControl::Syn { @@ -2306,18 +2143,10 @@ impl<'a> TcpSocket<'a> { match self.ack_delay_timer { AckDelayTimer::Idle => {} AckDelayTimer::Waiting(_) => { - net_trace!( - "tcp:{}:{}: stop delayed ack timer", - self.local_endpoint, - self.remote_endpoint - ) + tcp_trace!("stop delayed ack timer") } AckDelayTimer::Immediate => { - net_trace!( - "tcp:{}:{}: stop delayed ack timer (was force-expired)", - self.local_endpoint, - self.remote_endpoint - ) + tcp_trace!("stop delayed ack timer (was force-expired)") } } self.ack_delay_timer = AckDelayTimer::Idle; @@ -2347,8 +2176,7 @@ impl<'a> TcpSocket<'a> { if self.state == State::Closed { // When aborting a connection, forget about it after sending a single RST packet. - self.local_endpoint = IpEndpoint::default(); - self.remote_endpoint = IpEndpoint::default(); + self.tuple = None; } Ok(()) @@ -2357,7 +2185,7 @@ impl<'a> TcpSocket<'a> { #[allow(clippy::if_same_then_else)] pub(crate) fn poll_at(&self, cx: &mut Context) -> PollAt { // The logic here mirrors the beginning of dispatch() closely. - if !self.remote_endpoint.is_specified() { + if self.tuple.is_none() { // No one to talk to, nothing to transmit. PollAt::Ingress } else if self.remote_last_ts.is_none() { @@ -2410,7 +2238,7 @@ impl<'a> fmt::Write for TcpSocket<'a> { #[cfg(test)] mod test { use super::*; - use crate::wire::{IpAddress, IpRepr}; + use crate::wire::IpRepr; use core::i32; use std::ops::{Deref, DerefMut}; use std::vec::Vec; @@ -2421,6 +2249,10 @@ mod test { const LOCAL_PORT: u16 = 80; const REMOTE_PORT: u16 = 49500; + const LISTEN_END: IpListenEndpoint = IpListenEndpoint { + addr: None, + port: LOCAL_PORT, + }; const LOCAL_END: IpEndpoint = IpEndpoint { addr: LOCAL_ADDR.into_address(), port: LOCAL_PORT, @@ -2429,6 +2261,10 @@ mod test { addr: REMOTE_ADDR.into_address(), port: REMOTE_PORT, }; + const TUPLE: Tuple = Tuple { + local: LOCAL_END, + remote: REMOTE_END, + }; const LOCAL_SEQ: TcpSeqNumber = TcpSeqNumber(10000); const REMOTE_SEQ: TcpSeqNumber = TcpSeqNumber(-10001); @@ -2611,9 +2447,7 @@ mod test { ($socket1:expr, $socket2:expr) => {{ let (s1, s2) = ($socket1, $socket2); assert_eq!(s1.state, s2.state, "state"); - assert_eq!(s1.listen_address, s2.listen_address, "listen_address"); - assert_eq!(s1.local_endpoint, s2.local_endpoint, "local_endpoint"); - assert_eq!(s1.remote_endpoint, s2.remote_endpoint, "remote_endpoint"); + assert_eq!(s1.tuple, s2.tuple, "tuple"); assert_eq!(s1.local_seq_no, s2.local_seq_no, "local_seq_no"); assert_eq!(s1.remote_seq_no, s2.remote_seq_no, "remote_seq_no"); assert_eq!(s1.remote_last_seq, s2.remote_last_seq, "remote_last_seq"); @@ -2640,8 +2474,7 @@ mod test { fn socket_syn_received_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynReceived; - s.local_endpoint = LOCAL_END; - s.remote_endpoint = REMOTE_END; + s.tuple = Some(TUPLE); s.local_seq_no = LOCAL_SEQ; s.remote_seq_no = REMOTE_SEQ + 1; s.remote_last_seq = LOCAL_SEQ; @@ -2656,8 +2489,7 @@ mod test { fn socket_syn_sent_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_with_buffer_sizes(tx_len, rx_len); s.state = State::SynSent; - s.local_endpoint = LOCAL_END; - s.remote_endpoint = REMOTE_END; + s.tuple = Some(TUPLE); s.local_seq_no = LOCAL_SEQ; s.remote_last_seq = LOCAL_SEQ; s @@ -2667,16 +2499,6 @@ mod test { socket_syn_sent_with_buffer_sizes(64, 64) } - fn socket_syn_sent_with_local_ipendpoint(local: IpEndpoint) -> TestSocket { - let mut s = socket(); - s.state = State::SynSent; - s.local_endpoint = local; - s.remote_endpoint = REMOTE_END; - s.local_seq_no = LOCAL_SEQ; - s.remote_last_seq = LOCAL_SEQ; - s - } - fn socket_established_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let mut s = socket_syn_received_with_buffer_sizes(tx_len, rx_len); s.state = State::Established; @@ -2804,7 +2626,7 @@ mod test { fn socket_listen() -> TestSocket { let mut s = socket(); s.state = State::Listen; - s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); + s.listen_endpoint = LISTEN_END; s } @@ -2876,7 +2698,7 @@ mod test { ] { let mut s = socket_with_buffer_sizes(64, *buffer_size); s.state = State::Listen; - s.local_endpoint = IpEndpoint::new(IpAddress::default(), LOCAL_PORT); + s.listen_endpoint = LISTEN_END; assert_eq!(s.remote_win_shift, *shift_amt); send!( s, @@ -3110,6 +2932,7 @@ mod test { #[test] fn test_syn_received_rst() { let mut s = socket_syn_received(); + s.listen_endpoint = LISTEN_END; recv!( s, [TcpRepr { @@ -3130,11 +2953,8 @@ mod test { } ); assert_eq!(s.state, State::Listen); - assert_eq!( - s.local_endpoint, - IpEndpoint::new(IpAddress::Unspecified, LOCAL_END.port) - ); - assert_eq!(s.remote_endpoint, IpEndpoint::default()); + assert_eq!(s.listen_endpoint, LISTEN_END); + assert_eq!(s.tuple, None); } #[test] @@ -3150,8 +2970,7 @@ mod test { } ); assert_eq!(s.state(), State::SynReceived); - assert_eq!(s.local_endpoint(), LOCAL_END); - assert_eq!(s.remote_endpoint(), REMOTE_END); + assert_eq!(s.tuple, Some(TUPLE)); recv!( s, [TcpRepr { @@ -3191,8 +3010,7 @@ mod test { } ); assert_eq!(s.state(), State::SynReceived); - assert_eq!(s.local_endpoint(), LOCAL_END); - assert_eq!(s.remote_endpoint(), REMOTE_END); + assert_eq!(s.tuple, Some(TUPLE)); recv!( s, [TcpRepr { @@ -3231,11 +3049,6 @@ mod test { #[test] fn test_connect_validation() { let mut s = socket(); - assert_eq!( - s.socket - .connect(&mut s.cx, (IpAddress::Unspecified, 80), LOCAL_END), - Err(Error::Unaddressable) - ); assert_eq!( s.socket .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 0)), @@ -3243,19 +3056,18 @@ mod test { ); assert_eq!( s.socket - .connect(&mut s.cx, (IpvXAddress::UNSPECIFIED, 0), LOCAL_END), + .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 1024)), Err(Error::Unaddressable) ); assert_eq!( s.socket - .connect(&mut s.cx, (IpAddress::Unspecified, 80), LOCAL_END), + .connect(&mut s.cx, (IpvXAddress::UNSPECIFIED, 0), LOCAL_END), Err(Error::Unaddressable) ); s.socket .connect(&mut s.cx, REMOTE_END, LOCAL_END) .expect("Connect failed with valid parameters"); - assert_eq!(s.local_endpoint(), LOCAL_END); - assert_eq!(s.remote_endpoint(), REMOTE_END); + assert_eq!(s.tuple, Some(TUPLE)); } #[test] @@ -3265,7 +3077,7 @@ mod test { s.socket .connect(&mut s.cx, REMOTE_END, LOCAL_END.port) .unwrap(); - assert_eq!(s.local_endpoint, LOCAL_END); + assert_eq!(s.tuple, Some(TUPLE)); recv!( s, [TcpRepr { @@ -3289,24 +3101,13 @@ mod test { ..SEND_TEMPL } ); - assert_eq!(s.local_endpoint, LOCAL_END); + assert_eq!(s.tuple, Some(TUPLE)); } #[test] fn test_connect_unspecified_local() { let mut s = socket(); - assert_eq!( - s.socket - .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 80)), - Ok(()) - ); - s.abort(); - assert_eq!( - s.socket - .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), - Ok(()) - ); - s.abort(); + assert_eq!(s.socket.connect(&mut s.cx, REMOTE_END, 80), Ok(())); } #[test] @@ -3321,14 +3122,9 @@ mod test { #[test] fn test_connect_twice() { let mut s = socket(); + assert_eq!(s.socket.connect(&mut s.cx, REMOTE_END, 80), Ok(())); assert_eq!( - s.socket - .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), - Ok(()) - ); - assert_eq!( - s.socket - .connect(&mut s.cx, REMOTE_END, (IpAddress::Unspecified, 80)), + s.socket.connect(&mut s.cx, REMOTE_END, 80), Err(Error::Illegal) ); } @@ -3338,7 +3134,7 @@ mod test { let mut s = socket(); s.local_seq_no = LOCAL_SEQ; s.socket.connect(&mut s.cx, REMOTE_END, LOCAL_END).unwrap(); - sanity!(s, socket_syn_sent_with_local_ipendpoint(LOCAL_END)); + sanity!(s, socket_syn_sent()); } #[test] @@ -4673,8 +4469,7 @@ mod test { #[test] fn test_listen() { let mut s = socket(); - s.listen(IpEndpoint::new(IpAddress::default(), LOCAL_PORT)) - .unwrap(); + s.listen(LISTEN_END).unwrap(); assert_eq!(s.state, State::Listen); } @@ -4691,8 +4486,7 @@ mod test { } ); assert_eq!(s.state(), State::SynReceived); - assert_eq!(s.local_endpoint(), LOCAL_END); - assert_eq!(s.remote_endpoint(), REMOTE_END); + assert_eq!(s.tuple, Some(TUPLE)); recv!( s, [TcpRepr { diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 6bb1322d6..802033f62 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -7,7 +7,7 @@ use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::storage::{PacketBuffer, PacketMetadata}; -use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr}; +use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; /// A UDP packet metadata. @@ -22,7 +22,7 @@ pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>; /// packet buffers. #[derive(Debug)] pub struct UdpSocket<'a> { - endpoint: IpEndpoint, + endpoint: IpListenEndpoint, rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -37,7 +37,7 @@ impl<'a> UdpSocket<'a> { /// Create an UDP socket with the given buffers. pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { UdpSocket { - endpoint: IpEndpoint::default(), + endpoint: IpListenEndpoint::default(), rx_buffer, tx_buffer, hop_limit: None, @@ -85,7 +85,7 @@ impl<'a> UdpSocket<'a> { /// Return the bound endpoint. #[inline] - pub fn endpoint(&self) -> IpEndpoint { + pub fn endpoint(&self) -> IpListenEndpoint { self.endpoint } @@ -121,7 +121,7 @@ impl<'a> UdpSocket<'a> { /// This function returns `Err(Error::Illegal)` if the socket was open /// (see [is_open](#method.is_open)), and `Err(Error::Unaddressable)` /// if the port in the given endpoint is zero. - pub fn bind>(&mut self, endpoint: T) -> Result<()> { + pub fn bind>(&mut self, endpoint: T) -> Result<()> { let endpoint = endpoint.into(); if endpoint.port == 0 { return Err(Error::Unaddressable); @@ -145,7 +145,7 @@ impl<'a> UdpSocket<'a> { /// Close the socket. pub fn close(&mut self) { // Clear the bound endpoint of the socket. - self.endpoint = IpEndpoint::default(); + self.endpoint = IpListenEndpoint::default(); // Reset the RX and TX buffers of the socket. self.tx_buffer.reset(); @@ -211,7 +211,10 @@ impl<'a> UdpSocket<'a> { if self.endpoint.port == 0 { return Err(Error::Unaddressable); } - if !remote_endpoint.is_specified() { + if remote_endpoint.addr.is_unspecified() { + return Err(Error::Unaddressable); + } + if remote_endpoint.port == 0 { return Err(Error::Unaddressable); } @@ -297,8 +300,8 @@ impl<'a> UdpSocket<'a> { if self.endpoint.port != repr.dst_port { return false; } - if !self.endpoint.addr.is_unspecified() - && self.endpoint.addr != ip_repr.dst_addr() + if !self.endpoint.addr.is_none() + && self.endpoint.addr != Some(ip_repr.dst_addr()) && !ip_repr.dst_addr().is_broadcast() && !ip_repr.dst_addr().is_multicast() { @@ -349,6 +352,14 @@ impl<'a> UdpSocket<'a> { self.tx_buffer .dequeue_with(|remote_endpoint, payload_buf| { + let src_addr = match endpoint.addr { + Some(addr) => addr, + None => match cx.get_source_address(remote_endpoint.addr) { + Some(addr) => addr, + None => return Err(Error::Unaddressable), + }, + }; + net_trace!( "udp:{}:{}: sending {} octets", endpoint, @@ -361,7 +372,7 @@ impl<'a> UdpSocket<'a> { dst_port: remote_endpoint.port, }; let ip_repr = IpRepr::new( - endpoint.addr, + src_addr, remote_endpoint.addr, IpProtocol::Udp, repr.header_len() + payload_buf.len(), @@ -388,7 +399,7 @@ impl<'a> UdpSocket<'a> { #[cfg(test)] mod test { use super::*; - use crate::wire::{IpAddress, IpRepr, UdpRepr}; + use crate::wire::{IpRepr, UdpRepr}; fn buffer(packets: usize) -> UdpSocketBuffer<'static> { UdpSocketBuffer::new( @@ -511,7 +522,7 @@ mod test { socket.send_slice( b"abcdef", IpEndpoint { - addr: IpAddress::Unspecified, + addr: IpvXAddress::UNSPECIFIED.into(), ..REMOTE_END } ), diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 9170c5451..6037c0827 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -11,9 +11,7 @@ use crate::{Error, Result}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] pub enum Version { - Unspecified, #[cfg(feature = "proto-ipv4")] Ipv4, #[cfg(feature = "proto-ipv6")] @@ -39,7 +37,6 @@ impl Version { impl fmt::Display for Version { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Version::Unspecified => write!(f, "IPv?"), #[cfg(feature = "proto-ipv4")] Version::Ipv4 => write!(f, "IPv4"), #[cfg(feature = "proto-ipv6")] @@ -84,11 +81,7 @@ impl fmt::Display for Protocol { /// An internetworking address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[non_exhaustive] pub enum Address { - /// An unspecified address. - /// May be used as a placeholder for storage where the address is not assigned yet. - Unspecified, /// An IPv4 address. #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Address), @@ -112,20 +105,18 @@ impl Address { } /// Return the protocol version. - pub fn version(&self) -> Option { + pub fn version(&self) -> Version { match self { - Address::Unspecified => None, #[cfg(feature = "proto-ipv4")] - Address::Ipv4(_) => Some(Version::Ipv4), + Address::Ipv4(_) => Version::Ipv4, #[cfg(feature = "proto-ipv6")] - Address::Ipv6(_) => Some(Version::Ipv6), + Address::Ipv6(_) => Version::Ipv6, } } /// Return an address as a sequence of octets, in big-endian. pub fn as_bytes(&self) -> &[u8] { match *self { - Address::Unspecified => &[], #[cfg(feature = "proto-ipv4")] Address::Ipv4(ref addr) => addr.as_bytes(), #[cfg(feature = "proto-ipv6")] @@ -136,7 +127,6 @@ impl Address { /// Query whether the address is a valid unicast address. pub fn is_unicast(&self) -> bool { match *self { - Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_unicast(), #[cfg(feature = "proto-ipv6")] @@ -147,7 +137,6 @@ impl Address { /// Query whether the address is a valid multicast address. pub fn is_multicast(&self) -> bool { match *self { - Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_multicast(), #[cfg(feature = "proto-ipv6")] @@ -158,7 +147,6 @@ impl Address { /// Query whether the address is the broadcast address. pub fn is_broadcast(&self) -> bool { match *self { - Address::Unspecified => false, #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_broadcast(), #[cfg(feature = "proto-ipv6")] @@ -169,7 +157,6 @@ impl Address { /// Query whether the address falls into the "unspecified" range. pub fn is_unspecified(&self) -> bool { match *self { - Address::Unspecified => true, #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_unspecified(), #[cfg(feature = "proto-ipv6")] @@ -177,17 +164,6 @@ impl Address { } } - /// Return an unspecified address that has the same IP version as `self`. - pub fn as_unspecified(&self) -> Address { - match *self { - Address::Unspecified => Address::Unspecified, - #[cfg(feature = "proto-ipv4")] - Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED), - #[cfg(feature = "proto-ipv6")] - Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED), - } - } - /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`, /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise. pub fn prefix_len(&self) -> Option { @@ -233,7 +209,6 @@ impl From
for ::std::net::IpAddr { Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()), #[cfg(feature = "proto-ipv6")] Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()), - _ => unreachable!(), } } } @@ -252,12 +227,6 @@ impl From<::std::net::Ipv6Addr> for Address { } } -impl Default for Address { - fn default() -> Address { - Address::Unspecified - } -} - #[cfg(feature = "proto-ipv4")] impl From for Address { fn from(addr: Ipv4Address) -> Self { @@ -275,7 +244,6 @@ impl From for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Address::Unspecified => write!(f, "*"), #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => write!(f, "{}", addr), #[cfg(feature = "proto-ipv6")] @@ -288,7 +256,6 @@ impl fmt::Display for Address { impl defmt::Format for Address { fn format(&self, f: defmt::Formatter) { match self { - &Address::Unspecified => defmt::write!(f, "{:?}", "*"), #[cfg(feature = "proto-ipv4")] &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr), #[cfg(feature = "proto-ipv6")] @@ -300,7 +267,6 @@ impl defmt::Format for Address { /// A specification of a CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[non_exhaustive] pub enum Cidr { #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Cidr), @@ -312,17 +278,13 @@ impl Cidr { /// Create a CIDR block from the given address and prefix length. /// /// # Panics - /// This function panics if the given address is unspecified, or - /// the given prefix length is invalid for the given address. + /// This function panics if the given prefix length is invalid for the given address. pub fn new(addr: Address, prefix_len: u8) -> Cidr { match addr { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)), #[cfg(feature = "proto-ipv6")] Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)), - Address::Unspecified => { - panic!("a CIDR block cannot be based on an unspecified address") - } } } @@ -354,14 +316,8 @@ impl Cidr { (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr), #[cfg(feature = "proto-ipv6")] (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr), - #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))] - (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => false, - (_, &Address::Unspecified) => - // a fully unspecified address covers both IPv4 and IPv6, - // and no CIDR block can do that. - { - false - } + #[allow(unreachable_patterns)] + _ => false, } } @@ -373,8 +329,8 @@ impl Cidr { (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other), #[cfg(feature = "proto-ipv6")] (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other), - #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))] - (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false, + #[allow(unreachable_patterns)] + _ => false, } } } @@ -418,28 +374,20 @@ impl defmt::Format for Cidr { /// An internet endpoint address. /// -/// An endpoint can be constructed from a port, in which case the address is unspecified. -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +/// `Endpoint` always fully specifies both the address and the port. +/// +/// See also ['ListenEndpoint'], which allows not specifying the address +/// in order to listen on a given port on any address. +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub struct Endpoint { pub addr: Address, pub port: u16, } impl Endpoint { - /// An endpoint with unspecified address and port. - pub const UNSPECIFIED: Endpoint = Endpoint { - addr: Address::Unspecified, - port: 0, - }; - /// Create an endpoint address from given address and port. pub fn new(addr: Address, port: u16) -> Endpoint { - Endpoint { addr, port } - } - - /// Query whether the endpoint has a specified address and port. - pub fn is_specified(&self) -> bool { - !self.addr.is_unspecified() && self.port != 0 + Endpoint { addr: addr, port } } } @@ -486,19 +434,100 @@ impl defmt::Format for Endpoint { } } -impl From for Endpoint { - fn from(port: u16) -> Endpoint { +impl> From<(T, u16)> for Endpoint { + fn from((addr, port): (T, u16)) -> Endpoint { Endpoint { - addr: Address::Unspecified, + addr: addr.into(), port, } } } -impl> From<(T, u16)> for Endpoint { - fn from((addr, port): (T, u16)) -> Endpoint { - Endpoint { - addr: addr.into(), +/// An internet endpoint address for listening. +/// +/// In contrast with [`Endpoint`], `ListenEndpoint` allows not specifying the address, +/// in order to listen on a given port at all our addresses. +/// +/// An endpoint can be constructed from a port, in which case the address is unspecified. +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +pub struct ListenEndpoint { + pub addr: Option
, + pub port: u16, +} + +impl ListenEndpoint { + /// Query whether the endpoint has a specified address and port. + pub fn is_specified(&self) -> bool { + self.addr.is_some() && self.port != 0 + } +} + +#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))] +impl From<::std::net::SocketAddr> for ListenEndpoint { + fn from(x: ::std::net::SocketAddr) -> ListenEndpoint { + ListenEndpoint { + addr: Some(x.ip().into()), + port: x.port(), + } + } +} + +#[cfg(all(feature = "std", feature = "proto-ipv4"))] +impl From<::std::net::SocketAddrV4> for ListenEndpoint { + fn from(x: ::std::net::SocketAddrV4) -> ListenEndpoint { + ListenEndpoint { + addr: Some((*x.ip()).into()), + port: x.port(), + } + } +} + +#[cfg(all(feature = "std", feature = "proto-ipv6"))] +impl From<::std::net::SocketAddrV6> for ListenEndpoint { + fn from(x: ::std::net::SocketAddrV6) -> ListenEndpoint { + ListenEndpoint { + addr: Some((*x.ip()).into()), + port: x.port(), + } + } +} + +impl fmt::Display for ListenEndpoint { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(addr) = self.addr { + write!(f, "{}:{}", addr, self.port) + } else { + write!(f, "*:{}", self.port) + } + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ListenEndpoint { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{:?}:{=u16}", self.addr, self.port); + } +} + +impl From for ListenEndpoint { + fn from(port: u16) -> ListenEndpoint { + ListenEndpoint { addr: None, port } + } +} + +impl From for ListenEndpoint { + fn from(endpoint: Endpoint) -> ListenEndpoint { + ListenEndpoint { + addr: Some(endpoint.addr), + port: endpoint.port, + } + } +} + +impl> From<(T, u16)> for ListenEndpoint { + fn from((addr, port): (T, u16)) -> ListenEndpoint { + ListenEndpoint { + addr: Some(addr.into()), port, } } @@ -510,7 +539,6 @@ impl> From<(T, u16)> for Endpoint { /// or IPv6 concrete high-level representation. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] pub enum Repr { #[cfg(feature = "proto-ipv4")] Ipv4(Ipv4Repr), @@ -562,6 +590,7 @@ impl Repr { payload_len, hop_limit, }), + #[allow(unreachable_patterns)] _ => panic!("IP version mismatch: src={:?} dst={:?}", src_addr, dst_addr), } } @@ -618,17 +647,11 @@ impl Repr { /// Set the payload length. pub fn set_payload_len(&mut self, length: usize) { - match *self { + match self { #[cfg(feature = "proto-ipv4")] - Repr::Ipv4(Ipv4Repr { - ref mut payload_len, - .. - }) => *payload_len = length, + Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length, #[cfg(feature = "proto-ipv6")] - Repr::Ipv6(Ipv6Repr { - ref mut payload_len, - .. - }) => *payload_len = length, + Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length, } } @@ -759,6 +782,7 @@ pub mod checksum { ]) } + #[allow(unreachable_patterns)] _ => panic!( "Unexpected pseudo header addresses: {}, {}", src_addr, dst_addr @@ -892,11 +916,6 @@ pub(crate) mod test { #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Repr}; - #[test] - fn endpoint_unspecified() { - assert!(!Endpoint::UNSPECIFIED.is_specified()); - } - #[test] #[cfg(feature = "proto-ipv4")] fn to_prefix_len_ipv4() { diff --git a/src/wire/mod.rs b/src/wire/mod.rs index f508a7b9c..3a64b7dfe 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -157,8 +157,9 @@ pub use self::ieee802154::{ }; pub use self::ip::{ - Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol, - Repr as IpRepr, Version as IpVersion, + Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, + ListenEndpoint as IpListenEndpoint, Protocol as IpProtocol, Repr as IpRepr, + Version as IpVersion, }; #[cfg(feature = "proto-ipv4")] From 5db57bcac582cc58eb53bc31347db1812df06f58 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 03:45:29 +0200 Subject: [PATCH 311/566] Clippy fixes. --- src/socket/dhcpv4.rs | 2 -- src/socket/tcp.rs | 2 +- src/socket/udp.rs | 2 +- src/time.rs | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index bf502dc6e..d74cdffde 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -635,8 +635,6 @@ mod test { macro_rules! send { ($socket:ident, $repr:expr) => (send!($socket, time 0, $repr)); - ($socket:ident, $repr:expr, $result:expr) => - (send!($socket, time 0, $repr, $result)); ($socket:ident, time $time:expr, $repr:expr) => (send!($socket, time $time, $repr, Ok(( )))); ($socket:ident, time $time:expr, $repr:expr, $result:expr) => diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 663cda38f..940b1231d 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -106,7 +106,7 @@ impl RttEstimator { fn sample(&mut self, new_rtt: u32) { // "Congestion Avoidance and Control", Van Jacobson, Michael J. Karels, 1988 self.rtt = (self.rtt * 7 + new_rtt + 7) / 8; - let diff = (self.rtt as i32 - new_rtt as i32).abs() as u32; + let diff = (self.rtt as i32 - new_rtt as i32).unsigned_abs(); self.deviation = (self.deviation * 3 + diff + 3) / 4; self.rto_count = 0; diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 802033f62..c72447499 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -300,7 +300,7 @@ impl<'a> UdpSocket<'a> { if self.endpoint.port != repr.dst_port { return false; } - if !self.endpoint.addr.is_none() + if self.endpoint.addr.is_some() && self.endpoint.addr != Some(ip_repr.dst_addr()) && !ip_repr.dst_addr().is_broadcast() && !ip_repr.dst_addr().is_multicast() diff --git a/src/time.rs b/src/time.rs index 6a9961bdd..25694c063 100644 --- a/src/time.rs +++ b/src/time.rs @@ -164,7 +164,7 @@ impl ops::Sub for Instant { type Output = Duration; fn sub(self, rhs: Instant) -> Duration { - Duration::from_micros((self.micros - rhs.micros).abs() as u64) + Duration::from_micros((self.micros - rhs.micros).unsigned_abs()) } } From 833ab44b26235d540301c3e84a570f348d6222c6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 04:38:37 +0200 Subject: [PATCH 312/566] Add bridge teardown instructions. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index e105cfb47..fc920b074 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,14 @@ sudo ip link set br0 up sudo dhcpcd br0 ``` +To tear down: + +``` +sudo killall dhcpcd +sudo ip link set br0 down +sudo brctl delbr br0 +``` + ### Fault injection In order to demonstrate the response of _smoltcp_ to adverse network conditions, all examples From 72d66dd219a7477cffa6eaa78174099b64035b64 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Oct 2021 02:07:38 +0200 Subject: [PATCH 313/566] wire: add DNS --- Cargo.toml | 5 +- src/wire/dns.rs | 785 ++++++++++++++++++++++++++++++++++++++++++++++++ src/wire/mod.rs | 2 + 3 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 src/wire/dns.rs diff --git a/Cargo.toml b/Cargo.toml index 562befd9a..a0b1eea4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,8 @@ verbose = [] "proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] "proto-sixlowpan" = ["proto-ipv6"] - +"proto-dns" = [] + "socket" = [] "socket-raw" = ["socket"] "socket-udp" = ["socket"] @@ -60,7 +61,7 @@ default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", "medium-ieee802154", "phy-raw_socket", "phy-tuntap_interface", - "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", + "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", "proto-dns", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "async" ] diff --git a/src/wire/dns.rs b/src/wire/dns.rs new file mode 100644 index 000000000..b9304904e --- /dev/null +++ b/src/wire/dns.rs @@ -0,0 +1,785 @@ +#![allow(dead_code)] + +use bitflags::bitflags; +use byteorder::{ByteOrder, NetworkEndian}; +use core::iter; +use core::iter::Iterator; + +#[cfg(feature = "proto-ipv4")] +use crate::wire::Ipv4Address; +#[cfg(feature = "proto-ipv6")] +use crate::wire::Ipv6Address; +use crate::{Error, Result}; + +enum_with_unknown! { + /// DNS OpCodes + pub enum Opcode(u8) { + Query = 0x00, + Status = 0x01, + } +} +enum_with_unknown! { + /// DNS OpCodes + pub enum Rcode(u8) { + NoError = 0x00, + FormErr = 0x01, + ServFail = 0x02, + NXDomain = 0x03, + NotImp = 0x04, + Refused = 0x05, + YXDomain = 0x06, + YXRRSet = 0x07, + NXRRSet = 0x08, + NotAuth = 0x09, + NotZone = 0x0a, + } +} + +enum_with_unknown! { + /// DNS record types + pub enum Type(u16) { + A = 0x0001, + Ns = 0x0002, + Cname = 0x0005, + Soa = 0x0006, + Aaaa = 0x001c, + } +} + +bitflags! { + pub struct Flags: u16 { + const RESPONSE = 0b1000_0000_0000_0000; + const AUTHORITATIVE = 0b0000_0100_0000_0000; + const TRUNCATED = 0b0000_0010_0000_0000; + const RECURSION_DESIRED = 0b0000_0001_0000_0000; + const RECURSION_AVAILABLE = 0b0000_0000_1000_0000; + const AUTHENTIC_DATA = 0b0000_0000_0010_0000; + const CHECK_DISABLED = 0b0000_0000_0001_0000; + } +} + +mod field { + use crate::wire::field::*; + + pub const ID: Field = 0..2; + pub const FLAGS: Field = 2..4; + pub const QDCOUNT: Field = 4..6; + pub const ANCOUNT: Field = 6..8; + pub const NSCOUNT: Field = 8..10; + pub const ARCOUNT: Field = 10..12; + + pub const HEADER_END: usize = 12; +} + +// DNS class IN (Internet) +const CLASS_IN: u16 = 1; + +/// A read/write wrapper around a DNS packet buffer. +#[derive(Debug, PartialEq)] +pub struct Packet> { + buffer: T, +} + +impl> Packet { + /// Imbue a raw octet buffer with DNS packet structure. + pub fn new_unchecked(buffer: T) -> Packet { + Packet { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result> { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Malformed)` if the buffer is smaller than + /// the header length. + pub fn check_len(&self) -> Result<()> { + let len = self.buffer.as_ref().len(); + if len < field::HEADER_END { + Err(Error::Malformed) + } else { + Ok(()) + } + } + + /// Consume the packet, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + pub fn payload(&self) -> &[u8] { + &self.buffer.as_ref()[field::HEADER_END..] + } + + pub fn transaction_id(&self) -> u16 { + let field = &self.buffer.as_ref()[field::ID]; + NetworkEndian::read_u16(field) + } + + pub fn flags(&self) -> Flags { + let field = &self.buffer.as_ref()[field::FLAGS]; + Flags::from_bits_truncate(NetworkEndian::read_u16(field)) + } + + pub fn opcode(&self) -> Opcode { + let field = &self.buffer.as_ref()[field::FLAGS]; + let flags = NetworkEndian::read_u16(field); + Opcode::from((flags >> 11 & 0xF) as u8) + } + + pub fn rcode(&self) -> Rcode { + let field = &self.buffer.as_ref()[field::FLAGS]; + let flags = NetworkEndian::read_u16(field); + Rcode::from((flags & 0xF) as u8) + } + + pub fn question_count(&self) -> u16 { + let field = &self.buffer.as_ref()[field::QDCOUNT]; + NetworkEndian::read_u16(field) + } + + pub fn answer_record_count(&self) -> u16 { + let field = &self.buffer.as_ref()[field::ANCOUNT]; + NetworkEndian::read_u16(field) + } + + pub fn authority_record_count(&self) -> u16 { + let field = &self.buffer.as_ref()[field::NSCOUNT]; + NetworkEndian::read_u16(field) + } + + pub fn additional_record_count(&self) -> u16 { + let field = &self.buffer.as_ref()[field::ARCOUNT]; + NetworkEndian::read_u16(field) + } + + /// Parse part of a name from `bytes`, following pointers if any. + pub fn parse_name<'a>(&'a self, mut bytes: &'a [u8]) -> impl Iterator> { + let mut packet = self.buffer.as_ref(); + + iter::from_fn(move || loop { + if bytes.is_empty() { + return Some(Err(Error::Malformed)); + } + match bytes[0] { + 0x00 => return None, + x if x & 0xC0 == 0x00 => { + let len = (x & 0x3F) as usize; + if bytes.len() < 1 + len { + return Some(Err(Error::Malformed)); + } + let label = &bytes[1..1 + len]; + bytes = &bytes[1 + len..]; + return Some(Ok(label)); + } + x if x & 0xC0 == 0xC0 => { + if bytes.len() < 2 { + return Some(Err(Error::Malformed)); + } + let y = bytes[1]; + let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); + if packet.len() <= ptr { + return Some(Err(Error::Malformed)); + } + + // RFC1035 says: "In this scheme, an entire domain name or a list of labels at + // the end of a domain name is replaced with a pointer to a ***prior*** occurance + // of the same name. + // + // Is it unclear if this means the pointer MUST point backwards in the packet or not. Either way, + // pointers that don't point backwards are never seen in the fields, so use this to check that + // there are no pointer loops. + + // Split packet into parts before and after `ptr`. + // parse the part after, keep only the part before in `packet`. This ensure we never + // parse the same byte twice, therefore eliminating pointer loops. + + bytes = &packet[ptr..]; + packet = &packet[..ptr]; + } + _ => return Some(Err(Error::Malformed)), + } + }) + } +} + +impl + AsMut<[u8]>> Packet { + pub fn payload_mut(&mut self) -> &mut [u8] { + let data = self.buffer.as_mut(); + &mut data[field::HEADER_END..] + } + + pub fn set_transaction_id(&mut self, val: u16) { + let field = &mut self.buffer.as_mut()[field::ID]; + NetworkEndian::write_u16(field, val) + } + + pub fn set_flags(&mut self, val: Flags) { + let field = &mut self.buffer.as_mut()[field::FLAGS]; + let mask = Flags::all().bits; + let old = NetworkEndian::read_u16(field); + NetworkEndian::write_u16(field, (old & !mask) | val.bits()); + } + + pub fn set_opcode(&mut self, val: Opcode) { + let field = &mut self.buffer.as_mut()[field::FLAGS]; + let mask = 0x3800; + let val: u8 = val.into(); + let val = (val as u16) << 11; + let old = NetworkEndian::read_u16(field); + NetworkEndian::write_u16(field, (old & !mask) | val); + } + + pub fn set_question_count(&mut self, val: u16) { + let field = &mut self.buffer.as_mut()[field::QDCOUNT]; + NetworkEndian::write_u16(field, val) + } + pub fn set_answer_record_count(&mut self, val: u16) { + let field = &mut self.buffer.as_mut()[field::ANCOUNT]; + NetworkEndian::write_u16(field, val) + } + pub fn set_authority_record_count(&mut self, val: u16) { + let field = &mut self.buffer.as_mut()[field::NSCOUNT]; + NetworkEndian::write_u16(field, val) + } + pub fn set_additional_record_count(&mut self, val: u16) { + let field = &mut self.buffer.as_mut()[field::ARCOUNT]; + NetworkEndian::write_u16(field, val) + } +} + +/// Parse part of a name from `bytes`, not following pointers. +/// Returns the unused part of `bytes`, and the pointer offset if the sequence ends with a pointer. +fn parse_name_part<'a>( + mut bytes: &'a [u8], + mut f: impl FnMut(&'a [u8]), +) -> Result<(&'a [u8], Option)> { + loop { + let x = *bytes.get(0).ok_or(Error::Malformed)?; + bytes = &bytes[1..]; + match x { + 0x00 => return Ok((bytes, None)), + x if x & 0xC0 == 0x00 => { + let len = (x & 0x3F) as usize; + let label = bytes.get(..len).ok_or(Error::Malformed)?; + bytes = &bytes[len..]; + f(label); + } + x if x & 0xC0 == 0xC0 => { + let y = *bytes.get(0).ok_or(Error::Malformed)?; + bytes = &bytes[1..]; + + let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); + return Ok((bytes, Some(ptr))); + } + _ => return Err(Error::Malformed), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct Question<'a> { + pub name: &'a [u8], + pub type_: Type, +} + +impl<'a> Question<'a> { + pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Question<'a>)> { + let (rest, _) = parse_name_part(buffer, |_| ())?; + let name = &buffer[..buffer.len() - rest.len()]; + + if rest.len() < 4 { + return Err(Error::Malformed); + } + let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); + let class = NetworkEndian::read_u16(&rest[2..4]); + let rest = &rest[4..]; + + if class != CLASS_IN { + return Err(Error::Malformed); + } + + Ok((rest, Question { name, type_ })) + } + + /// Return the length of a packet that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + self.name.len() + 4 + } + + /// Emit a high-level representation into a DNS packet. + pub fn emit(&self, packet: &mut [u8]) { + packet[..self.name.len()].copy_from_slice(self.name); + let rest = &mut packet[self.name.len()..]; + NetworkEndian::write_u16(&mut rest[0..2], self.type_.into()); + NetworkEndian::write_u16(&mut rest[2..4], CLASS_IN); + } +} + +#[derive(Debug, PartialEq)] +pub struct Record<'a> { + pub name: &'a [u8], + pub ttl: u32, + pub data: RecordData<'a>, +} + +impl<'a> RecordData<'a> { + pub fn parse(type_: Type, data: &'a [u8]) -> Result> { + match type_ { + Type::A => { + if data.len() != 4 { + return Err(Error::Malformed); + } + Ok(RecordData::A(Ipv4Address::from_bytes(data))) + } + Type::Aaaa => { + if data.len() != 16 { + return Err(Error::Malformed); + } + Ok(RecordData::Aaaa(Ipv6Address::from_bytes(data))) + } + Type::Cname => Ok(RecordData::Cname(data)), + x => Ok(RecordData::Other(x, data)), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum RecordData<'a> { + #[cfg(feature = "proto-ipv4")] + A(Ipv4Address), + #[cfg(feature = "proto-ipv6")] + Aaaa(Ipv6Address), + Cname(&'a [u8]), + Other(Type, &'a [u8]), +} + +impl<'a> Record<'a> { + pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Record<'a>)> { + let (rest, _) = parse_name_part(buffer, |_| ())?; + let name = &buffer[..buffer.len() - rest.len()]; + + if rest.len() < 10 { + return Err(Error::Malformed); + } + let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); + let class = NetworkEndian::read_u16(&rest[2..4]); + let ttl = NetworkEndian::read_u32(&rest[4..8]); + let len = NetworkEndian::read_u16(&rest[8..10]) as usize; + let rest = &rest[10..]; + + if class != CLASS_IN { + return Err(Error::Malformed); + } + + let data = rest.get(..len).ok_or(Error::Malformed)?; + let rest = &rest[len..]; + + Ok(( + rest, + Record { + name, + ttl, + data: RecordData::parse(type_, data)?, + }, + )) + } +} + +/// High-level DNS packet representation. +/// +/// Currently only supports query packets. +#[derive(Debug, PartialEq)] +pub struct Repr<'a> { + pub transaction_id: u16, + pub opcode: Opcode, + pub flags: Flags, + pub question: Question<'a>, +} + +impl<'a> Repr<'a> { + /// Return the length of a packet that will be emitted from this high-level representation. + pub fn buffer_len(&self) -> usize { + field::HEADER_END + self.question.buffer_len() + } + + /// Emit a high-level representation into a DNS packet. + pub fn emit(&self, packet: &mut Packet<&mut T>) + where + T: AsRef<[u8]> + AsMut<[u8]>, + { + packet.set_transaction_id(self.transaction_id); + packet.set_flags(self.flags); + packet.set_opcode(self.opcode); + packet.set_question_count(1); + packet.set_answer_record_count(0); + packet.set_authority_record_count(0); + packet.set_additional_record_count(0); + self.question.emit(packet.payload_mut()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::vec::Vec; + + #[test] + fn test_parse_name() { + let bytes = &[ + 0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, + 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f, + 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, + 0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, + 0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24, + ]; + let packet = Packet::new_unchecked(bytes); + + let name_vec = |bytes| { + let mut v = Vec::new(); + packet + .parse_name(bytes) + .try_for_each(|label| label.map(|label| v.push(label))) + .map(|_| v) + }; + + //assert_eq!(parse_name_len(bytes, 0x0c), Ok(18)); + assert_eq!( + name_vec(&bytes[0x0c..]), + Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]]) + ); + //assert_eq!(parse_name_len(bytes, 0x22), Ok(2)); + assert_eq!( + name_vec(&bytes[0x22..]), + Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]]) + ); + //assert_eq!(parse_name_len(bytes, 0x2e), Ok(17)); + assert_eq!( + name_vec(&bytes[0x2e..]), + Ok(vec![ + &b"star-mini"[..], + &b"c10r"[..], + &b"facebook"[..], + &b"com"[..] + ]) + ); + //assert_eq!(parse_name_len(bytes, 0x3f), Ok(2)); + assert_eq!( + name_vec(&bytes[0x3f..]), + Ok(vec![ + &b"star-mini"[..], + &b"c10r"[..], + &b"facebook"[..], + &b"com"[..] + ]) + ); + } + + struct Parsed<'a> { + packet: Packet<&'a [u8]>, + questions: Vec>, + answers: Vec>, + authorities: Vec>, + additionals: Vec>, + } + + impl<'a> Parsed<'a> { + fn parse(bytes: &'a [u8]) -> Result { + let packet = Packet::new_unchecked(bytes); + let mut questions = Vec::new(); + let mut answers = Vec::new(); + let mut authorities = Vec::new(); + let mut additionals = Vec::new(); + + let mut payload = &bytes[12..]; + + for _ in 0..packet.question_count() { + let (p, r) = Question::parse(payload)?; + questions.push(r); + payload = p; + } + for _ in 0..packet.answer_record_count() { + let (p, r) = Record::parse(payload)?; + answers.push(r); + payload = p; + } + for _ in 0..packet.authority_record_count() { + let (p, r) = Record::parse(payload)?; + authorities.push(r); + payload = p; + } + for _ in 0..packet.additional_record_count() { + let (p, r) = Record::parse(payload)?; + additionals.push(r); + payload = p; + } + + // Check that there are no bytes left + assert_eq!(payload.len(), 0); + + Ok(Parsed { + packet, + questions, + answers, + authorities, + additionals, + }) + } + } + + #[test] + fn test_parse_request() { + let p = Parsed::parse(&[ + 0x51, 0x84, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, + ]) + .unwrap(); + + assert_eq!(p.packet.transaction_id(), 0x5184); + assert_eq!( + p.packet.flags(), + Flags::RECURSION_DESIRED | Flags::AUTHENTIC_DATA + ); + assert_eq!(p.packet.opcode(), Opcode::Query); + assert_eq!(p.packet.question_count(), 1); + assert_eq!(p.packet.answer_record_count(), 0); + assert_eq!(p.packet.authority_record_count(), 0); + assert_eq!(p.packet.additional_record_count(), 0); + + assert_eq!(p.questions.len(), 1); + assert_eq!( + p.questions[0].name, + &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00] + ); + assert_eq!(p.questions[0].type_, Type::A); + + assert_eq!(p.answers.len(), 0); + assert_eq!(p.authorities.len(), 0); + assert_eq!(p.additionals.len(), 0); + } + + #[test] + fn test_parse_response() { + let p = Parsed::parse(&[ + 0x51, 0x84, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, + 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xca, 0x00, 0x04, 0xac, 0xd9, + 0xa8, 0xae, + ]) + .unwrap(); + + assert_eq!(p.packet.transaction_id(), 0x5184); + assert_eq!( + p.packet.flags(), + Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE + ); + assert_eq!(p.packet.opcode(), Opcode::Query); + assert_eq!(p.packet.rcode(), Rcode::NoError); + assert_eq!(p.packet.question_count(), 1); + assert_eq!(p.packet.answer_record_count(), 1); + assert_eq!(p.packet.authority_record_count(), 0); + assert_eq!(p.packet.additional_record_count(), 0); + + assert_eq!( + p.questions[0].name, + &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00] + ); + assert_eq!(p.questions[0].type_, Type::A); + + assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[0].ttl, 202); + assert_eq!( + p.answers[0].data, + RecordData::A(Ipv4Address::new(0xac, 0xd9, 0xa8, 0xae)) + ); + } + + #[test] + fn test_parse_response_multiple_a() { + let p = Parsed::parse(&[ + 0x4b, 0x9e, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72, + 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, + 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x04, 0x0d, 0xe0, 0x77, 0x35, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x28, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x43, 0xc0, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x62, + ]) + .unwrap(); + + assert_eq!(p.packet.transaction_id(), 0x4b9e); + assert_eq!( + p.packet.flags(), + Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE + ); + assert_eq!(p.packet.opcode(), Opcode::Query); + assert_eq!(p.packet.rcode(), Rcode::NoError); + assert_eq!(p.packet.question_count(), 1); + assert_eq!(p.packet.answer_record_count(), 4); + assert_eq!(p.packet.authority_record_count(), 0); + assert_eq!(p.packet.additional_record_count(), 0); + + assert_eq!( + p.questions[0].name, + &[ + 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, + 0x00 + ] + ); + assert_eq!(p.questions[0].type_, Type::A); + + assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[0].ttl, 9); + assert_eq!( + p.answers[0].data, + RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x35)) + ); + + assert_eq!(p.answers[1].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[1].ttl, 9); + assert_eq!( + p.answers[1].data, + RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x28)) + ); + + assert_eq!(p.answers[2].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[2].ttl, 9); + assert_eq!( + p.answers[2].data, + RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x43)) + ); + + assert_eq!(p.answers[3].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[3].ttl, 9); + assert_eq!( + p.answers[3].data, + RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x62)) + ); + } + + #[test] + fn test_parse_response_cname() { + let p = Parsed::parse(&[ + 0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, + 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f, + 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, + 0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, + 0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24, + ]) + .unwrap(); + + assert_eq!(p.packet.transaction_id(), 0x786c); + assert_eq!( + p.packet.flags(), + Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE + ); + assert_eq!(p.packet.opcode(), Opcode::Query); + assert_eq!(p.packet.rcode(), Rcode::NoError); + assert_eq!(p.packet.question_count(), 1); + assert_eq!(p.packet.answer_record_count(), 2); + assert_eq!(p.packet.authority_record_count(), 0); + assert_eq!(p.packet.additional_record_count(), 0); + + assert_eq!( + p.questions[0].name, + &[ + 0x03, 0x77, 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, + 0x63, 0x6f, 0x6d, 0x00 + ] + ); + assert_eq!(p.questions[0].type_, Type::A); + + // cname + assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); + assert_eq!(p.answers[0].ttl, 1523); + assert_eq!( + p.answers[0].data, + RecordData::Cname(&[ + 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, 0x04, 0x63, 0x31, 0x30, + 0x72, 0xc0, 0x10 + ]) + ); + // a + assert_eq!(p.answers[1].name, &[0xc0, 0x2e]); + assert_eq!(p.answers[1].ttl, 5); + assert_eq!( + p.answers[1].data, + RecordData::A(Ipv4Address::new(0x1f, 0x0d, 0x53, 0x24)) + ); + } + + #[test] + fn test_parse_response_nxdomain() { + let p = Parsed::parse(&[ + 0x63, 0xc4, 0x81, 0x83, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x61, + 0x68, 0x61, 0x73, 0x64, 0x67, 0x68, 0x6c, 0x61, 0x6b, 0x73, 0x6a, 0x68, 0x62, 0x61, + 0x61, 0x73, 0x6c, 0x64, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, + 0x20, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x3d, 0x01, 0x61, 0x0c, + 0x67, 0x74, 0x6c, 0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x03, 0x6e, + 0x65, 0x74, 0x00, 0x05, 0x6e, 0x73, 0x74, 0x6c, 0x64, 0x0c, 0x76, 0x65, 0x72, 0x69, + 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x67, 0x72, 0x73, 0xc0, 0x20, 0x5f, 0xce, 0x8b, 0x85, + 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x03, 0x84, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, + 0x51, 0x80, + ]) + .unwrap(); + + assert_eq!(p.packet.transaction_id(), 0x63c4); + assert_eq!( + p.packet.flags(), + Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE + ); + assert_eq!(p.packet.opcode(), Opcode::Query); + assert_eq!(p.packet.rcode(), Rcode::NXDomain); + assert_eq!(p.packet.question_count(), 1); + assert_eq!(p.packet.answer_record_count(), 0); + assert_eq!(p.packet.authority_record_count(), 1); + assert_eq!(p.packet.additional_record_count(), 0); + + assert_eq!(p.questions[0].type_, Type::A); + + // SOA authority + assert_eq!(p.authorities[0].name, &[0xc0, 0x20]); // com. + assert_eq!(p.authorities[0].ttl, 899); + assert!(matches!( + p.authorities[0].data, + RecordData::Other(Type::Soa, _) + )); + } + + #[test] + fn test_emit() { + let name = &[ + 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, + 0x00, + ]; + + let repr = Repr { + transaction_id: 0x1234, + flags: Flags::RECURSION_DESIRED, + opcode: Opcode::Query, + question: Question { + name, + type_: Type::A, + }, + }; + + let mut buf = Vec::new(); + buf.resize(repr.buffer_len(), 0); + repr.emit(&mut Packet::new_unchecked(&mut buf)); + + let want = &[ + 0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72, + 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, + 0x01, 0x00, 0x01, + ]; + assert_eq!(&buf, want); + } +} diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 3a64b7dfe..f320f3178 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -81,6 +81,8 @@ pub mod pretty_print; mod arp; #[cfg(feature = "proto-dhcpv4")] pub(crate) mod dhcpv4; +#[cfg(feature = "proto-dns")] +pub(crate) mod dns; #[cfg(feature = "medium-ethernet")] mod ethernet; #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] From da1a2b2df0eafebb7fb92c00e56e88d533daa446 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Oct 2021 02:14:53 +0200 Subject: [PATCH 314/566] socket: add DNS --- Cargo.toml | 8 +- examples/dns.rs | 102 ++++++++++ src/iface/interface.rs | 29 ++- src/rand.rs | 14 ++ src/socket/dns.rs | 411 +++++++++++++++++++++++++++++++++++++++++ src/socket/mod.rs | 10 + src/time.rs | 2 + 7 files changed, 571 insertions(+), 5 deletions(-) create mode 100644 examples/dns.rs create mode 100644 src/socket/dns.rs diff --git a/Cargo.toml b/Cargo.toml index a0b1eea4e..07e0b005d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } cfg-if = "1.0.0" +heapless = "0.7.7" [dev-dependencies] env_logger = "0.9" @@ -54,6 +55,7 @@ verbose = [] "socket-tcp" = ["socket"] "socket-icmp" = ["socket"] "socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"] +"socket-dns" = ["socket", "proto-dns"] "async" = [] @@ -62,7 +64,7 @@ default = [ "medium-ethernet", "medium-ip", "medium-ieee802154", "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", "proto-dns", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", + "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "async" ] @@ -114,5 +116,9 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac name = "sixlowpan" required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] +[[example]] +name = "dns" +required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-dns"] + [profile.release] debug = 2 diff --git a/examples/dns.rs b/examples/dns.rs new file mode 100644 index 000000000..5180b9863 --- /dev/null +++ b/examples/dns.rs @@ -0,0 +1,102 @@ +#[macro_use] +extern crate log; +extern crate byteorder; +extern crate env_logger; +extern crate getopts; +extern crate smoltcp; + +mod utils; + +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::phy::Device; +use smoltcp::phy::{wait as phy_wait, Medium}; +use smoltcp::socket::DnsSocket; +use smoltcp::time::Instant; +use smoltcp::wire::{ + EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, +}; +use smoltcp::Error; +use std::collections::BTreeMap; +use std::os::unix::io::AsRawFd; + +fn main() { + utils::setup_logging("warn"); + + let (mut opts, mut free) = utils::create_options(); + utils::add_tuntap_options(&mut opts, &mut free); + utils::add_middleware_options(&mut opts, &mut free); + + let mut matches = utils::parse_options(&opts, free); + let device = utils::parse_tuntap_options(&mut matches); + let fd = device.as_raw_fd(); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + + let neighbor_cache = NeighborCache::new(BTreeMap::new()); + + let servers = vec![ + Ipv4Address::new(8, 8, 4, 4).into(), + Ipv4Address::new(8, 8, 8, 8).into(), + ]; + let dns_socket = DnsSocket::new(servers, vec![]); + + let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); + let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); + let ip_addrs = [ + IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), + IpCidr::new(src_ipv6, 64), + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); + let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); + let mut routes_storage = [None; 2]; + let mut routes = Routes::new(&mut routes_storage[..]); + routes.add_default_ipv4_route(default_v4_gw).unwrap(); + routes.add_default_ipv6_route(default_v6_gw).unwrap(); + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device, vec![]) + .ip_addrs(ip_addrs) + .routes(routes); + if medium == Medium::Ethernet { + builder = builder + .hardware_addr(HardwareAddress::Ethernet(ethernet_addr)) + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); + + let dns_handle = iface.add_socket(dns_socket); + + //let name = b"\x08facebook\x03com\x00"; + //let name = b"\x03www\x08facebook\x03com\x00"; + //let name = b"\x06reddit\x03com\x00"; + let name = b"\x09rust-lang\x03org\x00"; + + let (socket, cx) = iface.get_socket_and_context::(dns_handle); + let query = socket.start_query(cx, name).unwrap(); + + loop { + let timestamp = Instant::now(); + debug!("timestamp {:?}", timestamp); + + match iface.poll(timestamp) { + Ok(_) => {} + Err(e) => { + debug!("poll error: {}", e); + } + } + + match iface + .get_socket::(dns_handle) + .get_query_result(query) + { + Ok(addrs) => { + println!("Query done: {:?}", addrs); + break; + } + Err(Error::Exhausted) => {} // not done yet + Err(e) => panic!("query failed: {:?}", e), + } + + phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + } +} diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ea2aec51a..cf3972b1f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -365,7 +365,7 @@ pub(crate) enum IpPacket<'a> { Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)), #[cfg(feature = "socket-raw")] Raw((IpRepr, &'a [u8])), - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] Udp((IpRepr, UdpRepr, &'a [u8])), #[cfg(feature = "socket-tcp")] Tcp((IpRepr, TcpRepr<'a>)), @@ -417,7 +417,7 @@ impl<'a> IpPacket<'a> { ), #[cfg(feature = "socket-raw")] IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit( &mut UdpPacket::new_unchecked(payload), &_ip_repr.src_addr(), @@ -944,6 +944,10 @@ where Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { respond!(inner, IpPacket::Dhcpv4(response)) }), + #[cfg(feature = "socket-dns")] + Socket::Dns(ref mut socket) => socket.dispatch(inner, |inner, response| { + respond!(inner, IpPacket::Udp(response)) + }), }; match (device_result, socket_result) { @@ -1575,7 +1579,7 @@ impl<'a> InterfaceInner<'a> { match nxt_hdr { IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload), - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpProtocol::Udp => { self.process_udp(sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload) } @@ -2117,7 +2121,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] fn process_udp<'frame>( &mut self, sockets: &mut SocketSet, @@ -2146,6 +2150,23 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "socket-dns")] + for dns_socket in sockets + .iter_mut() + .filter_map(|i| DnsSocket::downcast(&mut i.socket)) + { + if !dns_socket.accepts(&ip_repr, &udp_repr) { + continue; + } + + match dns_socket.process(self, &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. + Err(e) => return Err(e), + } + } + // The packet wasn't handled by a socket, send an ICMP port unreachable packet. match ip_repr { #[cfg(feature = "proto-ipv4")] diff --git a/src/rand.rs b/src/rand.rs index 5c3bacd03..15d88f77e 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -23,4 +23,18 @@ impl Rand { let shift = 29 - (s >> 61); (s >> shift) as u32 } + + pub(crate) fn rand_u16(&mut self) -> u16 { + let n = self.rand_u32(); + (n ^ (n >> 16)) as u16 + } + + pub(crate) fn rand_source_port(&mut self) -> u16 { + loop { + let res = self.rand_u16(); + if res > 1024 { + return res; + } + } + } } diff --git a/src/socket/dns.rs b/src/socket/dns.rs new file mode 100644 index 000000000..a196d4134 --- /dev/null +++ b/src/socket/dns.rs @@ -0,0 +1,411 @@ +#![allow(dead_code, unused)] +use heapless::Vec; +use managed::ManagedSlice; + +use crate::socket::{Context, PollAt, Socket}; +use crate::time::{Duration, Instant}; +use crate::wire::dns::{Flags, Opcode, Packet, Question, Record, RecordData, Repr, Type}; +use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr, Ipv4Address, UdpRepr}; +use crate::{rand, Error, Result}; + +const DNS_PORT: u16 = 53; +const MAX_NAME_LEN: usize = 255; +const MAX_ADDRESS_COUNT: usize = 4; +const RETRANSMIT_DELAY: Duration = Duration::from_millis(1000); +const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10000); + +/// State for an in-progress DNS query. +/// +/// The only reason this struct is public is to allow the socket state +/// to be allocated externally. +#[derive(Debug)] +pub struct DnsQuery { + state: State, +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +enum State { + Pending(PendingQuery), + Completed(CompletedQuery), +} + +#[derive(Debug)] +struct PendingQuery { + name: Vec, + type_: Type, + + port: u16, // UDP port (src for request, dst for response) + txid: u16, // transaction ID + + retransmit_at: Instant, + delay: Duration, +} + +#[derive(Debug)] +struct CompletedQuery { + addresses: Vec, +} + +/// A handle to an in-progress DNS query. +#[derive(Clone, Copy)] +pub struct QueryHandle(usize); + +/// A Domain Name System socket. +/// +/// A UDP socket is bound to a specific endpoint, and owns transmit and receive +/// packet buffers. +#[derive(Debug)] +pub struct DnsSocket<'a> { + servers: ManagedSlice<'a, IpAddress>, + queries: ManagedSlice<'a, Option>, + + /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. + hop_limit: Option, +} + +impl<'a> DnsSocket<'a> { + /// Create a DNS socket with the given buffers. + pub fn new(servers: S, queries: Q) -> DnsSocket<'a> + where + S: Into>, + Q: Into>>, + { + DnsSocket { + servers: servers.into(), + queries: queries.into(), + hop_limit: None, + } + } + + /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. + /// + /// See also the [set_hop_limit](#method.set_hop_limit) method + pub fn hop_limit(&self) -> Option { + self.hop_limit + } + + /// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. + /// + /// A socket without an explicitly set hop limit value uses the default [IANA recommended] + /// value (64). + /// + /// # Panics + /// + /// This function panics if a hop limit value of 0 is given. See [RFC 1122 § 3.2.1.7]. + /// + /// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml + /// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7 + pub fn set_hop_limit(&mut self, hop_limit: Option) { + // A host MUST NOT send a datagram with a hop limit value of 0 + if let Some(0) = hop_limit { + panic!("the time-to-live value of a packet must not be zero") + } + + self.hop_limit = hop_limit + } + + fn find_free_query(&mut self) -> Result { + for (i, q) in self.queries.iter().enumerate() { + if q.is_none() { + return Ok(QueryHandle(i)); + } + } + + match self.queries { + ManagedSlice::Borrowed(_) => Err(Error::Exhausted), + #[cfg(any(feature = "std", feature = "alloc"))] + ManagedSlice::Owned(ref mut queries) => { + queries.push(None); + let index = queries.len() - 1; + Ok(QueryHandle(index)) + } + } + } + + pub fn start_query(&mut self, cx: &mut Context, name: &[u8]) -> Result { + let handle = self.find_free_query()?; + + self.queries[handle.0] = Some(DnsQuery { + state: State::Pending(PendingQuery { + name: Vec::from_slice(name).map_err(|_| Error::Truncated)?, + type_: Type::A, + txid: cx.rand().rand_u16(), + port: cx.rand().rand_source_port(), + delay: RETRANSMIT_DELAY, + retransmit_at: Instant::ZERO, + }), + }); + Ok(handle) + } + + pub fn get_query_result( + &mut self, + handle: QueryHandle, + ) -> Result> { + let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; + let q = slot.as_mut().ok_or(Error::Illegal)?; + match &mut q.state { + // Query is not done yet. + State::Pending(_) => Err(Error::Exhausted), + // Query is done + State::Completed(q) => { + let res = q.addresses.clone(); + *slot = None; // Free up the slot for recycling. + Ok(res) + } + } + } + + pub fn cancel_query(&mut self, handle: QueryHandle) -> Result<()> { + let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; + let q = slot.as_mut().ok_or(Error::Illegal)?; + *slot = None; // Free up the slot for recycling. + Ok(()) + } + + pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool { + udp_repr.src_port == DNS_PORT + && self + .servers + .iter() + .any(|server| *server == ip_repr.src_addr()) + } + + pub(crate) fn process( + &mut self, + cx: &mut Context, + ip_repr: &IpRepr, + udp_repr: &UdpRepr, + payload: &[u8], + ) -> Result<()> { + debug_assert!(self.accepts(ip_repr, udp_repr)); + + let size = payload.len(); + + net_trace!( + "receiving {} octets from {:?}:{}", + size, + ip_repr.src_addr(), + udp_repr.dst_port + ); + + let p = Packet::new_checked(payload)?; + if p.opcode() != Opcode::Query { + net_trace!("unwanted opcode {:?}", p.opcode()); + return Err(Error::Malformed); + } + + if !p.flags().contains(Flags::RESPONSE) { + net_trace!("packet doesn't have response bit set"); + return Err(Error::Malformed); + } + + if p.question_count() != 1 { + net_trace!("bad question count {:?}", p.question_count()); + return Err(Error::Malformed); + } + + // Find pending query + for q in self.queries.iter_mut().flatten() { + if let State::Pending(pq) = &mut q.state { + if udp_repr.dst_port != pq.port || p.transaction_id() != pq.txid { + continue; + } + + let payload = p.payload(); + let (mut payload, question) = Question::parse(payload)?; + + if question.type_ != pq.type_ { + net_trace!("question type mismatch"); + return Err(Error::Malformed); + } + if !eq_names(p.parse_name(question.name), p.parse_name(&pq.name))? { + net_trace!("question name mismatch"); + return Err(Error::Malformed); + } + + let mut addresses = Vec::new(); + + for _ in 0..p.answer_record_count() { + let (payload2, r) = Record::parse(payload)?; + payload = payload2; + + if !eq_names(p.parse_name(r.name), p.parse_name(&pq.name))? { + net_trace!("answer name mismatch: {:?}", r); + continue; + } + + match r.data { + RecordData::A(addr) => { + net_trace!("A: {:?}", addr); + if addresses.push(addr.into()).is_err() { + net_trace!("too many addresses in response, ignoring {:?}", addr); + } + } + RecordData::Aaaa(addr) => { + net_trace!("AAAA: {:?}", addr); + if addresses.push(addr.into()).is_err() { + net_trace!("too many addresses in response, ignoring {:?}", addr); + } + } + RecordData::Cname(name) => { + net_trace!("CNAME: {:?}", name); + copy_name(&mut pq.name, p.parse_name(name))?; + + // Relaunch query with the new name. + // If the server has bundled A records for the CNAME in the same packet, + // we'll process them in next iterations, and cancel the query relaunch. + pq.retransmit_at = Instant::ZERO; + pq.delay = RETRANSMIT_DELAY; + + pq.txid = cx.rand().rand_u16(); + pq.port = cx.rand().rand_source_port(); + } + RecordData::Other(type_, data) => { + net_trace!("unknown: {:?} {:?}", type_, data) + } + } + } + + if !addresses.is_empty() { + q.state = State::Completed(CompletedQuery { addresses }) + } + // If we get here, packet matched the current query, stop processing. + return Ok(()); + } + } + + // If we get here, packet matched with no query. + net_trace!("no query matched"); + Ok(()) + } + + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + where + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<()>, + { + let hop_limit = self.hop_limit.unwrap_or(64); + + for q in self.queries.iter_mut().flatten() { + if let State::Pending(pq) = &mut q.state { + if pq.retransmit_at > cx.now() { + // query is waiting for retransmit + continue; + } + + let repr = Repr { + transaction_id: pq.txid, + flags: Flags::RECURSION_DESIRED, + opcode: Opcode::Query, + question: Question { + name: &pq.name, + type_: Type::A, + }, + }; + + let mut payload = [0u8; 512]; + let payload = &mut payload[..repr.buffer_len()]; + repr.emit(&mut Packet::new_unchecked(payload)); + + let udp_repr = UdpRepr { + src_port: pq.port, + dst_port: 53, + }; + + let dst_addr = self.servers[0]; + let src_addr = cx.get_source_address(dst_addr).unwrap(); // TODO remove unwrap + let ip_repr = IpRepr::new( + src_addr, + dst_addr, + IpProtocol::Udp, + udp_repr.header_len() + payload.len(), + hop_limit, + ); + + net_trace!( + "sending {} octets to {:?}:{}", + payload.len(), + ip_repr.dst_addr(), + udp_repr.src_port + ); + + if let Err(e) = emit(cx, (ip_repr, udp_repr, payload)) { + net_trace!("DNS emit error {:?}", e); + return Ok(()); + } + + pq.retransmit_at = cx.now() + pq.delay; + pq.delay = MAX_RETRANSMIT_DELAY.min(pq.delay * 2); + + return Ok(()); + } + } + + // Nothing to dispatch + Err(Error::Exhausted) + } + + pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { + self.queries + .iter() + .flatten() + .filter_map(|q| match &q.state { + State::Pending(pq) => Some(PollAt::Time(pq.retransmit_at)), + State::Completed(_) => None, + }) + .min() + .unwrap_or(PollAt::Ingress) + } +} + +impl<'a> From> for Socket<'a> { + fn from(val: DnsSocket<'a>) -> Self { + Socket::Dns(val) + } +} + +fn eq_names<'a>( + mut a: impl Iterator>, + mut b: impl Iterator>, +) -> Result { + loop { + match (a.next(), b.next()) { + // Handle errors + (Some(Err(e)), _) => return Err(e), + (_, Some(Err(e))) => return Err(e), + + // Both finished -> equal + (None, None) => return Ok(true), + + // One finished before the other -> not equal + (None, _) => return Ok(false), + (_, None) => return Ok(false), + + // Got two labels, check if they're equal + (Some(Ok(la)), Some(Ok(lb))) => { + if la != lb { + return Ok(false); + } + } + } + } +} + +fn copy_name<'a, const N: usize>( + dest: &mut Vec, + name: impl Iterator>, +) -> Result<()> { + dest.truncate(0); + + for label in name { + let label = label?; + dest.push(label.len() as u8).map_err(|_| Error::Truncated); + dest.extend_from_slice(label).map_err(|_| Error::Truncated); + } + + // Write terminator 0x00 + dest.push(0).map_err(|_| Error::Truncated); + + Ok(()) +} diff --git a/src/socket/mod.rs b/src/socket/mod.rs index d8415c0e3..8bf48bc4a 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -16,6 +16,8 @@ use crate::time::Instant; #[cfg(feature = "socket-dhcpv4")] mod dhcpv4; +#[cfg(feature = "socket-dns")] +mod dns; #[cfg(feature = "socket-icmp")] mod icmp; #[cfg(feature = "socket-raw")] @@ -30,6 +32,8 @@ mod waker; #[cfg(feature = "socket-dhcpv4")] pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; +#[cfg(feature = "socket-dns")] +pub use self::dns::{DnsQuery, DnsSocket}; #[cfg(feature = "socket-icmp")] pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; #[cfg(feature = "socket-raw")] @@ -76,6 +80,8 @@ pub enum Socket<'a> { Tcp(TcpSocket<'a>), #[cfg(feature = "socket-dhcpv4")] Dhcpv4(Dhcpv4Socket), + #[cfg(feature = "socket-dns")] + Dns(DnsSocket<'a>), } impl<'a> Socket<'a> { @@ -91,6 +97,8 @@ impl<'a> Socket<'a> { Socket::Tcp(s) => s.poll_at(cx), #[cfg(feature = "socket-dhcpv4")] Socket::Dhcpv4(s) => s.poll_at(cx), + #[cfg(feature = "socket-dns")] + Socket::Dns(s) => s.poll_at(cx), } } } @@ -129,3 +137,5 @@ from_socket!(UdpSocket<'a>, Udp); from_socket!(TcpSocket<'a>, Tcp); #[cfg(feature = "socket-dhcpv4")] from_socket!(Dhcpv4Socket, Dhcpv4); +#[cfg(feature = "socket-dns")] +from_socket!(DnsSocket<'a>, Dns); diff --git a/src/time.rs b/src/time.rs index 25694c063..c91b40559 100644 --- a/src/time.rs +++ b/src/time.rs @@ -28,6 +28,8 @@ pub struct Instant { } impl Instant { + pub const ZERO: Instant = Instant::from_micros_const(0); + /// Create a new `Instant` from a number of microseconds. pub fn from_micros>(micros: T) -> Instant { Instant { From bf620615f9ab17324d4ec9e9361a54b7041a105e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 19 May 2022 19:15:45 +0200 Subject: [PATCH 315/566] DnsSocket: Be able to update servers --- src/socket/dns.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index a196d4134..e16aa3f21 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -78,6 +78,14 @@ impl<'a> DnsSocket<'a> { } } + /// Update the managed slice with servers + pub fn update_servers(&mut self, servers: &[IpAddress]) { + self.servers + .iter_mut() + .zip(servers.iter()) + .for_each(|(a, b)| *a = *b); + } + /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. /// /// See also the [set_hop_limit](#method.set_hop_limit) method From 1efe5d50da4a699db6af6df20d51e443385fedee Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 15 Nov 2021 16:18:19 +0100 Subject: [PATCH 316/566] defmt --- src/socket/dns.rs | 12 +++++++++--- src/wire/dns.rs | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index e16aa3f21..1c709b1fc 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -78,12 +78,18 @@ impl<'a> DnsSocket<'a> { } } - /// Update the managed slice with servers + /// Update the list of DNS servers, will replace all existing servers pub fn update_servers(&mut self, servers: &[IpAddress]) { - self.servers - .iter_mut() + let mut local_servers = self.servers.iter_mut(); + local_servers + .by_ref() .zip(servers.iter()) .for_each(|(a, b)| *a = *b); + + // Fill the rest with no address + for s in local_servers { + *s = IpAddress::Unspecified; + } } /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. diff --git a/src/wire/dns.rs b/src/wire/dns.rs index b9304904e..c87d7500d 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -47,6 +47,7 @@ enum_with_unknown! { } bitflags! { + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Flags: u16 { const RESPONSE = 0b1000_0000_0000_0000; const AUTHORITATIVE = 0b0000_0100_0000_0000; @@ -284,6 +285,7 @@ fn parse_name_part<'a>( } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Question<'a> { pub name: &'a [u8], pub type_: Type, @@ -323,6 +325,7 @@ impl<'a> Question<'a> { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Record<'a> { pub name: &'a [u8], pub ttl: u32, @@ -351,6 +354,7 @@ impl<'a> RecordData<'a> { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RecordData<'a> { #[cfg(feature = "proto-ipv4")] A(Ipv4Address), @@ -396,6 +400,7 @@ impl<'a> Record<'a> { /// /// Currently only supports query packets. #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub transaction_id: u16, pub opcode: Opcode, From 5fdae13164d246bc029bf67187c824e2e2689b28 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 17 Nov 2021 16:59:56 +0100 Subject: [PATCH 317/566] Make Dns's QueryHandle public --- src/socket/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 8bf48bc4a..bf65add25 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -33,7 +33,7 @@ mod waker; #[cfg(feature = "socket-dhcpv4")] pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; #[cfg(feature = "socket-dns")] -pub use self::dns::{DnsQuery, DnsSocket}; +pub use self::dns::{DnsQuery, DnsSocket, QueryHandle as DnsQueryHandle}; #[cfg(feature = "socket-icmp")] pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; #[cfg(feature = "socket-raw")] From dd18f3c4506f97848cb99217bdac57b535b98fe4 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 18 Nov 2021 16:52:24 +0100 Subject: [PATCH 318/566] dns: Added timeout and trying to use different servers --- Cargo.toml | 2 +- src/socket/dns.rs | 49 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 07e0b005d..13e3ba454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ verbose = [] "proto-ipv6" = [] "proto-sixlowpan" = ["proto-ipv6"] "proto-dns" = [] - + "socket" = [] "socket-raw" = ["socket"] "socket-udp" = ["socket"] diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 1c709b1fc..6758fe4e7 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -11,8 +11,9 @@ use crate::{rand, Error, Result}; const DNS_PORT: u16 = 53; const MAX_NAME_LEN: usize = 255; const MAX_ADDRESS_COUNT: usize = 4; -const RETRANSMIT_DELAY: Duration = Duration::from_millis(1000); -const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10000); +const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); +const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); +const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs /// State for an in-progress DNS query. /// @@ -28,6 +29,7 @@ pub struct DnsQuery { enum State { Pending(PendingQuery), Completed(CompletedQuery), + Failure, } #[derive(Debug)] @@ -38,8 +40,11 @@ struct PendingQuery { port: u16, // UDP port (src for request, dst for response) txid: u16, // transaction ID + timeout_at: Option, retransmit_at: Instant, delay: Duration, + + server_idx: usize, } #[derive(Debug)] @@ -88,7 +93,7 @@ impl<'a> DnsSocket<'a> { // Fill the rest with no address for s in local_servers { - *s = IpAddress::Unspecified; + *s = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); // TODO } } @@ -147,7 +152,9 @@ impl<'a> DnsSocket<'a> { txid: cx.rand().rand_u16(), port: cx.rand().rand_source_port(), delay: RETRANSMIT_DELAY, + timeout_at: None, retransmit_at: Instant::ZERO, + server_idx: 0, }), }); Ok(handle) @@ -168,6 +175,10 @@ impl<'a> DnsSocket<'a> { *slot = None; // Free up the slot for recycling. Ok(res) } + State::Failure => { + *slot = None; // Free up the slot for recycling. + Err(Error::Unaddressable) + } } } @@ -303,6 +314,35 @@ impl<'a> DnsSocket<'a> { for q in self.queries.iter_mut().flatten() { if let State::Pending(pq) = &mut q.state { + let timeout = if let Some(timeout) = pq.timeout_at { + timeout + } else { + let v = cx.now() + RETRANSMIT_TIMEOUT; + pq.timeout_at = Some(v); + v + }; + + // Check timeout + if timeout < cx.now() { + pq.server_idx += 1; + if pq.server_idx < self.servers.len() { + // DNS timeout + pq.timeout_at = Some(cx.now() + RETRANSMIT_TIMEOUT); + pq.retransmit_at = Instant::ZERO; + pq.delay = RETRANSMIT_DELAY; + } else { + // Query failure + q.state = State::Failure; + continue; + } + } + + // Check so the IP address is valid + if self.servers[pq.server_idx].is_unspecified() { + q.state = State::Failure; + continue; + } + if pq.retransmit_at > cx.now() { // query is waiting for retransmit continue; @@ -327,7 +367,7 @@ impl<'a> DnsSocket<'a> { dst_port: 53, }; - let dst_addr = self.servers[0]; + let dst_addr = self.servers[pq.server_idx]; let src_addr = cx.get_source_address(dst_addr).unwrap(); // TODO remove unwrap let ip_repr = IpRepr::new( src_addr, @@ -367,6 +407,7 @@ impl<'a> DnsSocket<'a> { .filter_map(|q| match &q.state { State::Pending(pq) => Some(PollAt::Time(pq.retransmit_at)), State::Completed(_) => None, + State::Failure => None, }) .min() .unwrap_or(PollAt::Ingress) From 5aff1c257ad07df2b172a4dede60a6172954c712 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 19 Nov 2021 09:21:27 +0100 Subject: [PATCH 319/566] dns: Failure if no addresses are received --- Cargo.toml | 2 +- src/socket/dns.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 13e3ba454..058eef420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } cfg-if = "1.0.0" -heapless = "0.7.7" +heapless = "0.7.8" [dev-dependencies] env_logger = "0.9" diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 6758fe4e7..8f1ccda7b 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -245,6 +245,7 @@ impl<'a> DnsSocket<'a> { net_trace!("question type mismatch"); return Err(Error::Malformed); } + if !eq_names(p.parse_name(question.name), p.parse_name(&pq.name))? { net_trace!("question name mismatch"); return Err(Error::Malformed); @@ -295,7 +296,10 @@ impl<'a> DnsSocket<'a> { if !addresses.is_empty() { q.state = State::Completed(CompletedQuery { addresses }) + } else { + q.state = State::Failure; } + // If we get here, packet matched the current query, stop processing. return Ok(()); } From d6a9519094ab0137afe7ca394e1fb3f75c2df8a5 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 29 Nov 2021 08:38:55 +0100 Subject: [PATCH 320/566] dns: return failure on NXDOMAIN. --- src/socket/dns.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 8f1ccda7b..ada579eee 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -4,7 +4,7 @@ use managed::ManagedSlice; use crate::socket::{Context, PollAt, Socket}; use crate::time::{Duration, Instant}; -use crate::wire::dns::{Flags, Opcode, Packet, Question, Record, RecordData, Repr, Type}; +use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr, Ipv4Address, UdpRepr}; use crate::{rand, Error, Result}; @@ -238,6 +238,13 @@ impl<'a> DnsSocket<'a> { continue; } + if p.rcode() == Rcode::NXDomain { + net_trace!("rcode NXDomain"); + + q.state = State::Failure; + continue; + } + let payload = p.payload(); let (mut payload, question) = Question::parse(payload)?; From ce1f0169f8ab375ff1d10079fdd2f42028f4d4f6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 19:54:38 +0200 Subject: [PATCH 321/566] dns: use Vec for server address list. --- examples/dns.rs | 2 +- src/socket/dns.rs | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/examples/dns.rs b/examples/dns.rs index 5180b9863..6d8b9fb6e 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -33,7 +33,7 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let servers = vec![ + let servers = &[ Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into(), ]; diff --git a/src/socket/dns.rs b/src/socket/dns.rs index ada579eee..97f38fd08 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -11,6 +11,7 @@ use crate::{rand, Error, Result}; const DNS_PORT: u16 = 53; const MAX_NAME_LEN: usize = 255; const MAX_ADDRESS_COUNT: usize = 4; +const MAX_SERVER_COUNT: usize = 4; const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs @@ -62,7 +63,7 @@ pub struct QueryHandle(usize); /// packet buffers. #[derive(Debug)] pub struct DnsSocket<'a> { - servers: ManagedSlice<'a, IpAddress>, + servers: Vec, queries: ManagedSlice<'a, Option>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -70,31 +71,29 @@ pub struct DnsSocket<'a> { } impl<'a> DnsSocket<'a> { - /// Create a DNS socket with the given buffers. - pub fn new(servers: S, queries: Q) -> DnsSocket<'a> + /// Create a DNS socket. + /// + /// # Panics + /// + /// Panics if `servers.len() > MAX_SERVER_COUNT` + pub fn new(servers: &[IpAddress], queries: Q) -> DnsSocket<'a> where - S: Into>, Q: Into>>, { DnsSocket { - servers: servers.into(), + servers: Vec::from_slice(servers).unwrap(), queries: queries.into(), hop_limit: None, } } /// Update the list of DNS servers, will replace all existing servers + /// + /// # Panics + /// + /// Panics if `servers.len() > MAX_SERVER_COUNT` pub fn update_servers(&mut self, servers: &[IpAddress]) { - let mut local_servers = self.servers.iter_mut(); - local_servers - .by_ref() - .zip(servers.iter()) - .for_each(|(a, b)| *a = *b); - - // Fill the rest with no address - for s in local_servers { - *s = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED); // TODO - } + self.servers = Vec::from_slice(servers).unwrap(); } /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. From 247839f77701c4ea8f253d9d4e026fe15605720e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 20:00:49 +0200 Subject: [PATCH 322/566] dns: fix panic when servers.len = 0 --- src/socket/dns.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 97f38fd08..742b4f4b9 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -334,21 +334,25 @@ impl<'a> DnsSocket<'a> { // Check timeout if timeout < cx.now() { + // DNS timeout + pq.timeout_at = Some(cx.now() + RETRANSMIT_TIMEOUT); + pq.retransmit_at = Instant::ZERO; + pq.delay = RETRANSMIT_DELAY; + + // Try next server. We check below whether we've tried all servers. pq.server_idx += 1; - if pq.server_idx < self.servers.len() { - // DNS timeout - pq.timeout_at = Some(cx.now() + RETRANSMIT_TIMEOUT); - pq.retransmit_at = Instant::ZERO; - pq.delay = RETRANSMIT_DELAY; - } else { - // Query failure - q.state = State::Failure; - continue; - } + } + + // Check if we've run out of servers to try. + if pq.server_idx >= self.servers.len() { + net_trace!("already tried all servers."); + q.state = State::Failure; + continue; } // Check so the IP address is valid if self.servers[pq.server_idx].is_unspecified() { + net_trace!("invalid unspecified DNS server addr."); q.state = State::Failure; continue; } From ea0d4d7f463485ad6e101d82985c57310b37453d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 20:26:20 +0200 Subject: [PATCH 323/566] dns: allow specifying name in human-friendly format. --- examples/dns.rs | 7 ++----- src/socket/dns.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/examples/dns.rs b/examples/dns.rs index 6d8b9fb6e..4c5c76b1b 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -25,11 +25,13 @@ fn main() { let (mut opts, mut free) = utils::create_options(); utils::add_tuntap_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free); + free.push("ADDRESS"); let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let name = &matches.free[0]; let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -66,11 +68,6 @@ fn main() { let dns_handle = iface.add_socket(dns_socket); - //let name = b"\x08facebook\x03com\x00"; - //let name = b"\x03www\x08facebook\x03com\x00"; - //let name = b"\x06reddit\x03com\x00"; - let name = b"\x09rust-lang\x03org\x00"; - let (socket, cx) = iface.get_socket_and_context::(dns_handle); let query = socket.start_query(cx, name).unwrap(); diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 742b4f4b9..08546c0c1 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -141,12 +141,59 @@ impl<'a> DnsSocket<'a> { } } - pub fn start_query(&mut self, cx: &mut Context, name: &[u8]) -> Result { + /// Start a query. + /// + /// `name` is specified in human-friendly format, such as `"rust-lang.org"`. + /// It accepts names both with and without trailing dot, and they're treated + /// the same (there's no support for DNS search path). + pub fn start_query(&mut self, cx: &mut Context, name: &str) -> Result { + let mut name = name.as_bytes(); + + if name.is_empty() { + net_trace!("invalid name: zero length"); + return Err(Error::Illegal); + } + + // Remove trailing dot, if any + if name[name.len() - 1] == b'.' { + name = &name[..name.len() - 1]; + } + + let mut raw_name: Vec = Vec::new(); + + for s in name.split(|&c| c == b'.') { + if s.len() > 255 { + net_trace!("invalid name: too long label"); + return Err(Error::Illegal); + } + if s.is_empty() { + net_trace!("invalid name: zero length label"); + return Err(Error::Illegal); + } + + // Push label + raw_name.push(s.len() as u8).map_err(|_| Error::Exhausted)?; + raw_name + .extend_from_slice(s) + .map_err(|_| Error::Exhausted)?; + } + + // Push terminator. + raw_name.push(0x00).map_err(|_| Error::Exhausted)?; + + self.start_query_raw(cx, &raw_name) + } + + /// Start a query with a raw (wire-format) DNS name. + /// `b"\x09rust-lang\x03org\x00"` + /// + /// You probably want to use [`start_query`] instead. + pub fn start_query_raw(&mut self, cx: &mut Context, raw_name: &[u8]) -> Result { let handle = self.find_free_query()?; self.queries[handle.0] = Some(DnsQuery { state: State::Pending(PendingQuery { - name: Vec::from_slice(name).map_err(|_| Error::Truncated)?, + name: Vec::from_slice(raw_name).map_err(|_| Error::Exhausted)?, type_: Type::A, txid: cx.rand().rand_u16(), port: cx.rand().rand_source_port(), From 280d1f18a9ccf169d023370d635e5b6861253ade Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 21:03:54 +0200 Subject: [PATCH 324/566] dns: don't relaunch query on CNAMEs. The recursive resolver is supposed to do that for us. (note, the old code already didn't relaunch it, but the intention was to make it do that) --- src/socket/dns.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 08546c0c1..468109fd7 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -330,16 +330,15 @@ impl<'a> DnsSocket<'a> { } RecordData::Cname(name) => { net_trace!("CNAME: {:?}", name); - copy_name(&mut pq.name, p.parse_name(name))?; - - // Relaunch query with the new name. - // If the server has bundled A records for the CNAME in the same packet, - // we'll process them in next iterations, and cancel the query relaunch. - pq.retransmit_at = Instant::ZERO; - pq.delay = RETRANSMIT_DELAY; - pq.txid = cx.rand().rand_u16(); - pq.port = cx.rand().rand_source_port(); + // When faced with a CNAME, recursive resolvers are supposed to + // resolve the CNAME and append the results for it. + // + // We update the query with the new name, so that we pick up the A/AAAA + // records for the CNAME when we parse them later. + // I believe it's mandatory the CNAME results MUST come *after* in the + // packet, so it's enough to do one linear pass over it. + copy_name(&mut pq.name, p.parse_name(name))?; } RecordData::Other(type_, data) => { net_trace!("unknown: {:?} {:?}", type_, data) @@ -347,10 +346,10 @@ impl<'a> DnsSocket<'a> { } } - if !addresses.is_empty() { - q.state = State::Completed(CompletedQuery { addresses }) - } else { + if addresses.is_empty() { q.state = State::Failure; + } else { + q.state = State::Completed(CompletedQuery { addresses }) } // If we get here, packet matched the current query, stop processing. From ac12274029db796b105db5225c9d92b4072fbe72 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 21:21:28 +0200 Subject: [PATCH 325/566] dns: feature and unused fixes --- src/iface/interface.rs | 5 +++-- src/lib.rs | 3 ++- src/socket/dns.rs | 20 ++++++++++++-------- src/wire/dns.rs | 2 ++ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index cf3972b1f..510092928 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -384,7 +384,7 @@ impl<'a> IpPacket<'a> { IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr), #[cfg(feature = "socket-raw")] IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), @@ -1685,7 +1685,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-igmp")] IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload), - #[cfg(feature = "socket-udp")] + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpProtocol::Udp => { self.process_udp(sockets, ip_repr, handled_by_raw_socket, ip_payload) } @@ -2134,6 +2134,7 @@ impl<'a> InterfaceInner<'a> { let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; let udp_payload = udp_packet.payload(); + #[cfg(feature = "socket-udp")] for udp_socket in sockets .iter_mut() .filter_map(|i| UdpSocket::downcast(&mut i.socket)) diff --git a/src/lib.rs b/src/lib.rs index cce09e55f..88f8418b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,9 +112,10 @@ compile_error!("You must enable at least one of the following features: proto-ip feature = "socket-tcp", feature = "socket-icmp", feature = "socket-dhcp", + feature = "socket-dns", )) ))] -compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp, socket-dhcp"); +compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp, socket-dhcp, socket-dns"); #[cfg(all( feature = "socket", diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 468109fd7..7cb35f45e 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -1,12 +1,11 @@ -#![allow(dead_code, unused)] use heapless::Vec; use managed::ManagedSlice; use crate::socket::{Context, PollAt, Socket}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; -use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr, Ipv4Address, UdpRepr}; -use crate::{rand, Error, Result}; +use crate::wire::{IpAddress, IpProtocol, IpRepr, UdpRepr}; +use crate::{Error, Result}; const DNS_PORT: u16 = 53; const MAX_NAME_LEN: usize = 255; @@ -230,7 +229,9 @@ impl<'a> DnsSocket<'a> { pub fn cancel_query(&mut self, handle: QueryHandle) -> Result<()> { let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; - let q = slot.as_mut().ok_or(Error::Illegal)?; + if slot.is_none() { + return Err(Error::Illegal); + } *slot = None; // Free up the slot for recycling. Ok(()) } @@ -245,7 +246,7 @@ impl<'a> DnsSocket<'a> { pub(crate) fn process( &mut self, - cx: &mut Context, + _cx: &mut Context, ip_repr: &IpRepr, udp_repr: &UdpRepr, payload: &[u8], @@ -316,12 +317,14 @@ impl<'a> DnsSocket<'a> { } match r.data { + #[cfg(feature = "proto-ipv4")] RecordData::A(addr) => { net_trace!("A: {:?}", addr); if addresses.push(addr.into()).is_err() { net_trace!("too many addresses in response, ignoring {:?}", addr); } } + #[cfg(feature = "proto-ipv6")] RecordData::Aaaa(addr) => { net_trace!("AAAA: {:?}", addr); if addresses.push(addr.into()).is_err() { @@ -515,12 +518,13 @@ fn copy_name<'a, const N: usize>( for label in name { let label = label?; - dest.push(label.len() as u8).map_err(|_| Error::Truncated); - dest.extend_from_slice(label).map_err(|_| Error::Truncated); + dest.push(label.len() as u8).map_err(|_| Error::Truncated)?; + dest.extend_from_slice(label) + .map_err(|_| Error::Truncated)?; } // Write terminator 0x00 - dest.push(0).map_err(|_| Error::Truncated); + dest.push(0).map_err(|_| Error::Truncated)?; Ok(()) } diff --git a/src/wire/dns.rs b/src/wire/dns.rs index c87d7500d..4b7d2526e 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -335,12 +335,14 @@ pub struct Record<'a> { impl<'a> RecordData<'a> { pub fn parse(type_: Type, data: &'a [u8]) -> Result> { match type_ { + #[cfg(feature = "proto-ipv4")] Type::A => { if data.len() != 4 { return Err(Error::Malformed); } Ok(RecordData::A(Ipv4Address::from_bytes(data))) } + #[cfg(feature = "proto-ipv6")] Type::Aaaa => { if data.len() != 16 { return Err(Error::Malformed); From 74bc0b49d764cde730c379236f98eb38f5db138b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 21:31:38 +0200 Subject: [PATCH 326/566] dns: add waker support. --- src/socket/dns.rs | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 7cb35f45e..5788adb52 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "async")] +use core::task::Waker; + use heapless::Vec; use managed::ManagedSlice; @@ -7,6 +10,9 @@ use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordDat use crate::wire::{IpAddress, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; +#[cfg(feature = "async")] +use super::WakerRegistration; + const DNS_PORT: u16 = 53; const MAX_NAME_LEN: usize = 255; const MAX_ADDRESS_COUNT: usize = 4; @@ -22,6 +28,17 @@ const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should ge #[derive(Debug)] pub struct DnsQuery { state: State, + + #[cfg(feature = "async")] + waker: WakerRegistration, +} + +impl DnsQuery { + fn set_state(&mut self, state: State) { + self.state = state; + #[cfg(feature = "async")] + self.waker.wake(); + } } #[derive(Debug)] @@ -201,6 +218,8 @@ impl<'a> DnsSocket<'a> { retransmit_at: Instant::ZERO, server_idx: 0, }), + #[cfg(feature = "async")] + waker: WakerRegistration::new(), }); Ok(handle) } @@ -236,6 +255,13 @@ impl<'a> DnsSocket<'a> { Ok(()) } + #[cfg(feature = "async")] + pub fn register_query_waker(&mut self, handle: QueryHandle, waker: &Waker) -> Result<()> { + let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; + slot.as_mut().ok_or(Error::Illegal)?.waker.register(waker); + Ok(()) + } + pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool { udp_repr.src_port == DNS_PORT && self @@ -287,8 +313,7 @@ impl<'a> DnsSocket<'a> { if p.rcode() == Rcode::NXDomain { net_trace!("rcode NXDomain"); - - q.state = State::Failure; + q.set_state(State::Failure); continue; } @@ -349,11 +374,11 @@ impl<'a> DnsSocket<'a> { } } - if addresses.is_empty() { - q.state = State::Failure; + q.set_state(if addresses.is_empty() { + State::Failure } else { - q.state = State::Completed(CompletedQuery { addresses }) - } + State::Completed(CompletedQuery { addresses }) + }); // If we get here, packet matched the current query, stop processing. return Ok(()); @@ -395,14 +420,14 @@ impl<'a> DnsSocket<'a> { // Check if we've run out of servers to try. if pq.server_idx >= self.servers.len() { net_trace!("already tried all servers."); - q.state = State::Failure; + q.set_state(State::Failure); continue; } // Check so the IP address is valid if self.servers[pq.server_idx].is_unspecified() { net_trace!("invalid unspecified DNS server addr."); - q.state = State::Failure; + q.set_state(State::Failure); continue; } From 14a7f4923238f20c582d8d2fc5a82f053dfed521 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 21:34:41 +0200 Subject: [PATCH 327/566] dns: add to CI --- .github/workflows/test.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd162b88a..e6440a13f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,18 +32,18 @@ jobs: - std proto-ipv4 # Test features chosen to be as orthogonal as possible. - - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp + - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp socket-dns - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp - - std medium-ethernet proto-ipv4 proto-igmp socket-raw - - std medium-ethernet proto-ipv4 socket-udp socket-tcp + - std medium-ethernet proto-ipv4 proto-igmp socket-raw socket-dns + - std medium-ethernet proto-ipv4 socket-udp socket-tcp socket-dns - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp - - std medium-ethernet medium-ip medium-ieee802154 proto-ipv6 socket-udp + - std medium-ethernet medium-ip medium-ieee802154 proto-ipv6 socket-udp socket-dns - std medium-ethernet proto-ipv6 socket-tcp - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp - std medium-ip proto-ipv6 socket-icmp socket-tcp # Test features chosen to be as aggressive as possible. - - std medium-ethernet medium-ip medium-ieee802154 proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async + - std medium-ethernet medium-ip medium-ieee802154 proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp socket-dns async include: # Test alloc feature which requires nightly. @@ -73,8 +73,8 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async + - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async steps: - uses: actions/checkout@v2 From 8f61715d7233109b2a2b852b7d5e2b503f245117 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 May 2022 21:39:59 +0200 Subject: [PATCH 328/566] dns: "fix" wire tests on ipv6 --- src/wire/dns.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wire/dns.rs b/src/wire/dns.rs index 4b7d2526e..38347ea7f 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -432,6 +432,7 @@ impl<'a> Repr<'a> { } } +#[cfg(feature = "proto-ipv4")] // tests assume ipv4 #[cfg(test)] mod test { use super::*; From 78f92bcea6a4cd7c151bb08dd372bea8fa64ecc7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 01:18:42 +0200 Subject: [PATCH 329/566] wire: use own Error type. --- examples/multicast.rs | 17 +++++++----- src/lib.rs | 6 +++++ src/socket/dns.rs | 10 +++---- src/wire/arp.rs | 12 ++++----- src/wire/dhcpv4.rs | 28 ++++++++++---------- src/wire/dns.rs | 38 +++++++++++++-------------- src/wire/ethernet.rs | 6 ++--- src/wire/icmpv4.rs | 16 +++++------ src/wire/icmpv6.rs | 12 ++++----- src/wire/ieee802154.rs | 13 +++++---- src/wire/igmp.rs | 10 +++---- src/wire/ip.rs | 6 ++--- src/wire/ipv4.rs | 30 ++++++++++----------- src/wire/ipv6.rs | 18 ++++++------- src/wire/ipv6fragment.rs | 8 +++--- src/wire/ipv6hopbyhop.rs | 23 +++++++--------- src/wire/ipv6option.rs | 22 ++++++++-------- src/wire/ipv6routing.rs | 40 ++++++++-------------------- src/wire/mld.rs | 6 ++--- src/wire/mod.rs | 28 +++++++++++++++++--- src/wire/ndisc.rs | 16 +++++------ src/wire/ndiscoption.rs | 29 +++++++++----------- src/wire/sixlowpan.rs | 57 +++++++++++++++++++--------------------- src/wire/tcp.rs | 55 ++++++++++++++++++-------------------- src/wire/udp.rs | 20 +++++++------- 25 files changed, 260 insertions(+), 266 deletions(-) diff --git a/examples/multicast.rs b/examples/multicast.rs index b92115b92..07be10c9f 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -82,13 +82,16 @@ fn main() { if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets // in the application layer - socket - .recv() - .and_then(Ipv4Packet::new_checked) - .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) - .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) - .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) - .unwrap_or_else(|e| println!("Recv IGMP error: {:?}", e)); + match socket.recv() { + Err(e) => println!("Recv IGMP error: {:?}", e), + Ok(buf) => { + Ipv4Packet::new_checked(buf) + .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) + .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) + .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) + .unwrap_or_else(|e| println!("parse IGMP error: {:?}", e)); + } + } } let socket = iface.get_socket::(udp_handle); diff --git a/src/lib.rs b/src/lib.rs index 88f8418b6..b28f434bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,3 +252,9 @@ impl fmt::Display for Error { } } } + +impl From for Error { + fn from(_: wire::Error) -> Self { + Error::Malformed + } +} diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 5788adb52..37a6aba23 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -7,7 +7,7 @@ use managed::ManagedSlice; use crate::socket::{Context, PollAt, Socket}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; -use crate::wire::{IpAddress, IpProtocol, IpRepr, UdpRepr}; +use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; #[cfg(feature = "async")] @@ -509,9 +509,9 @@ impl<'a> From> for Socket<'a> { } fn eq_names<'a>( - mut a: impl Iterator>, - mut b: impl Iterator>, -) -> Result { + mut a: impl Iterator>, + mut b: impl Iterator>, +) -> wire::Result { loop { match (a.next(), b.next()) { // Handle errors @@ -537,7 +537,7 @@ fn eq_names<'a>( fn copy_name<'a, const N: usize>( dest: &mut Vec, - name: impl Iterator>, + name: impl Iterator>, ) -> Result<()> { dest.truncate(0); diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 48d64ca69..05e059dcf 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -1,7 +1,7 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; -use crate::{Error, Result}; +use super::{Error, Result}; pub use super::EthernetProtocol as Protocol; @@ -80,7 +80,7 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_hardware_len] or /// [set_protocol_len]. @@ -91,9 +91,9 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::OPER.end { - Err(Error::Truncated) + Err(Error) } else if len < field::TPA(self.hardware_len(), self.protocol_len()).end { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -269,7 +269,7 @@ pub enum Repr { impl Repr { /// Parse an Address Resolution Protocol packet and return a high-level representation, - /// or return `Err(Error::Unrecognized)` if the packet is not recognized. + /// or return `Err(Error)` if the packet is not recognized. pub fn parse>(packet: &Packet) -> Result { match ( packet.hardware_type(), @@ -284,7 +284,7 @@ impl Repr { target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()), target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()), }), - _ => Err(Error::Unrecognized), + _ => Err(Error), } } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 1982ab9b9..8df841dc3 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -3,9 +3,9 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; +use super::{Error, Result}; use crate::wire::arp::Hardware; use crate::wire::{EthernetAddress, Ipv4Address}; -use crate::{Error, Result}; pub const SERVER_PORT: u16 = 67; pub const CLIENT_PORT: u16 = 68; @@ -77,7 +77,7 @@ impl<'a> DhcpOption<'a> { // See https://tools.ietf.org/html/rfc2132 for all possible DHCP options. let (skip_len, option); - match *buffer.get(0).ok_or(Error::Truncated)? { + match *buffer.get(0).ok_or(Error)? { field::OPT_END => { skip_len = 1; option = DhcpOption::EndOfList; @@ -87,9 +87,9 @@ impl<'a> DhcpOption<'a> { option = DhcpOption::Pad; } kind => { - let length = *buffer.get(1).ok_or(Error::Truncated)? as usize; + let length = *buffer.get(1).ok_or(Error)? as usize; skip_len = length + 2; - let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?; + let data = buffer.get(2..skip_len).ok_or(Error)?; match (kind, length) { (field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(), (field::OPT_DHCP_MESSAGE_TYPE, 1) => { @@ -101,7 +101,7 @@ impl<'a> DhcpOption<'a> { (field::OPT_CLIENT_ID, 7) => { let hardware_type = Hardware::from(u16::from(data[0])); if hardware_type != Hardware::Ethernet { - return Err(Error::Unrecognized); + return Err(Error); } option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..])); @@ -354,13 +354,13 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// [set_header_len]: #method.set_header_len pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::MAGIC_NUMBER.end { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -470,7 +470,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { #[inline] pub fn options(&self) -> Result<&'a [u8]> { let data = self.buffer.as_ref(); - data.get(field::OPTIONS).ok_or(Error::Malformed) + data.get(field::OPTIONS).ok_or(Error) } } @@ -593,7 +593,7 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { #[inline] pub fn options_mut(&mut self) -> Result<&mut [u8]> { let data = self.buffer.as_mut(); - data.get_mut(field::OPTIONS).ok_or(Error::Truncated) + data.get_mut(field::OPTIONS).ok_or(Error) } } @@ -760,17 +760,17 @@ impl<'a> Repr<'a> { match packet.hardware_type() { Hardware::Ethernet => { if packet.hardware_len() != 6 { - return Err(Error::Malformed); + return Err(Error); } } - Hardware::Unknown(_) => return Err(Error::Unrecognized), // unimplemented + Hardware::Unknown(_) => return Err(Error), // unimplemented } if packet.magic_number() != DHCP_MAGIC_NUMBER { - return Err(Error::Malformed); + return Err(Error); } - let mut message_type = Err(Error::Malformed); + let mut message_type = Err(Error); let mut requested_ip = None; let mut client_identifier = None; let mut server_identifier = None; @@ -827,7 +827,7 @@ impl<'a> Repr<'a> { let chunk_size = 4; for (server, chunk) in servers.iter_mut().zip(data.chunks(chunk_size)) { if chunk.len() != chunk_size { - return Err(Error::Malformed); + return Err(Error); } *server = Some(Ipv4Address::from_bytes(chunk)); } diff --git a/src/wire/dns.rs b/src/wire/dns.rs index 38347ea7f..048ef40f4 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -5,11 +5,11 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::iter; use core::iter::Iterator; +use super::{Error, Result}; #[cfg(feature = "proto-ipv4")] use crate::wire::Ipv4Address; #[cfg(feature = "proto-ipv6")] use crate::wire::Ipv6Address; -use crate::{Error, Result}; enum_with_unknown! { /// DNS OpCodes @@ -98,12 +98,12 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Malformed)` if the buffer is smaller than + /// Returns `Err(Error)` if the buffer is smaller than /// the header length. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::HEADER_END { - Err(Error::Malformed) + Err(Error) } else { Ok(()) } @@ -166,14 +166,14 @@ impl> Packet { iter::from_fn(move || loop { if bytes.is_empty() { - return Some(Err(Error::Malformed)); + return Some(Err(Error)); } match bytes[0] { 0x00 => return None, x if x & 0xC0 == 0x00 => { let len = (x & 0x3F) as usize; if bytes.len() < 1 + len { - return Some(Err(Error::Malformed)); + return Some(Err(Error)); } let label = &bytes[1..1 + len]; bytes = &bytes[1 + len..]; @@ -181,12 +181,12 @@ impl> Packet { } x if x & 0xC0 == 0xC0 => { if bytes.len() < 2 { - return Some(Err(Error::Malformed)); + return Some(Err(Error)); } let y = bytes[1]; let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); if packet.len() <= ptr { - return Some(Err(Error::Malformed)); + return Some(Err(Error)); } // RFC1035 says: "In this scheme, an entire domain name or a list of labels at @@ -204,7 +204,7 @@ impl> Packet { bytes = &packet[ptr..]; packet = &packet[..ptr]; } - _ => return Some(Err(Error::Malformed)), + _ => return Some(Err(Error)), } }) } @@ -262,24 +262,24 @@ fn parse_name_part<'a>( mut f: impl FnMut(&'a [u8]), ) -> Result<(&'a [u8], Option)> { loop { - let x = *bytes.get(0).ok_or(Error::Malformed)?; + let x = *bytes.get(0).ok_or(Error)?; bytes = &bytes[1..]; match x { 0x00 => return Ok((bytes, None)), x if x & 0xC0 == 0x00 => { let len = (x & 0x3F) as usize; - let label = bytes.get(..len).ok_or(Error::Malformed)?; + let label = bytes.get(..len).ok_or(Error)?; bytes = &bytes[len..]; f(label); } x if x & 0xC0 == 0xC0 => { - let y = *bytes.get(0).ok_or(Error::Malformed)?; + let y = *bytes.get(0).ok_or(Error)?; bytes = &bytes[1..]; let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); return Ok((bytes, Some(ptr))); } - _ => return Err(Error::Malformed), + _ => return Err(Error), } } } @@ -297,14 +297,14 @@ impl<'a> Question<'a> { let name = &buffer[..buffer.len() - rest.len()]; if rest.len() < 4 { - return Err(Error::Malformed); + return Err(Error); } let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); let class = NetworkEndian::read_u16(&rest[2..4]); let rest = &rest[4..]; if class != CLASS_IN { - return Err(Error::Malformed); + return Err(Error); } Ok((rest, Question { name, type_ })) @@ -338,14 +338,14 @@ impl<'a> RecordData<'a> { #[cfg(feature = "proto-ipv4")] Type::A => { if data.len() != 4 { - return Err(Error::Malformed); + return Err(Error); } Ok(RecordData::A(Ipv4Address::from_bytes(data))) } #[cfg(feature = "proto-ipv6")] Type::Aaaa => { if data.len() != 16 { - return Err(Error::Malformed); + return Err(Error); } Ok(RecordData::Aaaa(Ipv6Address::from_bytes(data))) } @@ -372,7 +372,7 @@ impl<'a> Record<'a> { let name = &buffer[..buffer.len() - rest.len()]; if rest.len() < 10 { - return Err(Error::Malformed); + return Err(Error); } let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); let class = NetworkEndian::read_u16(&rest[2..4]); @@ -381,10 +381,10 @@ impl<'a> Record<'a> { let rest = &rest[10..]; if class != CLASS_IN { - return Err(Error::Malformed); + return Err(Error); } - let data = rest.get(..len).ok_or(Error::Malformed)?; + let data = rest.get(..len).ok_or(Error)?; let rest = &rest[len..]; Ok(( diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 0e48d21c1..71911bbba 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -1,7 +1,7 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; -use crate::{Error, Result}; +use super::{Error, Result}; enum_with_unknown! { /// Ethernet protocol type. @@ -115,11 +115,11 @@ impl> Frame { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < HEADER_LEN { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 7e0f5382b..864081992 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -1,10 +1,10 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::{cmp, fmt}; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::{Ipv4Packet, Ipv4Repr}; -use crate::{Error, Result}; enum_with_unknown! { /// Internet protocol control message type. @@ -189,7 +189,7 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_header_len]. /// @@ -197,7 +197,7 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::HEADER_END { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -386,7 +386,7 @@ impl<'a> Repr<'a> { { // Valid checksum is expected. if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { - return Err(Error::Checksum); + return Err(Error); } match (packet.msg_type(), packet.msg_code()) { @@ -409,7 +409,7 @@ impl<'a> Repr<'a> { // RFC 792 requires exactly eight bytes to be returned. // We allow more, since there isn't a reason not to, but require at least eight. if payload.len() < 8 { - return Err(Error::Truncated); + return Err(Error); } Ok(Repr::DstUnreachable { @@ -424,7 +424,7 @@ impl<'a> Repr<'a> { data: payload, }) } - _ => Err(Error::Unrecognized), + _ => Err(Error), } } @@ -635,8 +635,8 @@ mod test { #[test] fn test_check_len() { let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated)); - assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated)); + assert_eq!(Packet::new_checked(&[]), Err(Error)); + assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error)); assert!(Packet::new_checked(&bytes[..]).is_ok()); } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 4afa37db9..1df3bdb7e 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -1,13 +1,13 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::{cmp, fmt}; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::MldRepr; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::wire::NdiscRepr; use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; -use crate::{Error, Result}; enum_with_unknown! { /// Internet protocol control message type. @@ -262,11 +262,11 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::HEADER_END || len < self.header_len() { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -557,7 +557,7 @@ impl<'a> Repr<'a> { let payload = &packet.payload()[ip_packet.header_len() as usize..]; if payload.len() < 8 { - return Err(Error::Truncated); + return Err(Error); } let repr = Ipv6Repr { src_addr: ip_packet.src_addr(), @@ -570,7 +570,7 @@ impl<'a> Repr<'a> { } // Valid checksum is expected. if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) { - return Err(Error::Checksum); + return Err(Error); } match (packet.msg_type(), packet.msg_code()) { @@ -620,7 +620,7 @@ impl<'a> Repr<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), - _ => Err(Error::Unrecognized), + _ => Err(Error), } } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 04c30ef39..f9dd98f31 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -2,9 +2,8 @@ use core::fmt; use byteorder::{ByteOrder, LittleEndian}; +use super::{Error, Result}; use crate::wire::ipv6::Address as Ipv6Address; -use crate::Error; -use crate::Result; enum_with_unknown! { /// IEEE 802.15.4 frame type. @@ -238,22 +237,22 @@ impl> Frame { packet.check_len()?; if matches!(packet.dst_addressing_mode(), AddressingMode::Unknown(_)) { - return Err(Error::Malformed); + return Err(Error); } if matches!(packet.src_addressing_mode(), AddressingMode::Unknown(_)) { - return Err(Error::Malformed); + return Err(Error); } Ok(packet) } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { // We need at least 3 bytes if self.buffer.as_ref().len() < 3 { - return Err(Error::Truncated); + return Err(Error); } let mut offset = field::ADDRESSING.start + 2; @@ -267,7 +266,7 @@ impl> Frame { } if offset > self.buffer.as_ref().len() { - return Err(Error::Truncated); + return Err(Error); } Ok(()) diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 6d5dd2baf..3bdd65526 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -1,9 +1,9 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use super::{Error, Result}; use crate::time::Duration; use crate::wire::ip::checksum; -use crate::{Error, Result}; use crate::wire::Ipv4Address; @@ -69,11 +69,11 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::GROUP_ADDRESS.end as usize { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -208,7 +208,7 @@ impl Repr { // Check if the address is 0.0.0.0 or multicast let addr = packet.group_addr(); if !addr.is_unspecified() && !addr.is_multicast() { - return Err(Error::Malformed); + return Err(Error); } // construct a packet based on the Type field @@ -241,7 +241,7 @@ impl Repr { version: IgmpVersion::Version1, }) } - _ => Err(Error::Unrecognized), + _ => Err(Error), } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 6037c0827..b4d6a2d18 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -1,12 +1,12 @@ use core::convert::From; use core::fmt; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr}; -use crate::{Error, Result}; /// Internet protocol version. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] @@ -22,14 +22,14 @@ impl Version { /// Return the version of an IP packet stored in the provided buffer. /// /// This function never returns `Ok(IpVersion::Unspecified)`; instead, - /// unknown versions result in `Err(Error::Unrecognized)`. + /// unknown versions result in `Err(Error)`. pub fn of_packet(data: &[u8]) -> Result { match data[0] >> 4 { #[cfg(feature = "proto-ipv4")] 4 => Ok(Version::Ipv4), #[cfg(feature = "proto-ipv6")] 6 => Ok(Version::Ipv6), - _ => Err(Error::Unrecognized), + _ => Err(Error), } } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 006de4bb4..243d948c4 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -1,9 +1,9 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::{checksum, pretty_print_ip_payload}; -use crate::{Error, Result}; pub use super::IpProtocol as Protocol; @@ -164,7 +164,7 @@ impl Cidr { prefix_len: netmask.count_ones() as u8, }) } else { - Err(Error::Illegal) + Err(Error) } } @@ -305,8 +305,8 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. - /// Returns `Err(Error::Malformed)` if the header length is greater + /// Returns `Err(Error)` if the buffer is too short. + /// Returns `Err(Error)` if the header length is greater /// than total length. /// /// The result of this check is invalidated by calling [set_header_len] @@ -318,13 +318,13 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::DST_ADDR.end { - Err(Error::Truncated) + Err(Error) } else if len < self.header_len() as usize { - Err(Error::Truncated) + Err(Error) } else if self.header_len() as u16 > self.total_len() { - Err(Error::Malformed) + Err(Error) } else if len < self.total_len() as usize { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -611,20 +611,20 @@ impl Repr { ) -> Result { // Version 4 is expected. if packet.version() != 4 { - return Err(Error::Malformed); + return Err(Error); } // Valid checksum is expected. if checksum_caps.ipv4.rx() && !packet.verify_checksum() { - return Err(Error::Checksum); + return Err(Error); } // We do not support fragmentation. if packet.more_frags() || packet.frag_offset() != 0 { - return Err(Error::Fragmented); + return Err(Error); } // Since the packet is not fragmented, it must include the entire payload. let payload_len = packet.total_len() as usize - packet.header_len() as usize; if packet.payload().len() < payload_len { - return Err(Error::Truncated); + return Err(Error); } // All DSCP values are acceptable, since they are of no concern to receiving endpoint. @@ -837,7 +837,7 @@ mod test { bytes.extend(&PACKET_BYTES[..]); Packet::new_unchecked(&mut bytes).set_total_len(128); - assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated); + assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error); } static REPR_PACKET_BYTES: [u8; 24] = [ @@ -874,7 +874,7 @@ mod test { let packet = Packet::new_unchecked(&*packet.into_inner()); assert_eq!( Repr::parse(&packet, &ChecksumCapabilities::default()), - Err(Error::Malformed) + Err(Error) ); } @@ -882,7 +882,7 @@ mod test { fn test_parse_total_len_less_than_header_len() { let mut bytes = vec![0; 40]; bytes[0] = 0x09; - assert_eq!(Packet::new_checked(&mut bytes), Err(Error::Malformed)); + assert_eq!(Packet::new_checked(&mut bytes), Err(Error)); } #[test] diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 184060ad4..a54fbbd29 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -3,10 +3,10 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use super::{Error, Result}; use crate::wire::ip::pretty_print_ip_payload; #[cfg(feature = "proto-ipv4")] use crate::wire::ipv4; -use crate::{Error, Result}; pub use super::IpProtocol as Protocol; @@ -422,7 +422,7 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_payload_len]. /// @@ -431,7 +431,7 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::DST_ADDR.end || len < self.total_len() { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -639,7 +639,7 @@ impl Repr { // Ensure basic accessors will work packet.check_len()?; if packet.version() != 6 { - return Err(Error::Malformed); + return Err(Error); } Ok(Repr { src_addr: packet.src_addr(), @@ -708,10 +708,10 @@ impl> PrettyPrint for Packet { #[cfg(test)] mod test { + use super::Error; use super::{Address, Cidr}; use super::{Packet, Protocol, Repr}; use crate::wire::pretty_print::PrettyPrinter; - use crate::Error; #[cfg(feature = "proto-ipv4")] use crate::wire::ipv4::Address as Ipv4Address; @@ -1128,7 +1128,7 @@ mod test { bytes.extend(&REPR_PACKET_BYTES[..]); Packet::new_unchecked(&mut bytes).set_payload_len(0x80); - assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated); + assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error); } #[test] @@ -1145,7 +1145,7 @@ mod test { packet.set_version(4); packet.set_payload_len(0); let packet = Packet::new_unchecked(&*packet.into_inner()); - assert_eq!(Repr::parse(&packet), Err(Error::Malformed)); + assert_eq!(Repr::parse(&packet), Err(Error)); } #[test] @@ -1155,7 +1155,7 @@ mod test { packet.set_version(6); packet.set_payload_len(39); let packet = Packet::new_unchecked(&*packet.into_inner()); - assert_eq!(Repr::parse(&packet), Err(Error::Truncated)); + assert_eq!(Repr::parse(&packet), Err(Error)); } #[test] @@ -1165,7 +1165,7 @@ mod test { packet.set_version(6); packet.set_payload_len(1); let packet = Packet::new_unchecked(&*packet.into_inner()); - assert_eq!(Repr::parse(&packet), Err(Error::Truncated)); + assert_eq!(Repr::parse(&packet), Err(Error)); } #[test] diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index 690348e51..e19e6cc26 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use super::{Error, Result}; use core::fmt; use byteorder::{ByteOrder, NetworkEndian}; @@ -51,13 +51,13 @@ impl> Header { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let data = self.buffer.as_ref(); let len = data.len(); if len < field::IDENT.end { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -226,7 +226,7 @@ mod test { fn test_check_len() { // less than 8 bytes assert_eq!( - Err(Error::Truncated), + Err(Error), Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len() ); // valid diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 97b868553..09d2ca5b4 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use super::{Error, Result}; use core::fmt; pub use super::IpProtocol as Protocol; @@ -65,7 +65,7 @@ impl> Header { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_header_len]. /// @@ -75,13 +75,13 @@ impl> Header { let len = data.len(); if len < field::MIN_HEADER_SIZE { - return Err(Error::Truncated); + return Err(Error); } let of = field::OPTIONS(data[field::LENGTH]); if len < of.end { - return Err(Error::Truncated); + return Err(Error); } Ok(()) @@ -226,17 +226,17 @@ mod test { fn test_check_len() { // zero byte buffer assert_eq!( - Err(Error::Truncated), + Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len() ); // no length field assert_eq!( - Err(Error::Truncated), + Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len() ); // less than 8 bytes assert_eq!( - Err(Error::Truncated), + Err(Error), Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len() ); // valid @@ -248,10 +248,7 @@ mod test { ); // length field value greater than number of bytes let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; - assert_eq!( - Err(Error::Truncated), - Header::new_unchecked(&header).check_len() - ); + assert_eq!(Err(Error), Header::new_unchecked(&header).check_len()); } #[test] @@ -303,14 +300,14 @@ mod test { let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); - assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error::Truncated); + assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error); let mut bytes = vec![]; bytes.extend(&REPR_PACKET_PAD12); let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); - assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error::Truncated); + assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error); } #[test] diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index e22a8aad7..25bbc7dd5 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use super::{Error, Result}; use core::fmt; enum_with_unknown! { @@ -103,7 +103,7 @@ impl> Ipv6Option { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_data_len]. /// @@ -113,7 +113,7 @@ impl> Ipv6Option { let len = data.len(); if len < field::LENGTH { - return Err(Error::Truncated); + return Err(Error); } if self.option_type() == Type::Pad1 { @@ -121,13 +121,13 @@ impl> Ipv6Option { } if len == field::LENGTH { - return Err(Error::Truncated); + return Err(Error); } let df = field::DATA(data[field::LENGTH]); if len < df.end { - return Err(Error::Truncated); + return Err(Error); } Ok(()) @@ -362,7 +362,7 @@ mod test { let bytes = [0u8]; // zero byte buffer assert_eq!( - Err(Error::Truncated), + Err(Error), Ipv6Option::new_unchecked(&bytes[..0]).check_len() ); // pad1 @@ -373,7 +373,7 @@ mod test { // padn with truncated data assert_eq!( - Err(Error::Truncated), + Err(Error), Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len() ); // padn @@ -384,11 +384,11 @@ mod test { // unknown option type with truncated data assert_eq!( - Err(Error::Truncated), + Err(Error), Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len() ); assert_eq!( - Err(Error::Truncated), + Err(Error), Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len() ); // unknown type @@ -436,7 +436,7 @@ mod test { assert_eq!(opt.option_type(), Type::Unknown(255)); // unrecognized option without length and data - assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error::Truncated)); + assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error)); } #[test] @@ -533,7 +533,7 @@ mod test { .. }), ) => continue, - (6, Err(Error::Truncated)) => continue, + (6, Err(Error)) => continue, (i, res) => panic!("Unexpected option `{:?}` at index {}", res, i), } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 8914a1dad..56a16fa03 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -1,4 +1,4 @@ -use crate::{Error, Result}; +use super::{Error, Result}; use core::fmt; use crate::wire::IpProtocol as Protocol; @@ -158,7 +158,7 @@ impl> Header { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_header_len]. /// @@ -166,11 +166,11 @@ impl> Header { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::MIN_HEADER_SIZE { - return Err(Error::Truncated); + return Err(Error); } if len < field::DATA(self.header_len()).end as usize { - return Err(Error::Truncated); + return Err(Error); } Ok(()) @@ -444,7 +444,7 @@ impl<'a> Repr<'a> { addresses: header.addresses(), }), - _ => Err(Error::Unrecognized), + _ => Err(Error), } } @@ -588,31 +588,13 @@ mod test { #[test] fn test_check_len() { // less than min header size - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_TYPE2[..3]).check_len() - ); - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_SRH_FULL[..3]).check_len() - ); - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_SRH_ELIDED[..3]).check_len() - ); + assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..3]).check_len()); + assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..3]).check_len()); + assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..3]).check_len()); // less than specified length field - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_TYPE2[..23]).check_len() - ); - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_SRH_FULL[..39]).check_len() - ); - assert_eq!( - Err(Error::Truncated), - Header::new(&BYTES_SRH_ELIDED[..11]).check_len() - ); + assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..23]).check_len()); + assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..39]).check_len()); + assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..11]).check_len()); // valid assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len()); assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len()); diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 83125b6d4..d163e722a 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -6,9 +6,9 @@ use byteorder::{ByteOrder, NetworkEndian}; +use super::{Error, Result}; use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::Ipv6Address; -use crate::{Error, Result}; enum_with_unknown! { /// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for @@ -192,7 +192,7 @@ impl> AddressRecord { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::RECORD_MCAST_ADDR.end { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -333,7 +333,7 @@ impl<'a> Repr<'a> { nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(), data: packet.payload(), }), - _ => Err(Error::Unrecognized), + _ => Err(Error), } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index f320f3178..0a87862ac 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -125,7 +125,9 @@ mod sixlowpan; mod tcp; mod udp; -use crate::{phy::Medium, Error}; +use core::fmt; + +use crate::phy::Medium; pub use self::pretty_print::PrettyPrinter; @@ -245,6 +247,24 @@ pub use self::dhcpv4::{ SERVER_PORT as DHCP_SERVER_PORT, }; +/// Parsing a packet failed. +/// +/// Either it is malformed, or it is not supported by smoltcp. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Error; + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "wire::Error") + } +} + +pub type Result = core::result::Result; + /// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -354,12 +374,12 @@ impl RawHardwareAddress { self.len == 0 } - pub fn parse(&self, medium: Medium) -> Result { + pub fn parse(&self, medium: Medium) -> Result { match medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { if self.len() < 6 { - return Err(Error::Malformed); + return Err(Error); } Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes( self.as_bytes(), @@ -368,7 +388,7 @@ impl RawHardwareAddress { #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { if self.len() < 8 { - return Err(Error::Malformed); + return Err(Error); } Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes( self.as_bytes(), diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 32a216396..789167345 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -1,6 +1,7 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; +use super::{Error, Result}; use crate::time::Duration; use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::Ipv6Address; @@ -8,7 +9,6 @@ use crate::wire::RawHardwareAddress; use crate::wire::{Ipv6Packet, Ipv6Repr}; use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; -use crate::{Error, Result}; bitflags! { #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -237,7 +237,7 @@ impl<'a> Repr<'a> { match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), _ => { - return Err(Error::Unrecognized); + return Err(Error); } } } else { @@ -256,7 +256,7 @@ impl<'a> Repr<'a> { NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info), _ => { - return Err(Error::Unrecognized); + return Err(Error); } } offset += opt.buffer_len(); @@ -278,7 +278,7 @@ impl<'a> Repr<'a> { match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), _ => { - return Err(Error::Unrecognized); + return Err(Error); } } } else { @@ -295,7 +295,7 @@ impl<'a> Repr<'a> { match opt.option_type() { NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), _ => { - return Err(Error::Unrecognized); + return Err(Error); } } } else { @@ -319,7 +319,7 @@ impl<'a> Repr<'a> { } NdiscOptionType::RedirectedHeader => { if opt.data_len() < 6 { - return Err(Error::Truncated); + return Err(Error); } else { let ip_packet = Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]); @@ -333,7 +333,7 @@ impl<'a> Repr<'a> { } } _ => { - return Err(Error::Unrecognized); + return Err(Error); } } } @@ -344,7 +344,7 @@ impl<'a> Repr<'a> { redirected_hdr, }) } - _ => Err(Error::Unrecognized), + _ => Err(Error), } } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index e29abb7e4..658b96a8c 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -2,9 +2,9 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use super::{Error, Result}; use crate::time::Duration; use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN}; -use crate::{Error, Result}; use crate::wire::RawHardwareAddress; @@ -162,7 +162,7 @@ impl> NdiscOption { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. /// /// The result of this check is invalidated by calling [set_data_len]. /// @@ -172,18 +172,18 @@ impl> NdiscOption { let len = data.len(); if len < field::MIN_OPT_LEN { - Err(Error::Truncated) + Err(Error) } else { let data_range = field::DATA(data[field::LENGTH]); if len < data_range.end { - Err(Error::Truncated) + Err(Error) } else { match self.option_type() { Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()), Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()), Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()), Type::Unknown(_) => Ok(()), - _ => Err(Error::Truncated), + _ => Err(Error), } } } @@ -432,14 +432,14 @@ impl<'a> Repr<'a> { if opt.data_len() == 1 { Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr())) } else { - Err(Error::Malformed) + Err(Error) } } Type::TargetLinkLayerAddr => { if opt.data_len() == 1 { Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) } else { - Err(Error::Malformed) + Err(Error) } } Type::PrefixInformation => { @@ -452,7 +452,7 @@ impl<'a> Repr<'a> { prefix: opt.prefix(), })) } else { - Err(Error::Malformed) + Err(Error) } } Type::RedirectedHeader => { @@ -460,7 +460,7 @@ impl<'a> Repr<'a> { // does not have enough data to fill out the IP header // and common option fields. if opt.data_len() < 6 { - Err(Error::Truncated) + Err(Error) } else { let ip_packet = Ipv6Packet::new_unchecked(&opt.data()[field::IP_DATA..]); let ip_repr = Ipv6Repr::parse(&ip_packet)?; @@ -474,7 +474,7 @@ impl<'a> Repr<'a> { if opt.data_len() == 1 { Ok(Repr::Mtu(opt.mtu())) } else { - Err(Error::Malformed) + Err(Error) } } Type::Unknown(id) => Ok(Repr::Unknown { @@ -617,10 +617,10 @@ impl> PrettyPrint for NdiscOption { #[cfg(test)] mod test { + use super::Error; use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type}; use crate::time::Duration; use crate::wire::{EthernetAddress, Ipv6Address}; - use crate::Error; static PREFIX_OPT_BYTES: [u8; 32] = [ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, @@ -659,12 +659,9 @@ mod test { #[test] fn test_short_packet() { - assert_eq!( - NdiscOption::new_checked(&[0x00, 0x00]), - Err(Error::Truncated) - ); + assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error)); let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated)); + assert_eq!(NdiscOption::new_checked(&bytes), Err(Error)); } #[test] diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index af9a47767..13ca2fbb7 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -1,12 +1,11 @@ -/// Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over -/// IEEE802.154-based networks. -/// -/// [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 +//! Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over +//! IEEE802.154-based networks. +//! +//! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 +use super::{Error, Result}; use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; -use crate::Error; -use crate::Result; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -46,23 +45,22 @@ impl<'a> Address<'a> { Some(LlAddress::Extended(ll)) => { bytes[8..].copy_from_slice(&LlAddress::Extended(ll).as_eui_64().unwrap()); } - _ => return Err(Error::Malformed), + _ => return Err(Error), } Ok(ipv6::Address::from_bytes(&bytes)) } - Address::WithContext(_) => Err(Error::NotSupported), - Address::Reserved => Err(Error::Malformed), + Address::WithContext(_) => Err(Error), + Address::Reserved => Err(Error), } } } pub mod iphc { + use super::{Error, Result}; use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; - use crate::Error; - use crate::Result; use byteorder::{ByteOrder, NetworkEndian}; use super::Address; @@ -124,11 +122,11 @@ pub mod iphc { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.len() < 2 { - return Err(Error::Truncated); + return Err(Error); } let mut offset = self.ip_fields_start() @@ -139,7 +137,7 @@ pub mod iphc { offset += self.dst_address_size(); if offset as usize > buffer.len() { - return Err(Error::Truncated); + return Err(Error); } Ok(()) @@ -290,7 +288,7 @@ pub mod iphc { // Any remaining bits are zero. Ok(Address::WithContext(&[])) } - _ => Err(Error::Malformed), + _ => Err(Error), } } @@ -428,10 +426,10 @@ pub mod iphc { // P are octets used to encode the prefix itself. // L are octets used to encode the prefix length. // The prefix information P and L is taken from the specified context. - Err(Error::NotSupported) + Err(Error) } (1, 1, 0b01 | 0b10 | 0b11) => Ok(Address::Reserved), - _ => Err(Error::Malformed), + _ => Err(Error), } } @@ -781,7 +779,7 @@ pub mod iphc { if packet.dispatch_field() != DISPATCH { // This is not an LOWPAN_IPHC packet. - return Err(Error::Malformed); + return Err(Error); } let src_addr = packet.src_addr()?.resolve(ll_src_addr)?; @@ -975,13 +973,12 @@ pub mod iphc { } pub mod nhc { + use super::{Error, Result}; use crate::wire::ip::checksum; use crate::wire::ip::Address as IpAddress; use crate::wire::ipv6; use crate::wire::udp::Repr as UdpRepr; use crate::wire::IpProtocol; - use crate::Error; - use crate::Result; use byteorder::{ByteOrder, NetworkEndian}; use ipv6::Address; @@ -1032,7 +1029,7 @@ pub mod nhc { // We have a compressed UDP header. Ok(Packet::UdpHeader(UdpPacket::new_checked(buffer)?)) } else { - Err(Error::Unrecognized) + Err(Error) } } } @@ -1089,11 +1086,11 @@ pub mod nhc { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.is_empty() { - Err(Error::Truncated) + Err(Error) } else { Ok(()) } @@ -1235,7 +1232,7 @@ pub mod nhc { packet.check_len()?; if packet.dispatch_field() != EXT_HEADER_DISPATCH { - return Err(Error::Malformed); + return Err(Error); } Ok(ExtensionHeaderRepr { @@ -1293,17 +1290,17 @@ pub mod nhc { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.is_empty() { - return Err(Error::Truncated); + return Err(Error); } let index = 1 + self.ports_size() + self.checksum_size(); if index > buffer.len() { - return Err(Error::Truncated); + return Err(Error); } Ok(()) @@ -1507,7 +1504,7 @@ pub mod nhc { packet.check_len()?; if packet.dispatch_field() != UDP_DISPATCH { - return Err(Error::Malformed); + return Err(Error); } let payload_len = packet.payload().len(); @@ -1526,11 +1523,11 @@ pub mod nhc { if let Some(checksum) = packet.checksum() { if chk_sum != checksum { - return Err(Error::Checksum); + return Err(Error); } } else { net_trace!("Currently we do not support elided checksums."); - return Err(Error::Unrecognized); + return Err(Error); }; Ok(UdpNhcRepr(UdpRepr { diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 74d4a4549..401dcc50d 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -1,10 +1,10 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::{cmp, fmt, i32, ops}; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::{IpAddress, IpProtocol}; -use crate::{Error, Result}; /// A TCP sequence number. /// @@ -128,8 +128,8 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. - /// Returns `Err(Error::Malformed)` if the header length field has a value smaller + /// Returns `Err(Error)` if the buffer is too short. + /// Returns `Err(Error)` if the header length field has a value smaller /// than the minimal header length. /// /// The result of this check is invalidated by calling [set_header_len]. @@ -138,13 +138,11 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::URGENT.end { - Err(Error::Truncated) + Err(Error) } else { let header_len = self.header_len() as usize; - if len < header_len { - Err(Error::Truncated) - } else if header_len < field::URGENT.end { - Err(Error::Malformed) + if len < header_len || header_len < field::URGENT.end { + Err(Error) } else { Ok(()) } @@ -608,7 +606,7 @@ pub enum TcpOption<'a> { impl<'a> TcpOption<'a> { pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], TcpOption<'a>)> { let (length, option); - match *buffer.get(0).ok_or(Error::Truncated)? { + match *buffer.get(0).ok_or(Error)? { field::OPT_END => { length = 1; option = TcpOption::EndOfList; @@ -618,21 +616,21 @@ impl<'a> TcpOption<'a> { option = TcpOption::NoOperation; } kind => { - length = *buffer.get(1).ok_or(Error::Truncated)? as usize; - let data = buffer.get(2..length).ok_or(Error::Truncated)?; + length = *buffer.get(1).ok_or(Error)? as usize; + let data = buffer.get(2..length).ok_or(Error)?; match (kind, length) { (field::OPT_END, _) | (field::OPT_NOP, _) => unreachable!(), (field::OPT_MSS, 4) => { option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)) } - (field::OPT_MSS, _) => return Err(Error::Malformed), + (field::OPT_MSS, _) => return Err(Error), (field::OPT_WS, 3) => option = TcpOption::WindowScale(data[0]), - (field::OPT_WS, _) => return Err(Error::Malformed), + (field::OPT_WS, _) => return Err(Error), (field::OPT_SACKPERM, 2) => option = TcpOption::SackPermitted, - (field::OPT_SACKPERM, _) => return Err(Error::Malformed), + (field::OPT_SACKPERM, _) => return Err(Error), (field::OPT_SACKRNG, n) => { if n < 10 || (n - 2) % 8 != 0 { - return Err(Error::Malformed); + return Err(Error); } if n > 26 { // It's possible for a remote to send 4 SACK blocks, but extremely rare. @@ -801,14 +799,14 @@ impl<'a> Repr<'a> { { // Source and destination ports must be present. if packet.src_port() == 0 { - return Err(Error::Malformed); + return Err(Error); } if packet.dst_port() == 0 { - return Err(Error::Malformed); + return Err(Error); } // Valid checksum is expected. if checksum_caps.tcp.rx() && !packet.verify_checksum(src_addr, dst_addr) { - return Err(Error::Checksum); + return Err(Error); } let control = match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) { @@ -817,7 +815,7 @@ impl<'a> Repr<'a> { (true, false, false, _) => Control::Syn, (false, true, false, _) => Control::Fin, (false, false, true, _) => Control::Rst, - _ => return Err(Error::Malformed), + _ => return Err(Error), }; let ack_number = match packet.ack() { true => Some(packet.ack_number()), @@ -1157,7 +1155,7 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_truncated() { let packet = Packet::new_unchecked(&PACKET_BYTES[..23]); - assert_eq!(packet.check_len(), Err(Error::Truncated)); + assert_eq!(packet.check_len(), Err(Error)); } #[test] @@ -1165,7 +1163,7 @@ mod test { let mut bytes = vec![0; 20]; let mut packet = Packet::new_unchecked(&mut bytes); packet.set_header_len(10); - assert_eq!(packet.check_len(), Err(Error::Malformed)); + assert_eq!(packet.check_len(), Err(Error)); } #[cfg(feature = "proto-ipv4")] @@ -1277,14 +1275,11 @@ mod test { #[test] fn test_malformed_tcp_options() { - assert_eq!(TcpOption::parse(&[]), Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0xc]), Err(Error::Truncated)); - assert_eq!( - TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]), - Err(Error::Truncated) - ); - assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error::Truncated)); - assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error::Malformed)); - assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error::Malformed)); + assert_eq!(TcpOption::parse(&[]), Err(Error)); + assert_eq!(TcpOption::parse(&[0xc]), Err(Error)); + assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]), Err(Error)); + assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error)); + assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error)); + assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error)); } } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 458ec509c..ba88d8705 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -1,10 +1,10 @@ use byteorder::{ByteOrder, NetworkEndian}; use core::fmt; +use super::{Error, Result}; use crate::phy::ChecksumCapabilities; use crate::wire::ip::checksum; use crate::wire::{IpAddress, IpProtocol}; -use crate::{Error, Result}; /// A read/write wrapper around an User Datagram Protocol packet buffer. #[derive(Debug, PartialEq, Clone)] @@ -48,8 +48,8 @@ impl> Packet { } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. - /// Returns `Err(Error::Malformed)` if the length field has a value smaller + /// Returns `Err(Error)` if the buffer is too short. + /// Returns `Err(Error)` if the length field has a value smaller /// than the header length. /// /// The result of this check is invalidated by calling [set_len]. @@ -58,13 +58,11 @@ impl> Packet { pub fn check_len(&self) -> Result<()> { let buffer_len = self.buffer.as_ref().len(); if buffer_len < HEADER_LEN { - Err(Error::Truncated) + Err(Error) } else { let field_len = self.len() as usize; - if buffer_len < field_len { - Err(Error::Truncated) - } else if field_len < HEADER_LEN { - Err(Error::Malformed) + if buffer_len < field_len || field_len < HEADER_LEN { + Err(Error) } else { Ok(()) } @@ -221,7 +219,7 @@ impl Repr { { // Destination port cannot be omitted (but source port can be). if packet.dst_port() == 0 { - return Err(Error::Malformed); + return Err(Error); } // Valid checksum is expected... if checksum_caps.udp.rx() && !packet.verify_checksum(src_addr, dst_addr) { @@ -229,7 +227,7 @@ impl Repr { // ... except on UDP-over-IPv4, where it can be omitted. #[cfg(feature = "proto-ipv4")] (&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) if packet.checksum() == 0 => (), - _ => return Err(Error::Checksum), + _ => return Err(Error), } } @@ -360,7 +358,7 @@ mod test { let mut bytes = vec![0; 12]; let mut packet = Packet::new_unchecked(&mut bytes); packet.set_len(4); - assert_eq!(packet.check_len(), Err(Error::Malformed)); + assert_eq!(packet.check_len(), Err(Error)); } #[test] From 72a9ee46ce58c9cddad0cb54c2b9e96a71377ccd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 02:11:10 +0200 Subject: [PATCH 330/566] socket: reorganize module structure, with one module per protocol. --- examples/benchmark.rs | 18 +++---- examples/client.rs | 12 ++--- examples/dhcp_client.rs | 10 ++-- examples/dns.rs | 8 +-- examples/httpclient.rs | 10 ++-- examples/loopback.rs | 18 +++---- examples/multicast.rs | 20 ++++---- examples/ping.rs | 12 ++--- examples/server.rs | 43 ++++++++-------- examples/sixlowpan.rs | 10 ++-- fuzz/fuzz_targets/tcp_headers.rs | 18 +++---- src/iface/interface.rs | 88 +++++++++++++++----------------- src/socket/dhcpv4.rs | 12 ++--- src/socket/dns.rs | 16 ++---- src/socket/icmp.rs | 55 +++++++++----------- src/socket/mod.rs | 51 +++++++----------- src/socket/raw.rs | 48 ++++++++--------- src/socket/tcp.rs | 16 +++--- src/socket/udp.rs | 36 ++++++------- 19 files changed, 231 insertions(+), 270 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index f9142c239..720894b20 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -13,7 +13,7 @@ use std::thread; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -85,13 +85,13 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let tcp1_rx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp1_tx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp1_socket = TcpSocket::new(tcp1_rx_buffer, tcp1_tx_buffer); + let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer); - let tcp2_rx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp2_tx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp2_socket = TcpSocket::new(tcp2_rx_buffer, tcp2_tx_buffer); + let tcp2_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; @@ -119,7 +119,7 @@ fn main() { } // tcp:1234: emit data - let socket = iface.get_socket::(tcp1_handle); + let socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -137,7 +137,7 @@ fn main() { } // tcp:1235: sink data - let socket = iface.get_socket::(tcp2_handle); + let socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } diff --git a/examples/client.rs b/examples/client.rs index 5aa4d1b79..6fa2dc7f7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -7,7 +7,7 @@ use std::str::{self, FromStr}; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::tcp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; @@ -30,9 +30,9 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]); - let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]); - let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 128]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)]; @@ -54,7 +54,7 @@ fn main() { let tcp_handle = iface.add_socket(tcp_socket); - let (socket, cx) = iface.get_socket_and_context::(tcp_handle); + let (socket, cx) = iface.get_socket_and_context::(tcp_handle); socket.connect(cx, (address, port), 49500).unwrap(); let mut tcp_active = false; @@ -67,7 +67,7 @@ fn main() { } } - let socket = iface.get_socket::(tcp_handle); + let socket = iface.get_socket::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 456668e37..72c26115a 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -6,7 +6,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes}; -use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket}; +use smoltcp::socket::dhcpv4; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; use smoltcp::{ @@ -44,7 +44,7 @@ fn main() { } let mut iface = builder.finalize(); - let mut dhcp_socket = Dhcpv4Socket::new(); + let mut dhcp_socket = dhcpv4::Socket::new(); // Set a ridiculously short max lease time to show DHCP renews work properly. // This will cause the DHCP client to start renewing after 5 seconds, and give up the @@ -60,10 +60,10 @@ fn main() { debug!("poll error: {}", e); } - let event = iface.get_socket::(dhcp_handle).poll(); + let event = iface.get_socket::(dhcp_handle).poll(); match event { None => {} - Some(Dhcpv4Event::Configured(config)) => { + Some(dhcpv4::Event::Configured(config)) => { debug!("DHCP config acquired!"); debug!("IP address: {}", config.address); @@ -83,7 +83,7 @@ fn main() { } } } - Some(Dhcpv4Event::Deconfigured) => { + Some(dhcpv4::Event::Deconfigured) => { debug!("DHCP lost config!"); set_ipv4_addr(&mut iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); iface.routes_mut().remove_default_ipv4_route(); diff --git a/examples/dns.rs b/examples/dns.rs index 4c5c76b1b..e48d2e349 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -10,7 +10,7 @@ mod utils; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::Device; use smoltcp::phy::{wait as phy_wait, Medium}; -use smoltcp::socket::DnsSocket; +use smoltcp::socket::dns; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, @@ -39,7 +39,7 @@ fn main() { Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into(), ]; - let dns_socket = DnsSocket::new(servers, vec![]); + let dns_socket = dns::Socket::new(servers, vec![]); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); @@ -68,7 +68,7 @@ fn main() { let dns_handle = iface.add_socket(dns_socket); - let (socket, cx) = iface.get_socket_and_context::(dns_handle); + let (socket, cx) = iface.get_socket_and_context::(dns_handle); let query = socket.start_query(cx, name).unwrap(); loop { @@ -83,7 +83,7 @@ fn main() { } match iface - .get_socket::(dns_handle) + .get_socket::(dns_handle) .get_query_result(query) { Ok(addrs) => { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 8748c0896..bda9b9510 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -8,7 +8,7 @@ use url::Url; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::tcp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; @@ -30,9 +30,9 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]); - let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 1024]); - let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addrs = [ @@ -76,7 +76,7 @@ fn main() { } } - let (socket, cx) = iface.get_socket_and_context::(tcp_handle); + let (socket, cx) = iface.get_socket_and_context::(tcp_handle); state = match state { State::Connect if !socket.is_active() => { diff --git a/examples/loopback.rs b/examples/loopback.rs index 183e0c50c..8fe5150a0 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -11,7 +11,7 @@ use log::{debug, error, info}; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{Loopback, Medium}; -use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -100,17 +100,17 @@ fn main() { // when stack overflows. static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024]; static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); + let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); + tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer) }; let client_socket = { static mut TCP_CLIENT_RX_DATA: [u8; 1024] = [0; 1024]; static mut TCP_CLIENT_TX_DATA: [u8; 1024] = [0; 1024]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] }); + let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] }); + tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer) }; let server_handle = iface.add_socket(server_socket); @@ -127,7 +127,7 @@ fn main() { } } - let mut socket = iface.get_socket::(server_handle); + let mut socket = iface.get_socket::(server_handle); if !socket.is_active() && !socket.is_listening() { if !did_listen { debug!("listening"); @@ -145,7 +145,7 @@ fn main() { done = true; } - let (mut socket, cx) = iface.get_socket_and_context::(client_handle); + let (mut socket, cx) = iface.get_socket_and_context::(client_handle); if !socket.is_open() { if !did_connect { debug!("connecting"); diff --git a/examples/multicast.rs b/examples/multicast.rs index 07be10c9f..6b94b1326 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -6,9 +6,7 @@ use std::os::unix::io::AsRawFd; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::wait as phy_wait; -use smoltcp::socket::{ - RawPacketMetadata, RawSocket, RawSocketBuffer, UdpPacketMetadata, UdpSocket, UdpSocketBuffer, -}; +use smoltcp::socket::{raw, udp}; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address, @@ -50,10 +48,10 @@ fn main() { .unwrap(); // Must fit at least one IGMP packet - let raw_rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; 2], vec![0; 512]); + let raw_rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; 2], vec![0; 512]); // Will not send IGMP - let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]); - let raw_socket = RawSocket::new( + let raw_tx_buffer = raw::PacketBuffer::new(vec![], vec![]); + let raw_socket = raw::Socket::new( IpVersion::Ipv4, IpProtocol::Igmp, raw_rx_buffer, @@ -62,10 +60,10 @@ fn main() { let raw_handle = iface.add_socket(raw_socket); // Must fit mDNS payload of at least one packet - let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 4], vec![0; 1024]); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY; 4], vec![0; 1024]); // Will not send mDNS - let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 0]); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 0]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let udp_handle = iface.add_socket(udp_socket); loop { @@ -77,7 +75,7 @@ fn main() { } } - let socket = iface.get_socket::(raw_handle); + let socket = iface.get_socket::(raw_handle); if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets @@ -94,7 +92,7 @@ fn main() { } } - let socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(MDNS_PORT).unwrap() } diff --git a/examples/ping.rs b/examples/ping.rs index 46685c703..cf240550d 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::wait as phy_wait; use smoltcp::phy::Device; -use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; +use smoltcp::socket::icmp; use smoltcp::wire::{ EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr, Ipv4Address, Ipv6Address, @@ -108,9 +108,9 @@ fn main() { let remote_addr = address; - let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]); - let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]); - let icmp_socket = IcmpSocket::new(icmp_rx_buffer, icmp_tx_buffer); + let icmp_rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]); + let icmp_tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]); + let icmp_socket = icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); @@ -156,9 +156,9 @@ fn main() { } let timestamp = Instant::now(); - let socket = iface.get_socket::(icmp_handle); + let socket = iface.get_socket::(icmp_handle); if !socket.is_open() { - socket.bind(IcmpEndpoint::Ident(ident)).unwrap(); + socket.bind(icmp::Endpoint::Ident(ident)).unwrap(); send_at = timestamp; } diff --git a/examples/server.rs b/examples/server.rs index d518b3349..a5db0a499 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -8,8 +8,7 @@ use std::str; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; -use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; -use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; +use smoltcp::socket::{tcp, udp}; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -27,25 +26,25 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 64]); - let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 128]); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let tcp1_rx_buffer = TcpSocketBuffer::new(vec![0; 64]); - let tcp1_tx_buffer = TcpSocketBuffer::new(vec![0; 128]); - let tcp1_socket = TcpSocket::new(tcp1_rx_buffer, tcp1_tx_buffer); + let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); + let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 128]); + let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer); - let tcp2_rx_buffer = TcpSocketBuffer::new(vec![0; 64]); - let tcp2_tx_buffer = TcpSocketBuffer::new(vec![0; 128]); - let tcp2_socket = TcpSocket::new(tcp2_rx_buffer, tcp2_tx_buffer); + let tcp2_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); + let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 128]); + let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); - let tcp3_rx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp3_tx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp3_socket = TcpSocket::new(tcp3_rx_buffer, tcp3_tx_buffer); + let tcp3_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp3_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp3_socket = tcp::Socket::new(tcp3_rx_buffer, tcp3_tx_buffer); - let tcp4_rx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp4_tx_buffer = TcpSocketBuffer::new(vec![0; 65535]); - let tcp4_socket = TcpSocket::new(tcp4_rx_buffer, tcp4_tx_buffer); + let tcp4_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp4_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); + let tcp4_socket = tcp::Socket::new(tcp4_rx_buffer, tcp4_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [ @@ -80,7 +79,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -106,7 +105,7 @@ fn main() { } // tcp:6969: respond "hello" - let socket = iface.get_socket::(tcp1_handle); + let socket = iface.get_socket::(tcp1_handle); if !socket.is_open() { socket.listen(6969).unwrap(); } @@ -119,7 +118,7 @@ fn main() { } // tcp:6970: echo with reverse - let socket = iface.get_socket::(tcp2_handle); + let socket = iface.get_socket::(tcp2_handle); if !socket.is_open() { socket.listen(6970).unwrap() } @@ -161,7 +160,7 @@ fn main() { } // tcp:6971: sinkhole - let socket = iface.get_socket::(tcp3_handle); + let socket = iface.get_socket::(tcp3_handle); if !socket.is_open() { socket.listen(6971).unwrap(); socket.set_keep_alive(Some(Duration::from_millis(1000))); @@ -182,7 +181,7 @@ fn main() { } // tcp:6972: fountain - let socket = iface.get_socket::(tcp4_handle); + let socket = iface.get_socket::(tcp4_handle); if !socket.is_open() { socket.listen(6972).unwrap() } diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 234b96443..6d5ce8fae 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -49,7 +49,7 @@ use std::str; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; -use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; +use smoltcp::socket::udp; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -68,9 +68,9 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 64]); - let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 128]); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, @@ -100,7 +100,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = iface.get_socket::(udp_handle); + let socket = iface.get_socket::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index a31c37f91..e7c90444d 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::phy::{Loopback, Medium}; -use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket}; @@ -145,17 +145,17 @@ fuzz_target!(|data: &[u8]| { // when stack overflows. static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024]; static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); + let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); + tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer) }; let client_socket = { static mut TCP_CLIENT_RX_DATA: [u8; 1024] = [0; 1024]; static mut TCP_CLIENT_TX_DATA: [u8; 1024] = [0; 1024]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] }); + let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] }); + tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer) }; let mut socket_set_entries: [_; 2] = Default::default(); @@ -170,7 +170,7 @@ fuzz_target!(|data: &[u8]| { let _ = iface.poll(&mut socket_set, clock.elapsed()); { - let mut socket = socket_set.get::(server_handle); + let mut socket = socket_set.get::(server_handle); if !socket.is_active() && !socket.is_listening() { if !did_listen { socket.listen(1234).unwrap(); @@ -185,7 +185,7 @@ fuzz_target!(|data: &[u8]| { } { - let mut socket = socket_set.get::(client_handle); + let mut socket = socket_set.get::(client_handle); if !socket.is_open() { if !did_connect { socket diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 510092928..ef10648d4 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -15,6 +15,10 @@ use crate::iface::Routes; use crate::iface::{NeighborAnswer, NeighborCache}; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::rand::Rand; +#[cfg(feature = "socket-dhcpv4")] +use crate::socket::dhcpv4; +#[cfg(feature = "socket-dns")] +use crate::socket::dns; use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; @@ -1392,7 +1396,7 @@ impl<'a> InterfaceInner<'a> { // If it does not accept the packet, then send an ICMP message. for udp_socket in sockets .iter_mut() - .filter_map(|i| UdpSocket::downcast(&mut i.socket)) + .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { if !udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { continue; @@ -1517,7 +1521,7 @@ impl<'a> InterfaceInner<'a> { // Pass every IP packet to all raw sockets we have registered. for raw_socket in sockets .iter_mut() - .filter_map(|i| RawSocket::downcast(&mut i.socket)) + .filter_map(|i| raw::Socket::downcast(&mut i.socket)) { if !raw_socket.accepts(ip_repr) { continue; @@ -1643,7 +1647,7 @@ impl<'a> InterfaceInner<'a> { { if let Some(dhcp_socket) = sockets .iter_mut() - .filter_map(|i| Dhcpv4Socket::downcast(&mut i.socket)) + .filter_map(|i| dhcpv4::Socket::downcast(&mut i.socket)) .next() { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); @@ -1822,7 +1826,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] for icmp_socket in _sockets .iter_mut() - .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) + .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { continue; @@ -2009,7 +2013,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] for icmp_socket in _sockets .iter_mut() - .filter_map(|i| IcmpSocket::downcast(&mut i.socket)) + .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { continue; @@ -2137,7 +2141,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] for udp_socket in sockets .iter_mut() - .filter_map(|i| UdpSocket::downcast(&mut i.socket)) + .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { if !udp_socket.accepts(self, &ip_repr, &udp_repr) { continue; @@ -2154,7 +2158,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dns")] for dns_socket in sockets .iter_mut() - .filter_map(|i| DnsSocket::downcast(&mut i.socket)) + .filter_map(|i| dns::Socket::downcast(&mut i.socket)) { if !dns_socket.accepts(&ip_repr, &udp_repr) { continue; @@ -2212,7 +2216,7 @@ impl<'a> InterfaceInner<'a> { for tcp_socket in sockets .iter_mut() - .filter_map(|i| TcpSocket::downcast(&mut i.socket)) + .filter_map(|i| tcp::Socket::downcast(&mut i.socket)) { if !tcp_socket.accepts(self, &ip_repr, &tcp_repr) { continue; @@ -2232,7 +2236,7 @@ impl<'a> InterfaceInner<'a> { Ok(None) } else { // The packet wasn't handled by a socket, send a TCP RST packet. - Ok(Some(IpPacket::Tcp(TcpSocket::rst_reply( + Ok(Some(IpPacket::Tcp(tcp::Socket::rst_reply( &ip_repr, &tcp_repr, )))) } @@ -3092,17 +3096,16 @@ mod test { #[test] #[cfg(feature = "socket-udp")] fn test_handle_udp_broadcast() { - use crate::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; use crate::wire::IpEndpoint; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; let mut iface = create_loopback(); - let rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); - let tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); + let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let udp_socket = UdpSocket::new(rx_buffer, tx_buffer); + let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); let mut udp_bytes = vec![0u8; 13]; let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); @@ -3137,7 +3140,7 @@ mod test { }); // Bind the socket to port 68 - let socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -3161,7 +3164,7 @@ mod test { // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer - let socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3592,15 +3595,14 @@ mod test { #[test] #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] fn test_icmpv4_socket() { - use crate::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; use crate::wire::Icmpv4Packet; let mut iface = create_loopback(); - let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 24]); - let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 24]); + let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); + let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); - let icmpv4_socket = IcmpSocket::new(rx_buffer, tx_buffer); + let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); let socket_handle = iface.add_socket(icmpv4_socket); @@ -3608,9 +3610,9 @@ mod test { let seq_no = 0x5432; let echo_data = &[0xff; 16]; - let socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); // Bind to the ID 0x1234 - assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(())); + assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); // Ensure the ident we bound to and the ident of the packet are the same. let mut bytes = [0xff; 24]; @@ -3634,7 +3636,7 @@ mod test { // Open a socket and ensure the packet is handled due to the listening // socket. - assert!(!iface.get_socket::(socket_handle).can_recv()); + assert!(!iface.get_socket::(socket_handle).can_recv()); // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening let echo_reply = Icmpv4Repr::EchoReply { @@ -3654,7 +3656,7 @@ mod test { Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) ); - let socket = iface.get_socket::(socket_handle); + let socket = iface.get_socket::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3852,19 +3854,18 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_no_reply() { - use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let mut iface = create_loopback(); let packets = 1; let rx_buffer = - RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = RawSocketBuffer::new( - vec![RawPacketMetadata::EMPTY; packets], + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * packets], ); - let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); + let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); iface.add_socket(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); @@ -3921,19 +3922,18 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] fn test_raw_socket_truncated_packet() { - use crate::socket::{RawPacketMetadata, RawSocket, RawSocketBuffer}; use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; let mut iface = create_loopback(); let packets = 1; let rx_buffer = - RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = RawSocketBuffer::new( - vec![RawPacketMetadata::EMPTY; packets], + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * packets], ); - let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); + let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); iface.add_socket(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); @@ -3994,35 +3994,31 @@ mod test { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] fn test_raw_socket_with_udp_socket() { - use crate::socket::{ - RawPacketMetadata, RawSocket, RawSocketBuffer, UdpPacketMetadata, UdpSocket, - UdpSocketBuffer, - }; use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; let mut iface = create_loopback(); - let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); - let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 15]); - let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let udp_socket_handle = iface.add_socket(udp_socket); // Bind the socket to port 68 - let socket = iface.get_socket::(udp_socket_handle); + let socket = iface.get_socket::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); let packets = 1; let raw_rx_buffer = - RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let raw_tx_buffer = RawSocketBuffer::new( - vec![RawPacketMetadata::EMPTY; packets], + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let raw_tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * packets], ); - let raw_socket = RawSocket::new( + let raw_socket = raw::Socket::new( IpVersion::Ipv4, IpProtocol::Udp, raw_rx_buffer, @@ -4080,7 +4076,7 @@ mod test { ); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = iface.get_socket::(udp_socket_handle); + let socket = iface.get_socket::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d74cdffde..b138a66f4 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -110,7 +110,7 @@ pub enum Event { } #[derive(Debug)] -pub struct Dhcpv4Socket { +pub struct Socket { /// State of the DHCP client. state: ClientState, /// Set to true on config/state change, cleared back to false by the `config` function. @@ -131,11 +131,11 @@ pub struct Dhcpv4Socket { /// The socket acquires an IP address configuration through DHCP autonomously. /// You must query the configuration with `.poll()` after every call to `Interface::poll()`, /// and apply the configuration to the `Interface`. -impl Dhcpv4Socket { +impl Socket { /// Create a DHCPv4 socket #[allow(clippy::new_without_default)] pub fn new() -> Self { - Dhcpv4Socket { + Socket { state: ClientState::Discovering(DiscoverState { retry_at: Instant::from_millis(0), }), @@ -561,12 +561,12 @@ mod test { // Helper functions struct TestSocket { - socket: Dhcpv4Socket, + socket: Socket, cx: Context<'static>, } impl Deref for TestSocket { - type Target = Dhcpv4Socket; + type Target = Socket; fn deref(&self) -> &Self::Target { &self.socket } @@ -797,7 +797,7 @@ mod test { // Tests fn socket() -> TestSocket { - let mut s = Dhcpv4Socket::new(); + let mut s = Socket::new(); assert_eq!(s.poll(), Some(Event::Deconfigured)); TestSocket { socket: s, diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 37a6aba23..a02680b8a 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -4,7 +4,7 @@ use core::task::Waker; use heapless::Vec; use managed::ManagedSlice; -use crate::socket::{Context, PollAt, Socket}; +use crate::socket::{Context, PollAt}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; @@ -78,7 +78,7 @@ pub struct QueryHandle(usize); /// A UDP socket is bound to a specific endpoint, and owns transmit and receive /// packet buffers. #[derive(Debug)] -pub struct DnsSocket<'a> { +pub struct Socket<'a> { servers: Vec, queries: ManagedSlice<'a, Option>, @@ -86,17 +86,17 @@ pub struct DnsSocket<'a> { hop_limit: Option, } -impl<'a> DnsSocket<'a> { +impl<'a> Socket<'a> { /// Create a DNS socket. /// /// # Panics /// /// Panics if `servers.len() > MAX_SERVER_COUNT` - pub fn new(servers: &[IpAddress], queries: Q) -> DnsSocket<'a> + pub fn new(servers: &[IpAddress], queries: Q) -> Socket<'a> where Q: Into>>, { - DnsSocket { + Socket { servers: Vec::from_slice(servers).unwrap(), queries: queries.into(), hop_limit: None, @@ -502,12 +502,6 @@ impl<'a> DnsSocket<'a> { } } -impl<'a> From> for Socket<'a> { - fn from(val: DnsSocket<'a>) -> Self { - Socket::Dns(val) - } -} - fn eq_names<'a>( mut a: impl Iterator>, mut b: impl Iterator>, diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 4cfdfbfa6..68c8f2820 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -6,7 +6,6 @@ use crate::phy::ChecksumCapabilities; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::socket::{Context, PollAt}; -use crate::storage::{PacketBuffer, PacketMetadata}; use crate::{Error, Result}; use crate::wire::IcmpRepr; @@ -46,10 +45,10 @@ impl Default for Endpoint { } /// An ICMP packet metadata. -pub type IcmpPacketMetadata = PacketMetadata; +pub type PacketMetadata = crate::storage::PacketMetadata; /// An ICMP packet ring buffer. -pub type IcmpSocketBuffer<'a> = PacketBuffer<'a, IpAddress>; +pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpAddress>; /// A ICMP socket /// @@ -61,9 +60,9 @@ pub type IcmpSocketBuffer<'a> = PacketBuffer<'a, IpAddress>; /// [IcmpEndpoint]: enum.IcmpEndpoint.html /// [bind]: #method.bind #[derive(Debug)] -pub struct IcmpSocket<'a> { - rx_buffer: IcmpSocketBuffer<'a>, - tx_buffer: IcmpSocketBuffer<'a>, +pub struct Socket<'a> { + rx_buffer: PacketBuffer<'a>, + tx_buffer: PacketBuffer<'a>, /// The endpoint this socket is communicating with endpoint: Endpoint, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -74,10 +73,10 @@ pub struct IcmpSocket<'a> { tx_waker: WakerRegistration, } -impl<'a> IcmpSocket<'a> { +impl<'a> Socket<'a> { /// Create an ICMP socket with the given buffers. - pub fn new(rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> { - IcmpSocket { + pub fn new(rx_buffer: PacketBuffer<'a>, tx_buffer: PacketBuffer<'a>) -> Socket<'a> { + Socket { rx_buffer: rx_buffer, tx_buffer: tx_buffer, endpoint: Default::default(), @@ -167,18 +166,17 @@ impl<'a> IcmpSocket<'a> { /// diagnose connection problems. /// /// ``` - /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata}; - /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); - /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); /// use smoltcp::wire::IpListenEndpoint; - /// use smoltcp::socket::IcmpEndpoint; + /// use smoltcp::socket::icmp; + /// # let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 20]); + /// # let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 20]); /// /// let mut icmp_socket = // ... - /// # IcmpSocket::new(rx_buffer, tx_buffer); + /// # icmp::Socket::new(rx_buffer, tx_buffer); /// /// // Bind to ICMP error responses for UDP packets sent from port 53. /// let endpoint = IpListenEndpoint::from(53); - /// icmp_socket.bind(IcmpEndpoint::Udp(endpoint)).unwrap(); + /// icmp_socket.bind(icmp::Endpoint::Udp(endpoint)).unwrap(); /// ``` /// /// ## Bind to a specific ICMP identifier: @@ -189,16 +187,16 @@ impl<'a> IcmpSocket<'a> { /// messages. /// /// ``` - /// # use smoltcp::socket::{Socket, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata}; - /// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); - /// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 20]); - /// use smoltcp::socket::IcmpEndpoint; + /// use smoltcp::wire::IpListenEndpoint; + /// use smoltcp::socket::icmp; + /// # let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 20]); + /// # let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 20]); /// /// let mut icmp_socket = // ... - /// # IcmpSocket::new(rx_buffer, tx_buffer); + /// # icmp::Socket::new(rx_buffer, tx_buffer); /// /// // Bind to ICMP messages with the ICMP identifier 0x1234 - /// icmp_socket.bind(IcmpEndpoint::Ident(0x1234)).unwrap(); + /// icmp_socket.bind(icmp::Endpoint::Ident(0x1234)).unwrap(); /// ``` /// /// [is_specified]: enum.IcmpEndpoint.html#method.is_specified @@ -509,18 +507,15 @@ mod tests_common { pub use crate::phy::DeviceCapabilities; pub use crate::wire::IpAddress; - pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static> { - IcmpSocketBuffer::new( - vec![IcmpPacketMetadata::EMPTY; packets], - vec![0; 66 * packets], - ) + pub fn buffer(packets: usize) -> PacketBuffer<'static> { + PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 66 * packets]) } pub fn socket( - rx_buffer: IcmpSocketBuffer<'static>, - tx_buffer: IcmpSocketBuffer<'static>, - ) -> IcmpSocket<'static> { - IcmpSocket::new(rx_buffer, tx_buffer) + rx_buffer: PacketBuffer<'static>, + tx_buffer: PacketBuffer<'static>, + ) -> Socket<'static> { + Socket::new(rx_buffer, tx_buffer) } pub const LOCAL_PORT: u16 = 53; diff --git a/src/socket/mod.rs b/src/socket/mod.rs index bf65add25..7f64c48e6 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -15,34 +15,21 @@ use crate::iface::Context; use crate::time::Instant; #[cfg(feature = "socket-dhcpv4")] -mod dhcpv4; +pub mod dhcpv4; #[cfg(feature = "socket-dns")] -mod dns; +pub mod dns; #[cfg(feature = "socket-icmp")] -mod icmp; +pub mod icmp; #[cfg(feature = "socket-raw")] -mod raw; +pub mod raw; #[cfg(feature = "socket-tcp")] -mod tcp; +pub mod tcp; #[cfg(feature = "socket-udp")] -mod udp; +pub mod udp; #[cfg(feature = "async")] mod waker; -#[cfg(feature = "socket-dhcpv4")] -pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event}; -#[cfg(feature = "socket-dns")] -pub use self::dns::{DnsQuery, DnsSocket, QueryHandle as DnsQueryHandle}; -#[cfg(feature = "socket-icmp")] -pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer}; -#[cfg(feature = "socket-raw")] -pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer}; -#[cfg(feature = "socket-tcp")] -pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket}; -#[cfg(feature = "socket-udp")] -pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer}; - #[cfg(feature = "async")] pub(crate) use self::waker::WakerRegistration; @@ -62,7 +49,7 @@ pub(crate) enum PollAt { /// /// This enumeration abstracts the various types of sockets based on the IP protocol. /// To downcast a `Socket` value to a concrete socket, use the [AnySocket] trait, -/// e.g. to get `UdpSocket`, call `UdpSocket::downcast(socket)`. +/// e.g. to get `udp::Socket`, call `udp::Socket::downcast(socket)`. /// /// It is usually more convenient to use [SocketSet::get] instead. /// @@ -71,17 +58,17 @@ pub(crate) enum PollAt { #[derive(Debug)] pub enum Socket<'a> { #[cfg(feature = "socket-raw")] - Raw(RawSocket<'a>), + Raw(raw::Socket<'a>), #[cfg(feature = "socket-icmp")] - Icmp(IcmpSocket<'a>), + Icmp(icmp::Socket<'a>), #[cfg(feature = "socket-udp")] - Udp(UdpSocket<'a>), + Udp(udp::Socket<'a>), #[cfg(feature = "socket-tcp")] - Tcp(TcpSocket<'a>), + Tcp(tcp::Socket<'a>), #[cfg(feature = "socket-dhcpv4")] - Dhcpv4(Dhcpv4Socket), + Dhcpv4(dhcpv4::Socket), #[cfg(feature = "socket-dns")] - Dns(DnsSocket<'a>), + Dns(dns::Socket<'a>), } impl<'a> Socket<'a> { @@ -128,14 +115,14 @@ macro_rules! from_socket { } #[cfg(feature = "socket-raw")] -from_socket!(RawSocket<'a>, Raw); +from_socket!(raw::Socket<'a>, Raw); #[cfg(feature = "socket-icmp")] -from_socket!(IcmpSocket<'a>, Icmp); +from_socket!(icmp::Socket<'a>, Icmp); #[cfg(feature = "socket-udp")] -from_socket!(UdpSocket<'a>, Udp); +from_socket!(udp::Socket<'a>, Udp); #[cfg(feature = "socket-tcp")] -from_socket!(TcpSocket<'a>, Tcp); +from_socket!(tcp::Socket<'a>, Tcp); #[cfg(feature = "socket-dhcpv4")] -from_socket!(Dhcpv4Socket, Dhcpv4); +from_socket!(dhcpv4::Socket, Dhcpv4); #[cfg(feature = "socket-dns")] -from_socket!(DnsSocket<'a>, Dns); +from_socket!(dns::Socket<'a>, Dns); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index ba5b9ab4f..e92b785c9 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -7,7 +7,6 @@ use crate::phy::ChecksumCapabilities; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::storage::{PacketBuffer, PacketMetadata}; use crate::{Error, Result}; use crate::wire::{IpProtocol, IpRepr, IpVersion}; @@ -17,37 +16,37 @@ use crate::wire::{Ipv4Packet, Ipv4Repr}; use crate::wire::{Ipv6Packet, Ipv6Repr}; /// A UDP packet metadata. -pub type RawPacketMetadata = PacketMetadata<()>; +pub type PacketMetadata = crate::storage::PacketMetadata<()>; /// A UDP packet ring buffer. -pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>; +pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, ()>; /// A raw IP socket. /// /// A raw socket is bound to a specific IP protocol, and owns /// transmit and receive packet buffers. #[derive(Debug)] -pub struct RawSocket<'a> { +pub struct Socket<'a> { ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a>, - tx_buffer: RawSocketBuffer<'a>, + rx_buffer: PacketBuffer<'a>, + tx_buffer: PacketBuffer<'a>, #[cfg(feature = "async")] rx_waker: WakerRegistration, #[cfg(feature = "async")] tx_waker: WakerRegistration, } -impl<'a> RawSocket<'a> { +impl<'a> Socket<'a> { /// Create a raw IP socket bound to the given IP version and datagram protocol, /// with the given buffers. pub fn new( ip_version: IpVersion, ip_protocol: IpProtocol, - rx_buffer: RawSocketBuffer<'a>, - tx_buffer: RawSocketBuffer<'a>, - ) -> RawSocket<'a> { - RawSocket { + rx_buffer: PacketBuffer<'a>, + tx_buffer: PacketBuffer<'a>, + ) -> Socket<'a> { + Socket { ip_version, ip_protocol, rx_buffer, @@ -330,11 +329,8 @@ mod test { #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Repr}; - fn buffer(packets: usize) -> RawSocketBuffer<'static> { - RawSocketBuffer::new( - vec![RawPacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ) + fn buffer(packets: usize) -> PacketBuffer<'static> { + PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 48 * packets]) } #[cfg(feature = "proto-ipv4")] @@ -342,10 +338,10 @@ mod test { use super::*; pub fn socket( - rx_buffer: RawSocketBuffer<'static>, - tx_buffer: RawSocketBuffer<'static>, - ) -> RawSocket<'static> { - RawSocket::new( + rx_buffer: PacketBuffer<'static>, + tx_buffer: PacketBuffer<'static>, + ) -> Socket<'static> { + Socket::new( IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO), rx_buffer, @@ -373,10 +369,10 @@ mod test { use super::*; pub fn socket( - rx_buffer: RawSocketBuffer<'static>, - tx_buffer: RawSocketBuffer<'static>, - ) -> RawSocket<'static> { - RawSocket::new( + rx_buffer: PacketBuffer<'static>, + tx_buffer: PacketBuffer<'static>, + ) -> Socket<'static> { + Socket::new( IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO), rx_buffer, @@ -615,7 +611,7 @@ mod test { fn test_doesnt_accept_wrong_proto() { #[cfg(feature = "proto-ipv4")] { - let socket = RawSocket::new( + let socket = Socket::new( IpVersion::Ipv4, IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1), buffer(1), @@ -627,7 +623,7 @@ mod test { } #[cfg(feature = "proto-ipv6")] { - let socket = RawSocket::new( + let socket = Socket::new( IpVersion::Ipv6, IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1), buffer(1), diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 940b1231d..fdd64ebd7 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -326,7 +326,7 @@ impl Display for Tuple { /// accept several connections, as many sockets must be allocated, or any new connection /// attempts will be reset. #[derive(Debug)] -pub struct TcpSocket<'a> { +pub struct Socket<'a> { state: State, timer: Timer, rtte: RttEstimator, @@ -401,10 +401,10 @@ pub struct TcpSocket<'a> { const DEFAULT_MSS: usize = 536; -impl<'a> TcpSocket<'a> { +impl<'a> Socket<'a> { #[allow(unused_comparisons)] // small usize platforms always pass rx_capacity check /// Create a socket using the given buffers. - pub fn new(rx_buffer: T, tx_buffer: T) -> TcpSocket<'a> + pub fn new(rx_buffer: T, tx_buffer: T) -> Socket<'a> where T: Into>, { @@ -420,7 +420,7 @@ impl<'a> TcpSocket<'a> { } let rx_cap_log2 = mem::size_of::() * 8 - rx_capacity.leading_zeros() as usize; - TcpSocket { + Socket { state: State::Closed, timer: Timer::new(), rtte: RttEstimator::default(), @@ -2224,7 +2224,7 @@ impl<'a> TcpSocket<'a> { } } -impl<'a> fmt::Write for TcpSocket<'a> { +impl<'a> fmt::Write for Socket<'a> { fn write_str(&mut self, slice: &str) -> fmt::Result { let slice = slice.as_bytes(); if self.send_slice(slice) == Ok(slice.len()) { @@ -2344,12 +2344,12 @@ mod test { // =========================================================================================// struct TestSocket { - socket: TcpSocket<'static>, + socket: Socket<'static>, cx: Context<'static>, } impl Deref for TestSocket { - type Target = TcpSocket<'static>; + type Target = Socket<'static>; fn deref(&self) -> &Self::Target { &self.socket } @@ -2465,7 +2465,7 @@ mod test { fn socket_with_buffer_sizes(tx_len: usize, rx_len: usize) -> TestSocket { let rx_buffer = SocketBuffer::new(vec![0; rx_len]); let tx_buffer = SocketBuffer::new(vec![0; tx_len]); - let mut socket = TcpSocket::new(rx_buffer, tx_buffer); + let mut socket = Socket::new(rx_buffer, tx_buffer); socket.set_ack_delay(None); let cx = Context::mock(); TestSocket { socket, cx } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index c72447499..e91068b31 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -6,25 +6,24 @@ use crate::iface::Context; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::storage::{PacketBuffer, PacketMetadata}; use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; use crate::{Error, Result}; /// A UDP packet metadata. -pub type UdpPacketMetadata = PacketMetadata; +pub type PacketMetadata = crate::storage::PacketMetadata; /// A UDP packet ring buffer. -pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>; +pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpEndpoint>; /// A User Datagram Protocol socket. /// /// A UDP socket is bound to a specific endpoint, and owns transmit and receive /// packet buffers. #[derive(Debug)] -pub struct UdpSocket<'a> { +pub struct Socket<'a> { endpoint: IpListenEndpoint, - rx_buffer: UdpSocketBuffer<'a>, - tx_buffer: UdpSocketBuffer<'a>, + rx_buffer: PacketBuffer<'a>, + tx_buffer: PacketBuffer<'a>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. hop_limit: Option, #[cfg(feature = "async")] @@ -33,10 +32,10 @@ pub struct UdpSocket<'a> { tx_waker: WakerRegistration, } -impl<'a> UdpSocket<'a> { +impl<'a> Socket<'a> { /// Create an UDP socket with the given buffers. - pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> { - UdpSocket { + pub fn new(rx_buffer: PacketBuffer<'a>, tx_buffer: PacketBuffer<'a>) -> Socket<'a> { + Socket { endpoint: IpListenEndpoint::default(), rx_buffer, tx_buffer, @@ -401,18 +400,15 @@ mod test { use super::*; use crate::wire::{IpRepr, UdpRepr}; - fn buffer(packets: usize) -> UdpSocketBuffer<'static> { - UdpSocketBuffer::new( - vec![UdpPacketMetadata::EMPTY; packets], - vec![0; 16 * packets], - ) + fn buffer(packets: usize) -> PacketBuffer<'static> { + PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets]) } fn socket( - rx_buffer: UdpSocketBuffer<'static>, - tx_buffer: UdpSocketBuffer<'static>, - ) -> UdpSocket<'static> { - UdpSocket::new(rx_buffer, tx_buffer) + rx_buffer: PacketBuffer<'static>, + tx_buffer: PacketBuffer<'static>, + ) -> Socket<'static> { + Socket::new(rx_buffer, tx_buffer) } const LOCAL_PORT: u16 = 53; @@ -735,7 +731,7 @@ mod test { #[test] fn test_process_empty_payload() { - let recv_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![]); + let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]); let mut socket = socket(recv_buffer, buffer(0)); let mut cx = Context::mock(); @@ -751,7 +747,7 @@ mod test { #[test] fn test_closing() { - let recv_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![]); + let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]); let mut socket = socket(recv_buffer, buffer(0)); assert_eq!(socket.bind(LOCAL_PORT), Ok(())); From 591b789d1ea4ad309f321681f1759ed1c7abe08a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 02:12:34 +0200 Subject: [PATCH 331/566] tcp: return own error enums for public API. --- src/socket/tcp.rs | 111 +++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index fdd64ebd7..53aec9a2b 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -16,12 +16,43 @@ use crate::wire::{ IpAddress, IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber, TCP_HEADER_LEN, }; -use crate::{Error, Result}; +use crate::Error; macro_rules! tcp_trace { ($($arg:expr),*) => (net_log!(trace, $($arg),*)); } +/// Error returned by [`Socket::listen`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ListenError { + InvalidState, + Unaddressable, +} + +/// Error returned by [`Socket::connect`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConnectError { + InvalidState, + Unaddressable, +} + +/// Error returned by [`Socket::send`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SendError { + InvalidState, +} + +/// Error returned by [`Socket::recv`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + InvalidState, + Finished, +} + /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; @@ -676,17 +707,17 @@ impl<'a> Socket<'a> { /// This function returns `Err(Error::Illegal)` if the socket was already open /// (see [is_open](#method.is_open)), and `Err(Error::Unaddressable)` /// if the port in the given endpoint is zero. - pub fn listen(&mut self, local_endpoint: T) -> Result<()> + pub fn listen(&mut self, local_endpoint: T) -> Result<(), ListenError> where T: Into, { let local_endpoint = local_endpoint.into(); if local_endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(ListenError::Unaddressable); } if self.is_open() { - return Err(Error::Illegal); + return Err(ListenError::InvalidState); } self.reset(); @@ -715,7 +746,7 @@ impl<'a> Socket<'a> { cx: &mut Context, remote_endpoint: T, local_endpoint: U, - ) -> Result<()> + ) -> Result<(), ConnectError> where T: Into, U: Into, @@ -724,13 +755,13 @@ impl<'a> Socket<'a> { let local_endpoint: IpListenEndpoint = local_endpoint.into(); if self.is_open() { - return Err(Error::Illegal); + return Err(ConnectError::InvalidState); } if remote_endpoint.port == 0 || remote_endpoint.addr.is_unspecified() { - return Err(Error::Unaddressable); + return Err(ConnectError::Unaddressable); } if local_endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(ConnectError::Unaddressable); } // If local address is not provided, choose it automatically. @@ -738,19 +769,19 @@ impl<'a> Socket<'a> { addr: match local_endpoint.addr { Some(addr) => { if addr.is_unspecified() { - return Err(Error::Unaddressable); + return Err(ConnectError::Unaddressable); } addr } None => cx .get_source_address(remote_endpoint.addr) - .ok_or(Error::Unaddressable)?, + .ok_or(ConnectError::Unaddressable)?, }, port: local_endpoint.port, }; if local_endpoint.addr.version() != remote_endpoint.addr.version() { - return Err(Error::Illegal); + return Err(ConnectError::Unaddressable); } self.reset(); @@ -940,12 +971,12 @@ impl<'a> Socket<'a> { !self.rx_buffer.is_empty() } - fn send_impl<'b, F, R>(&'b mut self, f: F) -> Result + fn send_impl<'b, F, R>(&'b mut self, f: F) -> Result where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R), { if !self.may_send() { - return Err(Error::Illegal); + return Err(SendError::InvalidState); } // The connection might have been idle for a long time, and so remote_last_ts @@ -973,7 +1004,7 @@ impl<'a> Socket<'a> { /// /// This function returns `Err(Error::Illegal)` if the transmit half of /// the connection is not open; see [may_send](#method.may_send). - pub fn send<'b, F, R>(&'b mut self, f: F) -> Result + pub fn send<'b, F, R>(&'b mut self, f: F) -> Result where F: FnOnce(&'b mut [u8]) -> (usize, R), { @@ -986,28 +1017,28 @@ impl<'a> Socket<'a> { /// by the amount of free space in the transmit buffer; down to zero. /// /// See also [send](#method.send). - pub fn send_slice(&mut self, data: &[u8]) -> Result { + pub fn send_slice(&mut self, data: &[u8]) -> Result { self.send_impl(|tx_buffer| { let size = tx_buffer.enqueue_slice(data); (size, size) }) } - fn recv_error_check(&mut self) -> Result<()> { + fn recv_error_check(&mut self) -> Result<(), RecvError> { // We may have received some data inside the initial SYN, but until the connection // is fully open we must not dequeue any data, as it may be overwritten by e.g. // another (stale) SYN. (We do not support TCP Fast Open.) if !self.may_recv() { if self.rx_fin_received { - return Err(Error::Finished); + return Err(RecvError::Finished); } - return Err(Error::Illegal); + return Err(RecvError::InvalidState); } Ok(()) } - fn recv_impl<'b, F, R>(&'b mut self, f: F) -> Result + fn recv_impl<'b, F, R>(&'b mut self, f: F) -> Result where F: FnOnce(&'b mut SocketBuffer<'a>) -> (usize, R), { @@ -1037,7 +1068,7 @@ impl<'a> Socket<'a> { /// /// In all other cases, `Err(Error::Illegal)` is returned and previously received data (if any) /// may be incomplete (truncated). - pub fn recv<'b, F, R>(&'b mut self, f: F) -> Result + pub fn recv<'b, F, R>(&'b mut self, f: F) -> Result where F: FnOnce(&'b mut [u8]) -> (usize, R), { @@ -1050,7 +1081,7 @@ impl<'a> Socket<'a> { /// by the amount of occupied space in the receive buffer; down to zero. /// /// See also [recv](#method.recv). - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { self.recv_impl(|rx_buffer| { let size = rx_buffer.dequeue_slice(data); (size, size) @@ -1061,7 +1092,7 @@ impl<'a> Socket<'a> { /// the receive buffer, and return a pointer to it. /// /// This function otherwise behaves identically to [recv](#method.recv). - pub fn peek(&mut self, size: usize) -> Result<&[u8]> { + pub fn peek(&mut self, size: usize) -> Result<&[u8], RecvError> { self.recv_error_check()?; let buffer = self.rx_buffer.get_allocated(0, size); @@ -1076,7 +1107,7 @@ impl<'a> Socket<'a> { /// the receive buffer, and fill a slice from it. /// /// This function otherwise behaves identically to [recv_slice](#method.recv_slice). - pub fn peek_slice(&mut self, data: &mut [u8]) -> Result { + pub fn peek_slice(&mut self, data: &mut [u8]) -> Result { let buffer = self.peek(data.len())?; let data = &mut data[..buffer.len()]; data.copy_from_slice(buffer); @@ -1262,7 +1293,7 @@ impl<'a> Socket<'a> { cx: &mut Context, ip_repr: &IpRepr, repr: &TcpRepr, - ) -> Result)>> { + ) -> Result)>, Error> { debug_assert!(self.accepts(cx, ip_repr, repr)); // Consider how much the sequence number space differs from the transmit buffer space. @@ -1903,9 +1934,9 @@ impl<'a> Socket<'a> { } } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> where - F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<(), Error>, { if self.tuple.is_none() { return Err(Error::Exhausted); @@ -2365,7 +2396,7 @@ mod test { socket: &mut TestSocket, timestamp: Instant, repr: &TcpRepr, - ) -> Result>> { + ) -> Result>, Error> { socket.cx.set_now(timestamp); let ip_repr = IpReprIpvX(IpvXRepr { @@ -2391,7 +2422,7 @@ mod test { fn recv(socket: &mut TestSocket, timestamp: Instant, mut f: F) where - F: FnMut(Result), + F: FnMut(Result), { socket.cx.set_now(timestamp); @@ -2736,14 +2767,14 @@ mod test { #[test] fn test_listen_validation() { let mut s = socket(); - assert_eq!(s.listen(0), Err(Error::Unaddressable)); + assert_eq!(s.listen(0), Err(ListenError::Unaddressable)); } #[test] fn test_listen_twice() { let mut s = socket(); assert_eq!(s.listen(80), Ok(())); - assert_eq!(s.listen(80), Err(Error::Illegal)); + assert_eq!(s.listen(80), Err(ListenError::InvalidState)); } #[test] @@ -3052,17 +3083,17 @@ mod test { assert_eq!( s.socket .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 0)), - Err(Error::Unaddressable) + Err(ConnectError::Unaddressable) ); assert_eq!( s.socket .connect(&mut s.cx, REMOTE_END, (IpvXAddress::UNSPECIFIED, 1024)), - Err(Error::Unaddressable) + Err(ConnectError::Unaddressable) ); assert_eq!( s.socket .connect(&mut s.cx, (IpvXAddress::UNSPECIFIED, 0), LOCAL_END), - Err(Error::Unaddressable) + Err(ConnectError::Unaddressable) ); s.socket .connect(&mut s.cx, REMOTE_END, LOCAL_END) @@ -3125,7 +3156,7 @@ mod test { assert_eq!(s.socket.connect(&mut s.cx, REMOTE_END, 80), Ok(())); assert_eq!( s.socket.connect(&mut s.cx, REMOTE_END, 80), - Err(Error::Illegal) + Err(ConnectError::InvalidState) ); } @@ -6297,7 +6328,7 @@ mod test { (3, ()) }) .unwrap(); - assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::Finished)); } #[test] @@ -6319,7 +6350,7 @@ mod test { (3, ()) }) .unwrap(); - assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::Finished)); } #[test] @@ -6341,7 +6372,7 @@ mod test { (3, ()) }) .unwrap(); - assert_eq!(s.recv(|_| (0, ())), Err(Error::Finished)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::Finished)); } #[test] @@ -6393,7 +6424,7 @@ mod test { ); // Error must be `Illegal` even if we've received a FIN, // because we are missing data. - assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::InvalidState)); } #[test] @@ -6422,7 +6453,7 @@ mod test { (3, ()) }) .unwrap(); - assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::InvalidState)); } #[test] @@ -6466,7 +6497,7 @@ mod test { (3, ()) }) .unwrap(); - assert_eq!(s.recv(|_| (0, ())), Err(Error::Illegal)); + assert_eq!(s.recv(|_| (0, ())), Err(RecvError::InvalidState)); } // =========================================================================================// From b7871e4e5704963e84b0a5eea04c4601162b4703 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 03:01:18 +0200 Subject: [PATCH 332/566] udp, icmp, raw: return own error enums for public API. --- src/socket/icmp.rs | 172 ++++++++++++++++++++--------------- src/socket/raw.rs | 108 ++++++++++++++-------- src/socket/udp.rs | 121 +++++++++++++++--------- src/storage/assembler.rs | 2 +- src/storage/mod.rs | 10 ++ src/storage/packet_buffer.rs | 63 ++++++------- src/storage/ring_buffer.rs | 85 ++++++++--------- 7 files changed, 335 insertions(+), 226 deletions(-) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 68c8f2820..55aff6c94 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -6,7 +6,7 @@ use crate::phy::ChecksumCapabilities; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::socket::{Context, PollAt}; -use crate::{Error, Result}; +use crate::Error; use crate::wire::IcmpRepr; #[cfg(feature = "proto-ipv4")] @@ -16,6 +16,29 @@ use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Repr}; use crate::wire::{IpAddress, IpListenEndpoint, IpProtocol, IpRepr}; use crate::wire::{UdpPacket, UdpRepr}; +/// Error returned by [`Socket::bind`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BindError { + InvalidState, + Unaddressable, +} + +/// Error returned by [`Socket::send`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SendError { + Unaddressable, + BufferFull, +} + +/// Error returned by [`Socket::recv`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + Exhausted, +} + /// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for /// more details. /// @@ -204,14 +227,14 @@ impl<'a> Socket<'a> { /// [IcmpEndpoint::Udp]: enum.IcmpEndpoint.html#variant.Udp /// [send]: #method.send /// [recv]: #method.recv - pub fn bind>(&mut self, endpoint: T) -> Result<()> { + pub fn bind>(&mut self, endpoint: T) -> Result<(), BindError> { let endpoint = endpoint.into(); if !endpoint.is_specified() { - return Err(Error::Unaddressable); + return Err(BindError::Unaddressable); } if self.is_open() { - return Err(Error::Illegal); + return Err(BindError::InvalidState); } self.endpoint = endpoint; @@ -273,12 +296,15 @@ impl<'a> Socket<'a> { /// This function returns `Err(Error::Exhausted)` if the transmit buffer is full, /// `Err(Error::Truncated)` if the requested size is larger than the packet buffer /// size, and `Err(Error::Unaddressable)` if the remote address is unspecified. - pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8]> { + pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8], SendError> { if endpoint.is_unspecified() { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } - let packet_buf = self.tx_buffer.enqueue(size, endpoint)?; + let packet_buf = self + .tx_buffer + .enqueue(size, endpoint) + .map_err(|_| SendError::BufferFull)?; net_trace!("icmp:{}: buffer to send {} octets", endpoint, size); Ok(packet_buf) @@ -287,7 +313,7 @@ impl<'a> Socket<'a> { /// Enqueue a packet to be sent to a given remote address, and fill it from a slice. /// /// See also [send](#method.send). - pub fn send_slice(&mut self, data: &[u8], endpoint: IpAddress) -> Result<()> { + pub fn send_slice(&mut self, data: &[u8], endpoint: IpAddress) -> Result<(), SendError> { let packet_buf = self.send(data.len(), endpoint)?; packet_buf.copy_from_slice(data); Ok(()) @@ -297,8 +323,8 @@ impl<'a> Socket<'a> { /// as a pointer to the payload. /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> { - let (endpoint, packet_buf) = self.rx_buffer.dequeue()?; + pub fn recv(&mut self) -> Result<(&[u8], IpAddress), RecvError> { + let (endpoint, packet_buf) = self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?; net_trace!( "icmp:{}: receive {} buffered octets", @@ -312,7 +338,7 @@ impl<'a> Socket<'a> { /// and return the amount of octets copied as well as the `IpAddress` /// /// See also [recv](#method.recv). - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpAddress)> { + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpAddress), RecvError> { let (buffer, endpoint) = self.recv()?; let length = cmp::min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); @@ -388,13 +414,14 @@ impl<'a> Socket<'a> { _cx: &mut Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr, - ) -> Result<()> { + ) -> Result<(), Error> { match *icmp_repr { #[cfg(feature = "proto-ipv4")] IcmpRepr::Ipv4(ref icmp_repr) => { let packet_buf = self .rx_buffer - .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr()) + .map_err(|_| Error::Exhausted)?; icmp_repr.emit( &mut Icmpv4Packet::new_unchecked(packet_buf), &ChecksumCapabilities::default(), @@ -410,7 +437,8 @@ impl<'a> Socket<'a> { IcmpRepr::Ipv6(ref icmp_repr) => { let packet_buf = self .rx_buffer - .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?; + .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr()) + .map_err(|_| Error::Exhausted)?; icmp_repr.emit( &ip_repr.src_addr(), &ip_repr.dst_addr(), @@ -432,59 +460,61 @@ impl<'a> Socket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> where - F: FnOnce(&mut Context, (IpRepr, IcmpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, IcmpRepr)) -> Result<(), Error>, { let hop_limit = self.hop_limit.unwrap_or(64); - self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { - net_trace!( - "icmp:{}: sending {} octets", - remote_endpoint, - packet_buf.len() - ); - match *remote_endpoint { - #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(dst_addr) => { - let src_addr = match cx.get_source_address_ipv4(dst_addr) { - Some(addr) => addr, - None => return Err(Error::Unaddressable), - }; - let packet = Icmpv4Packet::new_unchecked(&*packet_buf); - let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Icmp, - payload_len: repr.buffer_len(), - hop_limit: hop_limit, - }); - emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) - } - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(dst_addr) => { - let src_addr = match cx.get_source_address_ipv6(dst_addr) { - Some(addr) => addr, - None => return Err(Error::Unaddressable), - }; - let packet = Icmpv6Packet::new_unchecked(&*packet_buf); - let repr = Icmpv6Repr::parse( - &src_addr.into(), - &dst_addr.into(), - &packet, - &ChecksumCapabilities::ignored(), - )?; - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Icmpv6, - payload_len: repr.buffer_len(), - hop_limit: hop_limit, - }); - emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) + self.tx_buffer + .dequeue_with(|remote_endpoint, packet_buf| { + net_trace!( + "icmp:{}: sending {} octets", + remote_endpoint, + packet_buf.len() + ); + match *remote_endpoint { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(dst_addr) => { + let src_addr = match cx.get_source_address_ipv4(dst_addr) { + Some(addr) => addr, + None => return Err(Error::Unaddressable), + }; + let packet = Icmpv4Packet::new_unchecked(&*packet_buf); + let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Icmp, + payload_len: repr.buffer_len(), + hop_limit: hop_limit, + }); + emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) + } + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(dst_addr) => { + let src_addr = match cx.get_source_address_ipv6(dst_addr) { + Some(addr) => addr, + None => return Err(Error::Unaddressable), + }; + let packet = Icmpv6Packet::new_unchecked(&*packet_buf); + let repr = Icmpv6Repr::parse( + &src_addr.into(), + &dst_addr.into(), + &packet, + &ChecksumCapabilities::ignored(), + )?; + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Icmpv6, + payload_len: repr.buffer_len(), + hop_limit: hop_limit, + }); + emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) + } } - } - })?; + }) + .map_err(|_| Error::Exhausted)??; #[cfg(feature = "async")] self.tx_waker.wake(); @@ -568,7 +598,7 @@ mod test_ipv4 { let mut socket = socket(buffer(0), buffer(1)); assert_eq!( socket.send_slice(b"abcdef", IpAddress::Ipv4(Ipv4Address::default())), - Err(Error::Unaddressable) + Err(SendError::Unaddressable) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(())); } @@ -587,7 +617,7 @@ mod test_ipv4 { // This buffer is too long assert_eq!( socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()), - Err(Error::Truncated) + Err(SendError::BufferFull) ); assert!(socket.can_send()); @@ -601,7 +631,7 @@ mod test_ipv4 { ); assert_eq!( socket.send_slice(b"123456", REMOTE_IPV4.into()), - Err(Error::Exhausted) + Err(SendError::BufferFull) ); assert!(!socket.can_send()); @@ -669,7 +699,7 @@ mod test_ipv4 { assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); assert!(!socket.can_recv()); - assert_eq!(socket.recv(), Err(Error::Exhausted)); + assert_eq!(socket.recv(), Err(RecvError::Exhausted)); let checksum = ChecksumCapabilities::default(); @@ -816,7 +846,7 @@ mod test_ipv6 { let mut socket = socket(buffer(0), buffer(1)); assert_eq!( socket.send_slice(b"abcdef", IpAddress::Ipv6(Ipv6Address::default())), - Err(Error::Unaddressable) + Err(SendError::Unaddressable) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV6.into()), Ok(())); } @@ -835,7 +865,7 @@ mod test_ipv6 { // This buffer is too long assert_eq!( socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()), - Err(Error::Truncated) + Err(SendError::BufferFull) ); assert!(socket.can_send()); @@ -854,7 +884,7 @@ mod test_ipv6 { ); assert_eq!( socket.send_slice(b"123456", REMOTE_IPV6.into()), - Err(Error::Exhausted) + Err(SendError::BufferFull) ); assert!(!socket.can_send()); @@ -927,7 +957,7 @@ mod test_ipv6 { assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(())); assert!(!socket.can_recv()); - assert_eq!(socket.recv(), Err(Error::Exhausted)); + assert_eq!(socket.recv(), Err(RecvError::Exhausted)); let checksum = ChecksumCapabilities::default(); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index e92b785c9..985dadde9 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -7,7 +7,7 @@ use crate::phy::ChecksumCapabilities; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::{Error, Result}; +use crate::Error; use crate::wire::{IpProtocol, IpRepr, IpVersion}; #[cfg(feature = "proto-ipv4")] @@ -15,6 +15,28 @@ use crate::wire::{Ipv4Packet, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Packet, Ipv6Repr}; +/// Error returned by [`Socket::bind`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BindError { + InvalidState, + Unaddressable, +} + +/// Error returned by [`Socket::send`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SendError { + BufferFull, +} + +/// Error returned by [`Socket::recv`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + Exhausted, +} + /// A UDP packet metadata. pub type PacketMetadata = crate::storage::PacketMetadata<()>; @@ -152,8 +174,11 @@ impl<'a> Socket<'a> { /// /// **Note:** The IP header is parsed and re-serialized, and may not match /// the header actually transmitted bit for bit. - pub fn send(&mut self, size: usize) -> Result<&mut [u8]> { - let packet_buf = self.tx_buffer.enqueue(size, ())?; + pub fn send(&mut self, size: usize) -> Result<&mut [u8], SendError> { + let packet_buf = self + .tx_buffer + .enqueue(size, ()) + .map_err(|_| SendError::BufferFull)?; net_trace!( "raw:{}:{}: buffer to send {} octets", @@ -167,7 +192,7 @@ impl<'a> Socket<'a> { /// Enqueue a packet to send, and fill it from a slice. /// /// See also [send](#method.send). - pub fn send_slice(&mut self, data: &[u8]) -> Result<()> { + pub fn send_slice(&mut self, data: &[u8]) -> Result<(), SendError> { self.send(data.len())?.copy_from_slice(data); Ok(()) } @@ -178,8 +203,8 @@ impl<'a> Socket<'a> { /// /// **Note:** The IP header is parsed and re-serialized, and may not match /// the header actually received bit for bit. - pub fn recv(&mut self) -> Result<&[u8]> { - let ((), packet_buf) = self.rx_buffer.dequeue()?; + pub fn recv(&mut self) -> Result<&[u8], RecvError> { + let ((), packet_buf) = self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?; net_trace!( "raw:{}:{}: receive {} buffered octets", @@ -193,7 +218,7 @@ impl<'a> Socket<'a> { /// Dequeue a packet, and copy the payload into the given slice. /// /// See also [recv](#method.recv). - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result { let buffer = self.recv()?; let length = min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); @@ -216,12 +241,15 @@ impl<'a> Socket<'a> { cx: &mut Context, ip_repr: &IpRepr, payload: &[u8], - ) -> Result<()> { + ) -> Result<(), Error> { 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, ())?; + let packet_buf = self + .rx_buffer + .enqueue(total_len, ()) + .map_err(|_| Error::Exhausted)?; ip_repr.emit(&mut packet_buf[..header_len], &cx.checksum_caps()); packet_buf[header_len..].copy_from_slice(payload); @@ -238,15 +266,15 @@ impl<'a> Socket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> where - F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<(), Error>, { fn prepare<'a>( next_header: IpProtocol, buffer: &'a mut [u8], _checksum_caps: &ChecksumCapabilities, - ) -> Result<(IpRepr, &'a [u8])> { + ) -> Result<(IpRepr, &'a [u8]), Error> { match IpVersion::of_packet(buffer)? { #[cfg(feature = "proto-ipv4")] IpVersion::Ipv4 => { @@ -281,29 +309,31 @@ impl<'a> Socket<'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, &cx.checksum_caps()) { - Ok((ip_repr, raw_packet)) => { - net_trace!( - "raw:{}:{}: sending {} octets", - ip_version, - ip_protocol, - ip_repr.buffer_len() + raw_packet.len() - ); - emit(cx, (ip_repr, raw_packet)) - } - Err(error) => { - net_debug!( - "raw:{}:{}: dropping outgoing packet ({})", - ip_version, - ip_protocol, - error - ); - // Return Ok(()) so the packet is dequeued. - Ok(()) + self.tx_buffer + .dequeue_with(|&mut (), packet_buf| { + match prepare(ip_protocol, packet_buf, &cx.checksum_caps()) { + Ok((ip_repr, raw_packet)) => { + net_trace!( + "raw:{}:{}: sending {} octets", + ip_version, + ip_protocol, + ip_repr.buffer_len() + raw_packet.len() + ); + emit(cx, (ip_repr, raw_packet)) + } + Err(error) => { + net_debug!( + "raw:{}:{}: dropping outgoing packet ({})", + ip_version, + ip_protocol, + error + ); + // Return Ok(()) so the packet is dequeued. + Ok(()) + } } - } - })?; + }) + .map_err(|_| Error::Exhausted)??; #[cfg(feature = "async")] self.tx_waker.wake(); @@ -413,7 +443,7 @@ mod test { #[test] fn test_send_truncated() { let mut socket = $socket(buffer(0), buffer(1)); - assert_eq!(socket.send_slice(&[0; 56][..]), Err(Error::Truncated)); + assert_eq!(socket.send_slice(&[0; 56][..]), Err(SendError::BufferFull)); } #[test] @@ -428,7 +458,7 @@ mod test { ); assert_eq!(socket.send_slice(&$packet[..]), Ok(())); - assert_eq!(socket.send_slice(b""), Err(Error::Exhausted)); + assert_eq!(socket.send_slice(b""), Err(SendError::BufferFull)); assert!(!socket.can_send()); assert_eq!( @@ -476,7 +506,7 @@ mod test { assert!(socket.accepts(&$hdr)); assert_eq!( socket.process(&mut cx, &$hdr, &buffer), - Err(Error::Truncated) + Err(Error::Exhausted) ); } } @@ -551,7 +581,7 @@ mod test { let mut cksumd_packet = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut cksumd_packet).fill_checksum(); - assert_eq!(socket.recv(), Err(Error::Exhausted)); + assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); assert_eq!( socket.process( @@ -581,7 +611,7 @@ mod test { assert!(!socket.can_recv()); let mut cx = Context::mock(); - assert_eq!(socket.recv(), Err(Error::Exhausted)); + assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); assert_eq!( socket.process( diff --git a/src/socket/udp.rs b/src/socket/udp.rs index e91068b31..664dfd705 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -7,7 +7,7 @@ use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; -use crate::{Error, Result}; +use crate::Error; /// A UDP packet metadata. pub type PacketMetadata = crate::storage::PacketMetadata; @@ -15,6 +15,29 @@ pub type PacketMetadata = crate::storage::PacketMetadata; /// A UDP packet ring buffer. pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpEndpoint>; +/// Error returned by [`Socket::bind`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BindError { + InvalidState, + Unaddressable, +} + +/// Error returned by [`Socket::send`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SendError { + Unaddressable, + BufferFull, +} + +/// Error returned by [`Socket::recv`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + Exhausted, +} + /// A User Datagram Protocol socket. /// /// A UDP socket is bound to a specific endpoint, and owns transmit and receive @@ -120,14 +143,14 @@ impl<'a> Socket<'a> { /// This function returns `Err(Error::Illegal)` if the socket was open /// (see [is_open](#method.is_open)), and `Err(Error::Unaddressable)` /// if the port in the given endpoint is zero. - pub fn bind>(&mut self, endpoint: T) -> Result<()> { + pub fn bind>(&mut self, endpoint: T) -> Result<(), BindError> { let endpoint = endpoint.into(); if endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(BindError::Unaddressable); } if self.is_open() { - return Err(Error::Illegal); + return Err(BindError::InvalidState); } self.endpoint = endpoint; @@ -206,18 +229,25 @@ impl<'a> Socket<'a> { /// `Err(Error::Unaddressable)` if local or remote port, or remote address are unspecified, /// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity /// to ever send this packet. - pub fn send(&mut self, size: usize, remote_endpoint: IpEndpoint) -> Result<&mut [u8]> { + pub fn send( + &mut self, + size: usize, + remote_endpoint: IpEndpoint, + ) -> Result<&mut [u8], SendError> { if self.endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } if remote_endpoint.addr.is_unspecified() { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } if remote_endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } - let payload_buf = self.tx_buffer.enqueue(size, remote_endpoint)?; + let payload_buf = self + .tx_buffer + .enqueue(size, remote_endpoint) + .map_err(|_| SendError::BufferFull)?; net_trace!( "udp:{}:{}: buffer to send {} octets", @@ -231,7 +261,11 @@ impl<'a> Socket<'a> { /// Enqueue a packet to be sent to a given remote endpoint, and fill it from a slice. /// /// See also [send](#method.send). - pub fn send_slice(&mut self, data: &[u8], remote_endpoint: IpEndpoint) -> Result<()> { + pub fn send_slice( + &mut self, + data: &[u8], + remote_endpoint: IpEndpoint, + ) -> Result<(), SendError> { self.send(data.len(), remote_endpoint)? .copy_from_slice(data); Ok(()) @@ -241,8 +275,9 @@ impl<'a> Socket<'a> { /// as a pointer to the payload. /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> { - let (remote_endpoint, payload_buf) = self.rx_buffer.dequeue()?; + pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint), RecvError> { + let (remote_endpoint, payload_buf) = + self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?; net_trace!( "udp:{}:{}: receive {} buffered octets", @@ -257,8 +292,8 @@ impl<'a> Socket<'a> { /// and return the amount of octets copied as well as the endpoint. /// /// See also [recv](#method.recv). - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpEndpoint)> { - let (buffer, endpoint) = self.recv()?; + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpEndpoint), RecvError> { + let (buffer, endpoint) = self.recv().map_err(|_| RecvError::Exhausted)?; let length = min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); Ok((length, endpoint)) @@ -269,17 +304,19 @@ impl<'a> Socket<'a> { /// This function otherwise behaves identically to [recv](#method.recv). /// /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint)> { + pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint), RecvError> { let endpoint = self.endpoint; - self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| { - net_trace!( - "udp:{}:{}: peek {} buffered octets", - endpoint, - remote_endpoint, - payload_buf.len() - ); - (payload_buf, remote_endpoint) - }) + self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map( + |(remote_endpoint, payload_buf)| { + net_trace!( + "udp:{}:{}: peek {} buffered octets", + endpoint, + remote_endpoint, + payload_buf.len() + ); + (payload_buf, remote_endpoint) + }, + ) } /// Peek at a packet received from a remote endpoint, copy the payload into the given slice, @@ -288,7 +325,7 @@ impl<'a> Socket<'a> { /// This function otherwise behaves identically to [recv_slice](#method.recv_slice). /// /// See also [peek](#method.peek). - pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &IpEndpoint)> { + pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &IpEndpoint), RecvError> { let (buffer, endpoint) = self.peek()?; let length = min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); @@ -316,7 +353,7 @@ impl<'a> Socket<'a> { ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8], - ) -> Result<()> { + ) -> Result<(), Error> { debug_assert!(self.accepts(cx, ip_repr, repr)); let size = payload.len(); @@ -326,7 +363,8 @@ impl<'a> Socket<'a> { port: repr.src_port, }; self.rx_buffer - .enqueue(size, remote_endpoint)? + .enqueue(size, remote_endpoint) + .map_err(|_| Error::Exhausted)? .copy_from_slice(payload); net_trace!( @@ -342,9 +380,9 @@ impl<'a> Socket<'a> { Ok(()) } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), Error>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); @@ -378,7 +416,8 @@ impl<'a> Socket<'a> { hop_limit, ); emit(cx, (ip_repr, repr, payload_buf)) - })?; + }) + .map_err(|_| Error::Exhausted)??; #[cfg(feature = "async")] self.tx_waker.wake(); @@ -488,14 +527,14 @@ mod test { #[test] fn test_bind_unaddressable() { let mut socket = socket(buffer(0), buffer(0)); - assert_eq!(socket.bind(0), Err(Error::Unaddressable)); + assert_eq!(socket.bind(0), Err(BindError::Unaddressable)); } #[test] fn test_bind_twice() { let mut socket = socket(buffer(0), buffer(0)); assert_eq!(socket.bind(1), Ok(())); - assert_eq!(socket.bind(2), Err(Error::Illegal)); + assert_eq!(socket.bind(2), Err(BindError::InvalidState)); } #[test] @@ -511,7 +550,7 @@ mod test { assert_eq!( socket.send_slice(b"abcdef", REMOTE_END), - Err(Error::Unaddressable) + Err(SendError::Unaddressable) ); assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert_eq!( @@ -522,7 +561,7 @@ mod test { ..REMOTE_END } ), - Err(Error::Unaddressable) + Err(SendError::Unaddressable) ); assert_eq!( socket.send_slice( @@ -532,7 +571,7 @@ mod test { ..REMOTE_END } ), - Err(Error::Unaddressable) + Err(SendError::Unaddressable) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(())); } @@ -553,7 +592,7 @@ mod test { assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(())); assert_eq!( socket.send_slice(b"123456", REMOTE_END), - Err(Error::Exhausted) + Err(SendError::BufferFull) ); assert!(!socket.can_send()); @@ -588,7 +627,7 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert!(!socket.can_recv()); - assert_eq!(socket.recv(), Err(Error::Exhausted)); + assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); assert_eq!( @@ -613,7 +652,7 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert_eq!(socket.peek(), Err(Error::Exhausted)); + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); assert_eq!( socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), @@ -621,7 +660,7 @@ mod test { ); assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END))); assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); - assert_eq!(socket.peek(), Err(Error::Exhausted)); + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); } #[test] @@ -659,7 +698,7 @@ mod test { assert_eq!(&slice, b"abcd"); assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END))); assert_eq!(&slice, b"abcd"); - assert_eq!(socket.peek_slice(&mut slice[..]), Err(Error::Exhausted)); + assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted)); } #[test] @@ -724,7 +763,7 @@ mod test { let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx"; assert_eq!( socket.send_slice(too_large, REMOTE_END), - Err(Error::Truncated) + Err(SendError::BufferFull) ); assert_eq!(socket.send_slice(&too_large[..16 * 4], REMOTE_END), Ok(())); } diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 3ae8d81ad..8fb6390d5 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -192,7 +192,7 @@ impl Assembler { } /// Add a new contiguous range to the assembler, and return `Ok(bool)`, - /// or return `Err(())` if too many discontiguities are already recorded. + /// or return `Err(TooManyHolesError)` if too many discontiguities are already recorded. /// Returns `Ok(true)` when there was an overlap. pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result { let mut index = 0; diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 7e1150264..b03de7124 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -19,3 +19,13 @@ pub use self::ring_buffer::RingBuffer; pub trait Resettable { fn reset(&mut self); } + +/// Error returned when enqueuing into a full buffer. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Full; + +/// Error returned when dequeuing from an empty buffer. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Empty; diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 9653455bb..6be763723 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -1,7 +1,8 @@ use managed::ManagedSlice; -use crate::storage::RingBuffer; -use crate::{Error, Result}; +use crate::storage::{Full, RingBuffer}; + +use super::Empty; /// Size and header of a packet. #[derive(Debug, Clone, Copy)] @@ -74,30 +75,25 @@ impl<'a, H> PacketBuffer<'a, H> { // in case of failure. /// Enqueue a single packet with the given header into the buffer, and - /// return a reference to its payload, or return `Err(Error::Exhausted)` - /// if the buffer is full, or return `Err(Error::Truncated)` if the buffer - /// does not have enough spare payload space. - pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> { - if self.payload_ring.capacity() < size { - return Err(Error::Truncated); - } - - if self.metadata_ring.is_full() { - return Err(Error::Exhausted); + /// return a reference to its payload, or return `Err(Full)` + /// if the buffer is full. + pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8], Full> { + if self.payload_ring.capacity() < size || self.metadata_ring.is_full() { + return Err(Full); } let window = self.payload_ring.window(); let contig_window = self.payload_ring.contiguous_window(); if window < size { - return Err(Error::Exhausted); + return Err(Full); } else if contig_window < size { if window - contig_window < size { // The buffer length is larger than the current contiguous window // and is larger than the contiguous window will be after adding // the padding necessary to circle around to the beginning of the // ring buffer. - return Err(Error::Exhausted); + return Err(Full); } else { // Add padding to the end of the ring buffer so that the // contiguous window is at the beginning of the ring buffer. @@ -127,16 +123,16 @@ impl<'a, H> PacketBuffer<'a, H> { let _buf_dequeued = payload_ring.dequeue_many(metadata.size); Ok(()) // dequeue metadata } else { - Err(Error::Exhausted) // don't dequeue metadata + Err(()) // don't dequeue metadata } }); } /// Call `f` with a single packet from the buffer, and dequeue the packet if `f` - /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. - pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result + /// returns successfully, or return `Err(EmptyError)` if the buffer is empty. + pub fn dequeue_with<'c, R, E, F>(&'c mut self, f: F) -> Result, Empty> where - F: FnOnce(&mut H, &'c mut [u8]) -> Result, + F: FnOnce(&mut H, &'c mut [u8]) -> Result, { self.dequeue_padding(); @@ -166,7 +162,7 @@ impl<'a, H> PacketBuffer<'a, H> { /// Dequeue a single packet from the buffer, and return a reference to its payload /// as well as its header, or return `Err(Error::Exhausted)` if the buffer is empty. - pub fn dequeue(&mut self) -> Result<(H, &mut [u8])> { + pub fn dequeue(&mut self) -> Result<(H, &mut [u8]), Empty> { self.dequeue_padding(); let PacketMetadata { @@ -183,7 +179,7 @@ impl<'a, H> PacketBuffer<'a, H> { /// its payload as well as its header, or return `Err(Error:Exhausted)` if the buffer is empty. /// /// This function otherwise behaves identically to [dequeue](#method.dequeue). - pub fn peek(&mut self) -> Result<(&H, &[u8])> { + pub fn peek(&mut self) -> Result<(&H, &[u8]), Empty> { self.dequeue_padding(); if let Some(metadata) = self.metadata_ring.get_allocated(0, 1).first() { @@ -192,7 +188,7 @@ impl<'a, H> PacketBuffer<'a, H> { self.payload_ring.get_allocated(0, metadata.size), )) } else { - Err(Error::Exhausted) + Err(Empty) } } @@ -226,21 +222,21 @@ mod test { fn test_simple() { let mut buffer = buffer(); buffer.enqueue(6, ()).unwrap().copy_from_slice(b"abcdef"); - assert_eq!(buffer.enqueue(16, ()), Err(Error::Exhausted)); + assert_eq!(buffer.enqueue(16, ()), Err(Full)); assert_eq!(buffer.metadata_ring.len(), 1); assert_eq!(buffer.dequeue().unwrap().1, &b"abcdef"[..]); - assert_eq!(buffer.dequeue(), Err(Error::Exhausted)); + assert_eq!(buffer.dequeue(), Err(Empty)); } #[test] fn test_peek() { let mut buffer = buffer(); - assert_eq!(buffer.peek(), Err(Error::Exhausted)); + assert_eq!(buffer.peek(), Err(Empty)); buffer.enqueue(6, ()).unwrap().copy_from_slice(b"abcdef"); assert_eq!(buffer.metadata_ring.len(), 1); assert_eq!(buffer.peek().unwrap().1, &b"abcdef"[..]); assert_eq!(buffer.dequeue().unwrap().1, &b"abcdef"[..]); - assert_eq!(buffer.peek(), Err(Error::Exhausted)); + assert_eq!(buffer.peek(), Err(Empty)); } #[test] @@ -278,15 +274,16 @@ mod test { assert_eq!(buffer.metadata_ring.len(), 3); assert!(buffer.dequeue().is_ok()); - assert!(buffer - .dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>) - .is_err()); + assert!(matches!( + buffer.dequeue_with(|_, _| Result::<(), u32>::Err(123)), + Ok(Err(_)) + )); assert_eq!(buffer.metadata_ring.len(), 1); assert!(buffer .dequeue_with(|&mut (), payload| { assert_eq!(payload, &b"abcd"[..]); - Ok(()) + Result::<(), ()>::Ok(()) }) .is_ok()); assert_eq!(buffer.metadata_ring.len(), 0); @@ -307,7 +304,7 @@ mod test { assert!(buffer.is_full()); assert!(!buffer.is_empty()); assert_eq!(buffer.metadata_ring.len(), 4); - assert_eq!(buffer.enqueue(1, ()), Err(Error::Exhausted)); + assert_eq!(buffer.enqueue(1, ()), Err(Full)); } #[test] @@ -316,7 +313,7 @@ mod test { assert!(buffer.enqueue(4, ()).is_ok()); assert!(buffer.enqueue(8, ()).is_ok()); assert!(buffer.dequeue().is_ok()); - assert_eq!(buffer.enqueue(16, ()), Err(Error::Exhausted)); + assert_eq!(buffer.enqueue(16, ()), Err(Full)); assert_eq!(buffer.metadata_ring.len(), 1); } @@ -326,14 +323,14 @@ mod test { assert!(buffer.enqueue(4, ()).is_ok()); assert!(buffer.enqueue(8, ()).is_ok()); assert!(buffer.dequeue().is_ok()); - assert_eq!(buffer.enqueue(8, ()), Err(Error::Exhausted)); + assert_eq!(buffer.enqueue(8, ()), Err(Full)); assert_eq!(buffer.metadata_ring.len(), 1); } #[test] fn test_capacity_too_small() { let mut buffer = buffer(); - assert_eq!(buffer.enqueue(32, ()), Err(Error::Truncated)); + assert_eq!(buffer.enqueue(32, ()), Err(Full)); } #[test] diff --git a/src/storage/ring_buffer.rs b/src/storage/ring_buffer.rs index f54837b34..7d461b68c 100644 --- a/src/storage/ring_buffer.rs +++ b/src/storage/ring_buffer.rs @@ -6,7 +6,8 @@ use core::cmp; use managed::ManagedSlice; use crate::storage::Resettable; -use crate::{Error, Result}; + +use super::{Empty, Full}; /// A ring buffer. /// @@ -114,60 +115,57 @@ impl<'a, T: 'a> RingBuffer<'a, T> { /// and boundary conditions (empty/full) are errors. impl<'a, T: 'a> RingBuffer<'a, T> { /// Call `f` with a single buffer element, and enqueue the element if `f` - /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is full. - pub fn enqueue_one_with<'b, R, F>(&'b mut self, f: F) -> Result + /// returns successfully, or return `Err(Full)` if the buffer is full. + pub fn enqueue_one_with<'b, R, E, F>(&'b mut self, f: F) -> Result, Full> where - F: FnOnce(&'b mut T) -> Result, + F: FnOnce(&'b mut T) -> Result, { if self.is_full() { - return Err(Error::Exhausted); + return Err(Full); } let index = self.get_idx_unchecked(self.length); - match f(&mut self.storage[index]) { - Ok(result) => { - self.length += 1; - Ok(result) - } - Err(error) => Err(error), + let res = f(&mut self.storage[index]); + if res.is_ok() { + self.length += 1; } + Ok(res) } /// Enqueue a single element into the buffer, and return a reference to it, - /// or return `Err(Error::Exhausted)` if the buffer is full. + /// or return `Err(Full)` if the buffer is full. /// /// This function is a shortcut for `ring_buf.enqueue_one_with(Ok)`. - pub fn enqueue_one(&mut self) -> Result<&mut T> { - self.enqueue_one_with(Ok) + pub fn enqueue_one(&mut self) -> Result<&mut T, Full> { + self.enqueue_one_with(Ok)? } /// Call `f` with a single buffer element, and dequeue the element if `f` - /// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty. - pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result + /// returns successfully, or return `Err(Empty)` if the buffer is empty. + pub fn dequeue_one_with<'b, R, E, F>(&'b mut self, f: F) -> Result, Empty> where - F: FnOnce(&'b mut T) -> Result, + F: FnOnce(&'b mut T) -> Result, { if self.is_empty() { - return Err(Error::Exhausted); + return Err(Empty); } let next_at = self.get_idx_unchecked(1); - match f(&mut self.storage[self.read_at]) { - Ok(result) => { - self.length -= 1; - self.read_at = next_at; - Ok(result) - } - Err(error) => Err(error), + let res = f(&mut self.storage[self.read_at]); + + if res.is_ok() { + self.length -= 1; + self.read_at = next_at; } + Ok(res) } /// Dequeue an element from the buffer, and return a reference to it, - /// or return `Err(Error::Exhausted)` if the buffer is empty. + /// or return `Err(Empty)` if the buffer is empty. /// /// This function is a shortcut for `ring_buf.dequeue_one_with(Ok)`. - pub fn dequeue_one(&mut self) -> Result<&mut T> { - self.dequeue_one_with(Ok) + pub fn dequeue_one(&mut self) -> Result<&mut T, Empty> { + self.dequeue_one_with(Ok)? } } @@ -441,31 +439,36 @@ mod test { fn test_buffer_enqueue_dequeue_one_with() { let mut ring = RingBuffer::new(vec![0; 5]); assert_eq!( - ring.dequeue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted) + ring.dequeue_one_with(|_| -> Result::<(), ()> { unreachable!() }), + Err(Empty) ); - ring.enqueue_one_with(Ok).unwrap(); + ring.enqueue_one_with(Ok::<_, ()>).unwrap().unwrap(); assert!(!ring.is_empty()); assert!(!ring.is_full()); for i in 1..5 { - ring.enqueue_one_with(|e| Ok(*e = i)).unwrap(); + ring.enqueue_one_with(|e| Ok::<_, ()>(*e = i)) + .unwrap() + .unwrap(); assert!(!ring.is_empty()); } assert!(ring.is_full()); assert_eq!( - ring.enqueue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted) + ring.enqueue_one_with(|_| -> Result::<(), ()> { unreachable!() }), + Err(Full) ); for i in 0..5 { - assert_eq!(ring.dequeue_one_with(|e| Ok(*e)).unwrap(), i); + assert_eq!( + ring.dequeue_one_with(|e| Ok::<_, ()>(*e)).unwrap().unwrap(), + i + ); assert!(!ring.is_full()); } assert_eq!( - ring.dequeue_one_with(|_| unreachable!()) as Result<()>, - Err(Error::Exhausted) + ring.dequeue_one_with(|_| -> Result::<(), ()> { unreachable!() }), + Err(Empty) ); assert!(ring.is_empty()); } @@ -473,7 +476,7 @@ mod test { #[test] fn test_buffer_enqueue_dequeue_one() { let mut ring = RingBuffer::new(vec![0; 5]); - assert_eq!(ring.dequeue_one(), Err(Error::Exhausted)); + assert_eq!(ring.dequeue_one(), Err(Empty)); ring.enqueue_one().unwrap(); assert!(!ring.is_empty()); @@ -484,13 +487,13 @@ mod test { assert!(!ring.is_empty()); } assert!(ring.is_full()); - assert_eq!(ring.enqueue_one(), Err(Error::Exhausted)); + assert_eq!(ring.enqueue_one(), Err(Full)); for i in 0..5 { assert_eq!(*ring.dequeue_one().unwrap(), i); assert!(!ring.is_full()); } - assert_eq!(ring.dequeue_one(), Err(Error::Exhausted)); + assert_eq!(ring.dequeue_one(), Err(Empty)); assert!(ring.is_empty()); } @@ -777,7 +780,7 @@ mod test { assert_eq!(no_capacity.get_allocated(0, 0), &[]); no_capacity.dequeue_allocated(0); assert_eq!(no_capacity.enqueue_many(0), &[]); - assert_eq!(no_capacity.enqueue_one(), Err(Error::Exhausted)); + assert_eq!(no_capacity.enqueue_one(), Err(Full)); assert_eq!(no_capacity.contiguous_window(), 0); } From 37a276bcf277df3b7c0cf659d572baff688a147c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 04:04:33 +0200 Subject: [PATCH 333/566] socket: Make process() infallible. Malformed/unexpected packets are logged and ignored. See #281 on rationale for why this is better. --- src/iface/interface.rs | 180 +++++++---------------------------------- src/socket/dhcpv4.rs | 29 +++---- src/socket/dns.rs | 68 ++++++++++++---- src/socket/icmp.rs | 83 +++++++------------ src/socket/raw.rs | 80 ++++++++---------- src/socket/tcp.rs | 169 +++++++++++++++++++------------------- src/socket/udp.rs | 45 ++++------- 7 files changed, 253 insertions(+), 401 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ef10648d4..62a1f2c32 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1398,18 +1398,14 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { - if !udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { - continue; - } - - match udp_socket.process( - self, - &IpRepr::Ipv6(ipv6_repr), - &udp_repr, - udp_packet.payload(), - ) { - Ok(()) => return Ok(None), - Err(e) => return Err(e), + if udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { + udp_socket.process( + self, + &IpRepr::Ipv6(ipv6_repr), + &udp_repr, + udp_packet.payload(), + ); + return Ok(None); } } @@ -1523,17 +1519,9 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| raw::Socket::downcast(&mut i.socket)) { - if !raw_socket.accepts(ip_repr) { - continue; - } - - match raw_socket.process(self, 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 - Err(Error::Exhausted) | Err(Error::Truncated) => (), - // Raw sockets don't validate the packets in any way. - Err(_) => unreachable!(), + if raw_socket.accepts(ip_repr) { + raw_socket.process(self, ip_repr, ip_payload); + handled_by_raw_socket = true; } } handled_by_raw_socket @@ -1655,12 +1643,8 @@ impl<'a> InterfaceInner<'a> { UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; let udp_payload = udp_packet.payload(); - match dhcp_socket.process(self, &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. - Err(e) => return Err(e), - } + dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload); + return Ok(None); } } } @@ -1828,17 +1812,9 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { - if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { - continue; - } - - match icmp_socket.process(self, &ip_repr, &icmp_repr.into()) { - // The packet is valid and handled by socket. - Ok(()) => handled_by_icmp_socket = true, - // The socket buffer is full. - Err(Error::Exhausted) => (), - // ICMP sockets don't validate the packets in any way. - Err(_) => unreachable!(), + if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { + icmp_socket.process(self, &ip_repr, &icmp_repr.into()); + handled_by_icmp_socket = true; } } @@ -2015,17 +1991,9 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { - if !icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { - continue; - } - - match icmp_socket.process(self, &ip_repr, &icmp_repr.into()) { - // The packet is valid and handled by socket. - Ok(()) => handled_by_icmp_socket = true, - // The socket buffer is full. - Err(Error::Exhausted) => (), - // ICMP sockets don't validate the packets in any way. - Err(_) => unreachable!(), + if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { + icmp_socket.process(self, &ip_repr, &icmp_repr.into()); + handled_by_icmp_socket = true; } } @@ -2143,15 +2111,9 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { - if !udp_socket.accepts(self, &ip_repr, &udp_repr) { - continue; - } - - match udp_socket.process(self, &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. - Err(e) => return Err(e), + if udp_socket.accepts(self, &ip_repr, &udp_repr) { + udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); + return Ok(None); } } @@ -2160,15 +2122,9 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| dns::Socket::downcast(&mut i.socket)) { - if !dns_socket.accepts(&ip_repr, &udp_repr) { - continue; - } - - match dns_socket.process(self, &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. - Err(e) => return Err(e), + if dns_socket.accepts(&ip_repr, &udp_repr) { + dns_socket.process(self, &ip_repr, &udp_repr, udp_payload); + return Ok(None); } } @@ -2218,16 +2174,10 @@ impl<'a> InterfaceInner<'a> { .iter_mut() .filter_map(|i| tcp::Socket::downcast(&mut i.socket)) { - if !tcp_socket.accepts(self, &ip_repr, &tcp_repr) { - continue; - } - - match tcp_socket.process(self, &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, - // or the socket buffer is full. - Err(e) => return Err(e), + if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { + return Ok(tcp_socket + .process(self, &ip_repr, &tcp_repr) + .map(IpPacket::Tcp)); } } @@ -3919,78 +3869,6 @@ mod test { ); } - #[test] - #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] - fn test_raw_socket_truncated_packet() { - use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - - let mut iface = create_loopback(); - - let packets = 1; - let rx_buffer = - raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = raw::PacketBuffer::new( - vec![raw::PacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ); - let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - iface.add_socket(raw_socket); - - let src_addr = Ipv4Address([127, 0, 0, 2]); - let dst_addr = Ipv4Address([127, 0, 0, 1]); - - const PAYLOAD_LEN: usize = 49; // 49 > 48, hence packet will be truncated - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - - let ipv4_repr = Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + PAYLOAD_LEN, - }; - - // Emit to frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - udp_repr.emit( - &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - let frame = iface.inner.process_ipv4(&mut iface.sockets, &frame); - - // because the packet could not be handled we should send an Icmp message - assert!(match frame { - Ok(Some(IpPacket::Icmpv4(_))) => true, - _ => false, - }); - } - #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] fn test_raw_socket_with_udp_socket() { diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index b138a66f4..8c37c223e 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -196,7 +196,7 @@ impl Socket { ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8], - ) -> Result<()> { + ) { let src_ip = ip_repr.src_addr; // This is enforced in interface.rs. @@ -206,27 +206,26 @@ impl Socket { Ok(dhcp_packet) => dhcp_packet, Err(e) => { net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e); - return Ok(()); + return; } }; let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) { Ok(dhcp_repr) => dhcp_repr, Err(e) => { net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e); - return Ok(()); + return; } }; - let hardware_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr() { - addr - } else { - return Err(Error::Malformed); + let hardware_addr = match cx.hardware_addr() { + Some(HardwareAddress::Ethernet(addr)) => addr, + _ => return, }; if dhcp_repr.client_hardware_address != hardware_addr { - return Ok(()); + return; } if dhcp_repr.transaction_id != self.transaction_id { - return Ok(()); + return; } let server_identifier = match dhcp_repr.server_identifier { Some(server_identifier) => server_identifier, @@ -235,7 +234,7 @@ impl Socket { "DHCP ignoring {:?} because missing server_identifier", dhcp_repr.message_type ); - return Ok(()); + return; } }; @@ -250,7 +249,7 @@ impl Socket { (ClientState::Discovering(_state), DhcpMessageType::Offer) => { if !dhcp_repr.your_ip.is_unicast() { net_debug!("DHCP ignoring OFFER because your_ip is not unicast"); - return Ok(()); + return; } self.state = ClientState::Requesting(RequestState { @@ -305,8 +304,6 @@ impl Socket { ); } } - - Ok(()) } fn parse_ack( @@ -582,7 +579,7 @@ mod test { s: &mut TestSocket, timestamp: Instant, (ip_repr, udp_repr, dhcp_repr): (Ipv4Repr, UdpRepr, DhcpRepr), - ) -> Result<()> { + ) { s.cx.set_now(timestamp); net_trace!("send: {:?}", ip_repr); @@ -636,9 +633,7 @@ mod test { ($socket:ident, $repr:expr) => (send!($socket, time 0, $repr)); ($socket:ident, time $time:expr, $repr:expr) => - (send!($socket, time $time, $repr, Ok(( )))); - ($socket:ident, time $time:expr, $repr:expr, $result:expr) => - (assert_eq!(send(&mut $socket, Instant::from_millis($time), $repr), $result)); + (send(&mut $socket, Instant::from_millis($time), $repr)); } macro_rules! recv { diff --git a/src/socket/dns.rs b/src/socket/dns.rs index a02680b8a..26e42e71c 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -276,7 +276,7 @@ impl<'a> Socket<'a> { ip_repr: &IpRepr, udp_repr: &UdpRepr, payload: &[u8], - ) -> Result<()> { + ) { debug_assert!(self.accepts(ip_repr, udp_repr)); let size = payload.len(); @@ -288,20 +288,26 @@ impl<'a> Socket<'a> { udp_repr.dst_port ); - let p = Packet::new_checked(payload)?; + let p = match Packet::new_checked(payload) { + Ok(x) => x, + Err(_) => { + net_trace!("dns packet malformed"); + return; + } + }; if p.opcode() != Opcode::Query { net_trace!("unwanted opcode {:?}", p.opcode()); - return Err(Error::Malformed); + return; } if !p.flags().contains(Flags::RESPONSE) { net_trace!("packet doesn't have response bit set"); - return Err(Error::Malformed); + return; } if p.question_count() != 1 { net_trace!("bad question count {:?}", p.question_count()); - return Err(Error::Malformed); + return; } // Find pending query @@ -318,27 +324,53 @@ impl<'a> Socket<'a> { } let payload = p.payload(); - let (mut payload, question) = Question::parse(payload)?; + let (mut payload, question) = match Question::parse(payload) { + Ok(x) => x, + Err(_) => { + net_trace!("question malformed"); + return; + } + }; if question.type_ != pq.type_ { net_trace!("question type mismatch"); - return Err(Error::Malformed); + return; } - if !eq_names(p.parse_name(question.name), p.parse_name(&pq.name))? { - net_trace!("question name mismatch"); - return Err(Error::Malformed); + match eq_names(p.parse_name(question.name), p.parse_name(&pq.name)) { + Ok(true) => {} + Ok(false) => { + net_trace!("question name mismatch"); + return; + } + Err(_) => { + net_trace!("dns question name malformed"); + return; + } } let mut addresses = Vec::new(); for _ in 0..p.answer_record_count() { - let (payload2, r) = Record::parse(payload)?; + let (payload2, r) = match Record::parse(payload) { + Ok(x) => x, + Err(_) => { + net_trace!("dns answer record malformed"); + return; + } + }; payload = payload2; - if !eq_names(p.parse_name(r.name), p.parse_name(&pq.name))? { - net_trace!("answer name mismatch: {:?}", r); - continue; + match eq_names(p.parse_name(r.name), p.parse_name(&pq.name)) { + Ok(true) => {} + Ok(false) => { + net_trace!("answer name mismatch: {:?}", r); + continue; + } + Err(_) => { + net_trace!("dns answer record name malformed"); + return; + } } match r.data { @@ -366,7 +398,10 @@ impl<'a> Socket<'a> { // records for the CNAME when we parse them later. // I believe it's mandatory the CNAME results MUST come *after* in the // packet, so it's enough to do one linear pass over it. - copy_name(&mut pq.name, p.parse_name(name))?; + if copy_name(&mut pq.name, p.parse_name(name)).is_err() { + net_trace!("dns answer cname malformed"); + return; + } } RecordData::Other(type_, data) => { net_trace!("unknown: {:?} {:?}", type_, data) @@ -381,13 +416,12 @@ impl<'a> Socket<'a> { }); // If we get here, packet matched the current query, stop processing. - return Ok(()); + return; } } // If we get here, packet matched with no query. net_trace!("no query matched"); - Ok(()) } pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 55aff6c94..8c93c2579 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -409,55 +409,46 @@ impl<'a> Socket<'a> { } } - pub(crate) fn process( - &mut self, - _cx: &mut Context, - ip_repr: &IpRepr, - icmp_repr: &IcmpRepr, - ) -> Result<(), Error> { + pub(crate) fn process(&mut self, _cx: &mut Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) { match *icmp_repr { #[cfg(feature = "proto-ipv4")] IcmpRepr::Ipv4(ref icmp_repr) => { - let packet_buf = self + net_trace!("icmp: receiving {} octets", icmp_repr.buffer_len()); + + match self .rx_buffer .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr()) - .map_err(|_| Error::Exhausted)?; - icmp_repr.emit( - &mut Icmpv4Packet::new_unchecked(packet_buf), - &ChecksumCapabilities::default(), - ); - - net_trace!( - "icmp:{}: receiving {} octets", - icmp_repr.buffer_len(), - packet_buf.len() - ); + { + Ok(packet_buf) => { + icmp_repr.emit( + &mut Icmpv4Packet::new_unchecked(packet_buf), + &ChecksumCapabilities::default(), + ); + } + Err(_) => net_trace!("icmp: buffer full, dropped incoming packet"), + } } #[cfg(feature = "proto-ipv6")] IcmpRepr::Ipv6(ref icmp_repr) => { - let packet_buf = self + net_trace!("icmp: receiving {} octets", icmp_repr.buffer_len()); + + match self .rx_buffer .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr()) - .map_err(|_| Error::Exhausted)?; - icmp_repr.emit( - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(packet_buf), - &ChecksumCapabilities::default(), - ); - - net_trace!( - "icmp:{}: receiving {} octets", - icmp_repr.buffer_len(), - packet_buf.len() - ); + { + Ok(packet_buf) => icmp_repr.emit( + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &mut Icmpv6Packet::new_unchecked(packet_buf), + &ChecksumCapabilities::default(), + ), + Err(_) => net_trace!("icmp: buffer full, dropped incoming packet"), + } } } #[cfg(feature = "async")] self.rx_waker.wake(); - - Ok(()) } pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> @@ -709,17 +700,11 @@ mod test_ipv4 { let data = &packet.into_inner()[..]; assert!(socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); - assert_eq!( - socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()); assert!(socket.can_recv()); assert!(socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); - assert_eq!( - socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()), - Err(Error::Exhausted) - ); + socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()); assert_eq!(socket.recv(), Ok((data, REMOTE_IPV4.into()))); assert!(!socket.can_recv()); @@ -791,7 +776,7 @@ mod test_ipv4 { // Ensure we can accept ICMP error response to the bound // UDP port assert!(socket.accepts(&mut cx, &ip_repr, &icmp_repr.into())); - assert_eq!(socket.process(&mut cx, &ip_repr, &icmp_repr.into()), Ok(())); + socket.process(&mut cx, &ip_repr, &icmp_repr.into()); assert!(socket.can_recv()); let mut bytes = [0x00; 46]; @@ -972,17 +957,11 @@ mod test_ipv6 { let data = &packet.into_inner()[..]; assert!(socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); - assert_eq!( - socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()); assert!(socket.can_recv()); assert!(socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); - assert_eq!( - socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()), - Err(Error::Exhausted) - ); + socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()); assert_eq!(socket.recv(), Ok((data, REMOTE_IPV6.into()))); assert!(!socket.can_recv()); @@ -1059,7 +1038,7 @@ mod test_ipv6 { // Ensure we can accept ICMP error response to the bound // UDP port assert!(socket.accepts(&mut cx, &ip_repr, &icmp_repr.into())); - assert_eq!(socket.process(&mut cx, &ip_repr, &icmp_repr.into()), Ok(())); + socket.process(&mut cx, &ip_repr, &icmp_repr.into()); assert!(socket.can_recv()); let mut bytes = [0x00; 66]; diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 985dadde9..021ef4af8 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -236,34 +236,33 @@ impl<'a> Socket<'a> { true } - pub(crate) fn process( - &mut self, - cx: &mut Context, - ip_repr: &IpRepr, - payload: &[u8], - ) -> Result<(), Error> { + pub(crate) fn process(&mut self, cx: &mut Context, ip_repr: &IpRepr, payload: &[u8]) { 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, ()) - .map_err(|_| Error::Exhausted)?; - ip_repr.emit(&mut packet_buf[..header_len], &cx.checksum_caps()); - packet_buf[header_len..].copy_from_slice(payload); net_trace!( "raw:{}:{}: receiving {} octets", self.ip_version, self.ip_protocol, - packet_buf.len() + total_len ); + match self.rx_buffer.enqueue(total_len, ()) { + Ok(buf) => { + ip_repr.emit(&mut buf[..header_len], &cx.checksum_caps()); + buf[header_len..].copy_from_slice(payload); + } + Err(_) => net_trace!( + "raw:{}:{}: buffer full, dropped incoming packet", + self.ip_version, + self.ip_protocol + ), + } + #[cfg(feature = "async")] self.rx_waker.wake(); - - Ok(()) } pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> @@ -488,7 +487,7 @@ mod test { let mut cx = Context::mock(); assert!(socket.accepts(&$hdr)); - assert_eq!(socket.process(&mut cx, &$hdr, &$payload), Ok(())); + socket.process(&mut cx, &$hdr, &$payload); let mut slice = [0; 4]; assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4)); @@ -504,10 +503,7 @@ mod test { buffer[..$packet.len()].copy_from_slice(&$packet[..]); assert!(socket.accepts(&$hdr)); - assert_eq!( - socket.process(&mut cx, &$hdr, &buffer), - Err(Error::Exhausted) - ); + socket.process(&mut cx, &$hdr, &buffer); } } }; @@ -583,24 +579,18 @@ mod test { assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); - assert_eq!( - socket.process( - &mut cx, - &ipv4_locals::HEADER_REPR, - &ipv4_locals::PACKET_PAYLOAD - ), - Ok(()) + socket.process( + &mut cx, + &ipv4_locals::HEADER_REPR, + &ipv4_locals::PACKET_PAYLOAD, ); assert!(socket.can_recv()); assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); - assert_eq!( - socket.process( - &mut cx, - &ipv4_locals::HEADER_REPR, - &ipv4_locals::PACKET_PAYLOAD - ), - Err(Error::Exhausted) + socket.process( + &mut cx, + &ipv4_locals::HEADER_REPR, + &ipv4_locals::PACKET_PAYLOAD, ); assert_eq!(socket.recv(), Ok(&cksumd_packet[..])); assert!(!socket.can_recv()); @@ -613,24 +603,18 @@ mod test { assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); - assert_eq!( - socket.process( - &mut cx, - &ipv6_locals::HEADER_REPR, - &ipv6_locals::PACKET_PAYLOAD - ), - Ok(()) + socket.process( + &mut cx, + &ipv6_locals::HEADER_REPR, + &ipv6_locals::PACKET_PAYLOAD, ); assert!(socket.can_recv()); assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); - assert_eq!( - socket.process( - &mut cx, - &ipv6_locals::HEADER_REPR, - &ipv6_locals::PACKET_PAYLOAD - ), - Err(Error::Exhausted) + socket.process( + &mut cx, + &ipv6_locals::HEADER_REPR, + &ipv6_locals::PACKET_PAYLOAD, ); assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..])); assert!(!socket.can_recv()); diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 53aec9a2b..3c2e64417 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1293,7 +1293,7 @@ impl<'a> Socket<'a> { cx: &mut Context, ip_repr: &IpRepr, repr: &TcpRepr, - ) -> Result)>, Error> { + ) -> Option<(IpRepr, TcpRepr<'static>)> { debug_assert!(self.accepts(cx, ip_repr, repr)); // Consider how much the sequence number space differs from the transmit buffer space. @@ -1314,12 +1314,12 @@ impl<'a> Socket<'a> { // the initial SYN. (State::SynSent, TcpControl::Rst, None) => { net_debug!("unacceptable RST (expecting RST|ACK) in response to initial SYN"); - return Err(Error::Dropped); + return None; } (State::SynSent, TcpControl::Rst, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!("unacceptable RST|ACK in response to initial SYN"); - return Err(Error::Dropped); + return None; } } // Any other RST need only have a valid sequence number. @@ -1331,13 +1331,13 @@ impl<'a> Socket<'a> { // Every packet after the initial SYN must be an acknowledgement. (_, _, None) => { net_debug!("expecting an ACK"); - return Err(Error::Dropped); + return None; } // SYN|ACK in the SYN-SENT state must have the exact ACK number. (State::SynSent, TcpControl::Syn, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!("unacceptable SYN|ACK in response to initial SYN"); - return Ok(Some(Self::rst_reply(ip_repr, repr))); + return Some(Self::rst_reply(ip_repr, repr)); } } // ACKs in the SYN-SENT state are invalid. @@ -1350,24 +1350,24 @@ impl<'a> Socket<'a> { net_debug!( "expecting a SYN|ACK, received an ACK with the right ack_number, ignoring." ); - return Err(Error::Dropped); + return None; } net_debug!( "expecting a SYN|ACK, received an ACK with the wrong ack_number, sending RST." ); - return Ok(Some(Self::rst_reply(ip_repr, repr))); + return Some(Self::rst_reply(ip_repr, repr)); } // Anything else in the SYN-SENT state is invalid. (State::SynSent, _, _) => { net_debug!("expecting a SYN|ACK"); - return Err(Error::Dropped); + return None; } // ACK in the SYN-RECEIVED state must have the exact ACK number, or we RST it. (State::SynReceived, _, Some(ack_number)) => { if ack_number != self.local_seq_no + 1 { net_debug!("unacceptable ACK in response to SYN|ACK"); - return Ok(Some(Self::rst_reply(ip_repr, repr))); + return Some(Self::rst_reply(ip_repr, repr)); } } // Every acknowledgement must be for transmitted but unacknowledged data. @@ -1390,7 +1390,7 @@ impl<'a> Socket<'a> { ack_min, ack_max ); - return Err(Error::Dropped); + return None; } if ack_number > ack_max { @@ -1400,7 +1400,7 @@ impl<'a> Socket<'a> { ack_min, ack_max ); - return Ok(self.challenge_ack_reply(cx, ip_repr, repr)); + return self.challenge_ack_reply(cx, ip_repr, repr); } } } @@ -1452,7 +1452,7 @@ impl<'a> Socket<'a> { self.timer.set_for_close(cx.now()); } - return Ok(self.challenge_ack_reply(cx, ip_repr, repr)); + return self.challenge_ack_reply(cx, ip_repr, repr); } } } @@ -1497,14 +1497,14 @@ impl<'a> Socket<'a> { // Validate and update the state. match (self.state, control) { // RSTs are not accepted in the LISTEN state. - (State::Listen, TcpControl::Rst) => return Err(Error::Dropped), + (State::Listen, TcpControl::Rst) => return None, // RSTs in SYN-RECEIVED flip the socket back to the LISTEN state. (State::SynReceived, TcpControl::Rst) => { tcp_trace!("received RST"); self.tuple = None; self.set_state(State::Listen); - return Ok(None); + return None; } // RSTs in any other state close the socket. @@ -1512,7 +1512,7 @@ impl<'a> Socket<'a> { tcp_trace!("received RST"); self.set_state(State::Closed); self.tuple = None; - return Ok(None); + return None; } // SYN packets in the LISTEN state change it to SYN-RECEIVED. @@ -1521,7 +1521,7 @@ impl<'a> Socket<'a> { if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { tcp_trace!("received SYNACK with zero MSS, ignoring"); - return Ok(None); + return None; } self.remote_mss = max_seg_size as usize } @@ -1565,7 +1565,7 @@ impl<'a> Socket<'a> { if let Some(max_seg_size) = repr.max_seg_size { if max_seg_size == 0 { tcp_trace!("received SYNACK with zero MSS, ignoring"); - return Ok(None); + return None; } self.remote_mss = max_seg_size as usize; } @@ -1663,7 +1663,7 @@ impl<'a> Socket<'a> { _ => { net_debug!("unexpected packet {}", repr); - return Err(Error::Dropped); + return None; } } @@ -1753,7 +1753,7 @@ impl<'a> Socket<'a> { let payload_len = repr.payload.len(); if payload_len == 0 { - return Ok(None); + return None; } let assembler_was_empty = self.assembler.is_empty(); @@ -1779,7 +1779,7 @@ impl<'a> Socket<'a> { payload_len, payload_offset ); - return Err(Error::Dropped); + return None; } } @@ -1835,9 +1835,9 @@ impl<'a> Socket<'a> { // This is fine because smoltcp assumes that it can always transmit zero or one // packets for every packet it receives. tcp_trace!("ACKing incoming segment"); - Ok(Some(self.ack_reply(ip_repr, repr))) + Some(self.ack_reply(ip_repr, repr)) } else { - Ok(None) + None } } @@ -2396,7 +2396,7 @@ mod test { socket: &mut TestSocket, timestamp: Instant, repr: &TcpRepr, - ) -> Result>, Error> { + ) -> Option> { socket.cx.set_now(timestamp); let ip_repr = IpReprIpvX(IpvXRepr { @@ -2411,12 +2411,11 @@ mod test { assert!(socket.socket.accepts(&mut socket.cx, &ip_repr, repr)); match socket.socket.process(&mut socket.cx, &ip_repr, repr) { - Ok(Some((_ip_repr, repr))) => { + Some((_ip_repr, repr)) => { net_trace!("recv: {}", repr); - Ok(Some(repr)) + Some(repr) } - Ok(None) => Ok(None), - Err(err) => Err(err), + None => None, } } @@ -2449,7 +2448,7 @@ mod test { ($socket:ident, $repr:expr, $result:expr) => (send!($socket, time 0, $repr, $result)); ($socket:ident, time $time:expr, $repr:expr) => - (send!($socket, time $time, $repr, Ok(None))); + (send!($socket, time $time, $repr, None)); ($socket:ident, time $time:expr, $repr:expr, $result:expr) => (assert_eq!(send(&mut $socket, Instant::from_millis($time), &$repr), $result)); } @@ -2817,9 +2816,9 @@ mod test { seq_number: REMOTE_SEQ, ack_number: None, ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); + assert_eq!(s.state, State::Listen); } #[test] @@ -2878,13 +2877,13 @@ mod test { ack_number: Some(LOCAL_SEQ), // wrong ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ, ack_number: None, window_len: 0, ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::SynReceived); } @@ -2909,13 +2908,13 @@ mod test { ack_number: Some(LOCAL_SEQ + 2), // wrong ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 2, ack_number: None, window_len: 0, ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::SynReceived); } @@ -3232,13 +3231,13 @@ mod test { window_scale: Some(0), ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ, ack_number: None, window_len: 0, ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::SynSent); } @@ -3268,8 +3267,7 @@ mod test { seq_number: REMOTE_SEQ, ack_number: None, ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); assert_eq!(s.state, State::SynSent); } @@ -3284,8 +3282,7 @@ mod test { seq_number: REMOTE_SEQ, ack_number: Some(TcpSeqNumber(1234)), ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); assert_eq!(s.state, State::SynSent); } @@ -3312,8 +3309,7 @@ mod test { seq_number: REMOTE_SEQ, ack_number: Some(LOCAL_SEQ + 1), // Correct ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); // It should trigger no response and change no state @@ -3344,13 +3340,13 @@ mod test { ack_number: Some(LOCAL_SEQ), // WRONG ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ, // matching the ack_number of the unexpected ack ack_number: None, window_len: 0, ..RECV_TEMPL - })) + }) ); // It should trigger a RST, and change no state @@ -3380,13 +3376,13 @@ mod test { ack_number: Some(LOCAL_SEQ + 123456), // WRONG ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 123456, // matching the ack_number of the unexpected ack ack_number: None, window_len: 0, ..RECV_TEMPL - })) + }) ); // It should trigger a RST, and change no state @@ -3614,7 +3610,7 @@ mod test { payload: &segment, ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 5000), window_len: 4000, @@ -3627,7 +3623,7 @@ mod test { None ], ..RECV_TEMPL - })) + }) ); } } @@ -3846,8 +3842,7 @@ mod test { seq_number: REMOTE_SEQ + 1, ack_number: None, ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); } @@ -3861,8 +3856,7 @@ mod test { seq_number: REMOTE_SEQ + 1, ack_number: Some(TcpSeqNumber(LOCAL_SEQ.0 - 1)), ..SEND_TEMPL - }, - Err(Error::Dropped) + } ); assert_eq!(s.local_seq_no, LOCAL_SEQ + 1); // Data not yet transmitted. @@ -3873,11 +3867,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 10), ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); assert_eq!(s.local_seq_no, LOCAL_SEQ + 1); } @@ -3893,11 +3887,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); @@ -3909,8 +3903,7 @@ mod test { seq_number: REMOTE_SEQ + 1 + 256, ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL - }, - Ok(None) + } ); // If we wait a bit, we do get a new one. @@ -3922,11 +3915,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); assert_eq!(s.remote_seq_no, REMOTE_SEQ + 1); } @@ -3967,11 +3960,11 @@ mod test { payload: &b"123456"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::Established); send!( @@ -3982,12 +3975,12 @@ mod test { payload: &b"abcdef"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6 + 6), window_len: 52, ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::Established); } @@ -4082,11 +4075,11 @@ mod test { ack_number: None, ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); assert_eq!(s.state, State::Established); @@ -4114,12 +4107,12 @@ mod test { ack_number: None, ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 2), // this has changed window_len: 63, ..RECV_TEMPL - })) + }) ); } @@ -4358,11 +4351,11 @@ mod test { seq_number: REMOTE_SEQ + 1, ack_number: Some(LOCAL_SEQ + 1 + 1), ..SEND_TEMPL - }, Ok(Some(TcpRepr { + }, Some(TcpRepr { seq_number: LOCAL_SEQ + 1 + 1, ack_number: Some(REMOTE_SEQ + 1 + 1), ..RECV_TEMPL - }))); + })); assert_eq!( s.timer, Timer::Close { @@ -4898,12 +4891,12 @@ mod test { payload: &b"abcdef"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6), window_len: 58, ..RECV_TEMPL - })) + }) ); } @@ -5740,12 +5733,12 @@ mod test { payload: &b"123456"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6), window_len: 0, ..RECV_TEMPL - })) + }) ); } @@ -5874,12 +5867,12 @@ mod test { payload: &b"def"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 3), window_len: 6, ..RECV_TEMPL - })) + }) ); send!( s, @@ -5889,12 +5882,12 @@ mod test { payload: &b"abc"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 9), window_len: 0, ..RECV_TEMPL - })) + }) ); assert_eq!(s.remote_last_win, s.rx_buffer.window() as u16); s.recv(|buffer| (buffer.len(), ())).unwrap(); @@ -6099,11 +6092,11 @@ mod test { ack_number: Some(LOCAL_SEQ + 1), ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); } @@ -6208,11 +6201,11 @@ mod test { payload: &b"def"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL - })) + }) ); s.recv(|buffer| { assert_eq!(buffer, b""); @@ -6227,12 +6220,12 @@ mod test { payload: &b"abcdef"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 6), window_len: 58, ..RECV_TEMPL - })) + }) ); s.recv(|buffer| { assert_eq!(buffer, b"abcdef"); @@ -6396,12 +6389,12 @@ mod test { payload: &b"ghi"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 3), window_len: 61, ..RECV_TEMPL - })) + }) ); s.recv(|data| { assert_eq!(data, b"abc"); @@ -6476,12 +6469,12 @@ mod test { payload: &b"ghi"[..], ..SEND_TEMPL }, - Ok(Some(TcpRepr { + Some(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1 + 3), window_len: 61, ..RECV_TEMPL - })) + }) ); send!( s, diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 664dfd705..e100e0c7c 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -353,7 +353,7 @@ impl<'a> Socket<'a> { ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8], - ) -> Result<(), Error> { + ) { debug_assert!(self.accepts(cx, ip_repr, repr)); let size = payload.len(); @@ -362,10 +362,6 @@ impl<'a> Socket<'a> { addr: ip_repr.src_addr(), port: repr.src_port, }; - self.rx_buffer - .enqueue(size, remote_endpoint) - .map_err(|_| Error::Exhausted)? - .copy_from_slice(payload); net_trace!( "udp:{}:{}: receiving {} octets", @@ -374,10 +370,17 @@ impl<'a> Socket<'a> { size ); + match self.rx_buffer.enqueue(size, remote_endpoint) { + Ok(buf) => buf.copy_from_slice(payload), + Err(_) => net_trace!( + "udp:{}:{}: buffer full, dropped incoming packet", + self.endpoint, + remote_endpoint + ), + } + #[cfg(feature = "async")] self.rx_waker.wake(); - - Ok(()) } pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> @@ -630,17 +633,12 @@ mod test { assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - assert_eq!( - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); assert!(socket.can_recv()); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - assert_eq!( - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), - Err(Error::Exhausted) - ); + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); + assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); assert!(!socket.can_recv()); } @@ -654,10 +652,7 @@ mod test { assert_eq!(socket.peek(), Err(RecvError::Exhausted)); - assert_eq!( - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END))); assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); assert_eq!(socket.peek(), Err(RecvError::Exhausted)); @@ -671,10 +666,7 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - assert_eq!( - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); let mut slice = [0; 4]; assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END))); @@ -688,10 +680,7 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - assert_eq!( - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD), - Ok(()) - ); + socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); let mut slice = [0; 4]; assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END))); @@ -780,7 +769,7 @@ mod test { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, }; - assert_eq!(socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]), Ok(())); + socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]); assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); } From e60fbe2235cdf2a9099ed45a939e7ea8e7aa3825 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 04:35:52 +0200 Subject: [PATCH 334/566] iface: make all process_* infallible. Fixes #281 --- src/iface/interface.rs | 361 ++++++++++++++++++++--------------------- 1 file changed, 174 insertions(+), 187 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 62a1f2c32..8c07cd80d 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -24,6 +24,22 @@ use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; +macro_rules! check { + ($e:expr) => { + match $e { + Ok(x) => x, + Err(_) => { + // concat!/stringify! doesn't work with defmt macros + #[cfg(not(feature = "defmt"))] + net_trace!(concat!("iface: malformed ", stringify!($e))); + #[cfg(feature = "defmt")] + net_trace!("iface: malformed"); + return Default::default(); + } + } + }; +} + /// A network interface. /// /// The network interface logically owns a number of other data structures; to avoid @@ -825,61 +841,38 @@ where .. } = self; while let Some((rx_token, tx_token)) = device.receive() { - if let Err(err) = rx_token.consume(inner.now, |frame| match inner.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => match inner.process_ethernet(sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { + let res = rx_token.consume(inner.now, |frame| { + match inner.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + if let Some(packet) = inner.process_ethernet(sockets, &frame) { if let Err(err) = inner.dispatch(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } - Ok(()) - } - Err(err) => { - net_debug!("cannot process ingress packet: {}", err); - #[cfg(not(feature = "defmt"))] - net_debug!( - "packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame) - ); - Err(err) } - }, - #[cfg(feature = "medium-ip")] - Medium::Ip => match inner.process_ip(sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { + #[cfg(feature = "medium-ip")] + Medium::Ip => { + if let Some(packet) = inner.process_ip(sockets, &frame) { if let Err(err) = inner.dispatch_ip(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } - Ok(()) } - Err(err) => { - net_debug!("cannot process ingress packet: {}", err); - Err(err) - } - }, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => match inner.process_ieee802154(sockets, &frame) { - Ok(response) => { - processed_any = true; - if let Some(packet) = response { + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + if let Some(packet) = inner.process_ieee802154(sockets, &frame) { if let Err(err) = inner.dispatch_ieee802154(tx_token, packet) { net_debug!("Failed to send response: {}", err); } } - Ok(()) - } - Err(err) => { - net_debug!("cannot process ingress packet: {}", err); - Err(err) } - }, - }) { + } + processed_any = true; + Ok(()) + }); + + if let Err(err) = res { net_debug!("Failed to consume RX token: {}", err); } } @@ -1258,15 +1251,15 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, frame: &'frame T, - ) -> Result>> { - let eth_frame = EthernetFrame::new_checked(frame)?; + ) -> Option> { + let eth_frame = check!(EthernetFrame::new_checked(frame)); // Ignore any packets not directed to our hardware address or any of the multicast groups. if !eth_frame.dst_addr().is_broadcast() && !eth_frame.dst_addr().is_multicast() && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr.unwrap() { - return Ok(None); + return None; } match eth_frame.ethertype() { @@ -1274,18 +1267,18 @@ impl<'a> InterfaceInner<'a> { EthernetProtocol::Arp => self.process_arp(self.now, ð_frame), #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => { - let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; + let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); self.process_ipv4(sockets, &ipv4_packet) - .map(|o| o.map(EthernetPacket::Ip)) + .map(EthernetPacket::Ip) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { - let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; + let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload())); self.process_ipv6(sockets, &ipv6_packet) - .map(|o| o.map(EthernetPacket::Ip)) + .map(EthernetPacket::Ip) } // Drop all other traffic. - _ => Err(Error::Unrecognized), + _ => None, } } @@ -1294,20 +1287,20 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, - ) -> Result>> { + ) -> Option> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { - let ipv4_packet = Ipv4Packet::new_checked(ip_payload)?; + let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); self.process_ipv4(sockets, &ipv4_packet) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { - let ipv6_packet = Ipv6Packet::new_checked(ip_payload)?; + let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload)); self.process_ipv6(sockets, &ipv6_packet) } // Drop all other traffic. - _ => Err(Error::Unrecognized), + _ => None, } } @@ -1316,12 +1309,12 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, sixlowpan_payload: &'frame T, - ) -> Result>> { - let ieee802154_frame = Ieee802154Frame::new_checked(sixlowpan_payload)?; - let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame)?; + ) -> Option> { + let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); + let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); if ieee802154_repr.frame_type != Ieee802154FrameType::Data { - return Ok(None); + return None; } // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this @@ -1335,12 +1328,12 @@ impl<'a> InterfaceInner<'a> { "dropping {:?} because not our PAN id (or not broadcast)", ieee802154_repr ); - return Ok(None); + return None; } match ieee802154_frame.payload() { Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload), - None => Ok(None), + None => None, } } @@ -1350,14 +1343,14 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'frame T, - ) -> Result>> { + ) -> Option> { // The first header needs to be an IPHC header. - let iphc_packet = SixlowpanIphcPacket::new_checked(payload)?; - let iphc_repr = SixlowpanIphcRepr::parse( + let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload)); + let iphc_repr = check!(SixlowpanIphcRepr::parse( &iphc_packet, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, - )?; + )); let payload = iphc_packet.payload(); let mut ipv6_repr = Ipv6Repr { @@ -1371,26 +1364,26 @@ impl<'a> InterfaceInner<'a> { // Currently we assume the next header is a UDP, so we ignore everything else. match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { - match SixlowpanNhcPacket::dispatch(payload)? { + match check!(SixlowpanNhcPacket::dispatch(payload)) { SixlowpanNhcPacket::ExtensionHeader(_) => { net_debug!("Extension headers are currently not supported for 6LoWPAN"); - Ok(None) + None } #[cfg(not(feature = "socket-udp"))] SixlowpanNhcPacket::UdpHeader(_) => { net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); - Ok(None) + None } #[cfg(feature = "socket-udp")] SixlowpanNhcPacket::UdpHeader(udp_packet) => { ipv6_repr.next_header = IpProtocol::Udp; // Handle the UDP - let udp_repr = SixlowpanUdpRepr::parse( + let udp_repr = check!(SixlowpanUdpRepr::parse( &udp_packet, &iphc_repr.src_addr, &iphc_repr.dst_addr, udp_packet.checksum(), - )?; + )); // Look for UDP sockets that will accept the UDP packet. // If it does not accept the packet, then send an ICMP message. @@ -1405,7 +1398,7 @@ impl<'a> InterfaceInner<'a> { &udp_repr, udp_packet.payload(), ); - return Ok(None); + return None; } } @@ -1419,7 +1412,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &payload[0..payload_len], }; - Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) + self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr) } } } @@ -1430,7 +1423,7 @@ impl<'a> InterfaceInner<'a> { } _ => { net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN"); - Ok(None) + None } }, } @@ -1441,9 +1434,9 @@ impl<'a> InterfaceInner<'a> { &mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>, - ) -> Result>> { - let arp_packet = ArpPacket::new_checked(eth_frame.payload())?; - let arp_repr = ArpRepr::parse(&arp_packet)?; + ) -> Option> { + let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload())); + let arp_repr = check!(ArpRepr::parse(&arp_packet)); match arp_repr { ArpRepr::EthernetIpv4 { @@ -1455,24 +1448,24 @@ impl<'a> InterfaceInner<'a> { } => { // Only process ARP packets for us. if !self.has_ip_addr(target_protocol_addr) { - return Ok(None); + return None; } // Only process REQUEST and RESPONSE. if let ArpOperation::Unknown(_) = operation { net_debug!("arp: unknown operation code"); - return Err(Error::Malformed); + return None; } // Discard packets with non-unicast source addresses. if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { net_debug!("arp: non-unicast source address"); - return Err(Error::Malformed); + return None; } if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) { net_debug!("arp: source IP address not in same network as us"); - return Err(Error::Malformed); + return None; } // Fill the ARP cache from any ARP packet aimed at us (both request or response). @@ -1491,15 +1484,15 @@ impl<'a> InterfaceInner<'a> { _ => unreachable!(), }; - Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: src_hardware_addr, source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr, - }))) + })) } else { - Ok(None) + None } } } @@ -1532,13 +1525,13 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ipv6_packet: &Ipv6Packet<&'frame T>, - ) -> Result>> { - let ipv6_repr = Ipv6Repr::parse(ipv6_packet)?; + ) -> Option> { + let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet)); if !ipv6_repr.src_addr.is_unicast() { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); - return Err(Error::Malformed); + return None; } let ip_payload = ipv6_packet.payload(); @@ -1567,7 +1560,7 @@ impl<'a> InterfaceInner<'a> { nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8], - ) -> Result>> { + ) -> Option> { match nxt_hdr { IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload), @@ -1584,7 +1577,7 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "socket-raw")] - _ if handled_by_raw_socket => Ok(None), + _ if handled_by_raw_socket => None, _ => { // Send back as much of the original payload as we can. @@ -1597,7 +1590,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) + self.icmpv6_reply(ipv6_repr, icmp_reply_repr) } } } @@ -1607,13 +1600,13 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'frame T>, - ) -> Result>> { - let ipv4_repr = Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)?; + ) -> Option> { + let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); if !self.is_unicast_v4(ipv4_repr.src_addr) { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); - return Err(Error::Malformed); + return None; } let ip_repr = IpRepr::Ipv4(ipv4_repr); @@ -1629,7 +1622,7 @@ impl<'a> InterfaceInner<'a> { if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { // First check for source and dest ports, then do `UdpRepr::parse` if they match. // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) - let udp_packet = UdpPacket::new_checked(ip_payload)?; + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); if udp_packet.src_port() == DHCP_SERVER_PORT && udp_packet.dst_port() == DHCP_CLIENT_PORT { @@ -1639,12 +1632,16 @@ impl<'a> InterfaceInner<'a> { .next() { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let udp_repr = - UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &src_addr, + &dst_addr, + &self.caps.checksum + )); let udp_payload = udp_packet.payload(); dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload); - return Ok(None); + return None; } } } @@ -1663,7 +1660,7 @@ impl<'a> InterfaceInner<'a> { .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now) .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) { - return Ok(None); + return None; } } @@ -1681,7 +1678,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-tcp")] IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload), - _ if handled_by_raw_socket => Ok(None), + _ if handled_by_raw_socket => None, _ => { // Send back as much of the original payload as we can. @@ -1692,7 +1689,7 @@ impl<'a> InterfaceInner<'a> { header: ipv4_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)) + self.icmpv4_reply(ipv4_repr, icmp_reply_repr) } } } @@ -1733,9 +1730,9 @@ impl<'a> InterfaceInner<'a> { &mut self, ipv4_repr: Ipv4Repr, ip_payload: &'frame [u8], - ) -> Result>> { - let igmp_packet = IgmpPacket::new_checked(ip_payload)?; - let igmp_repr = IgmpRepr::parse(&igmp_packet)?; + ) -> Option> { + let igmp_packet = check!(IgmpPacket::new_checked(ip_payload)); + let igmp_repr = check!(IgmpRepr::parse(&igmp_packet)); // FIXME: report membership after a delay match igmp_repr { @@ -1786,7 +1783,7 @@ impl<'a> InterfaceInner<'a> { IgmpRepr::LeaveGroup { .. } => (), } - Ok(None) + None } #[cfg(feature = "proto-ipv6")] @@ -1795,14 +1792,14 @@ impl<'a> InterfaceInner<'a> { _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], - ) -> Result>> { - let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; - let icmp_repr = Icmpv6Repr::parse( + ) -> Option> { + let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload)); + let icmp_repr = check!(Icmpv6Repr::parse( &ip_repr.src_addr(), &ip_repr.dst_addr(), &icmp_packet, &self.caps.checksum, - )?; + )); #[cfg(feature = "socket-icmp")] let mut handled_by_icmp_socket = false; @@ -1831,14 +1828,14 @@ impl<'a> InterfaceInner<'a> { seq_no, data, }; - Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr)) + self.icmpv6_reply(ipv6_repr, icmp_reply_repr) } #[allow(unreachable_patterns)] _ => unreachable!(), }, // Ignore any echo replies. - Icmpv6Repr::EchoReply { .. } => Ok(None), + Icmpv6Repr::EchoReply { .. } => None, // Forward any NDISC packets to the ndisc packet handler #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -1851,10 +1848,10 @@ impl<'a> InterfaceInner<'a> { // Don't report an error if a packet with unknown type // has been handled by an ICMP socket #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => Ok(None), + _ if handled_by_icmp_socket => None, // FIXME: do something correct here? - _ => Err(Error::Unrecognized), + _ => None, } } @@ -1866,7 +1863,7 @@ impl<'a> InterfaceInner<'a> { &mut self, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>, - ) -> Result>> { + ) -> Option> { match repr { NdiscRepr::NeighborAdvert { lladdr, @@ -1875,9 +1872,9 @@ impl<'a> InterfaceInner<'a> { } => { let ip_addr = ip_repr.src_addr.into(); if let Some(lladdr) = lladdr { - let lladdr = lladdr.parse(self.caps.medium)?; + let lladdr = check!(lladdr.parse(self.caps.medium)); if !lladdr.is_unicast() || !target_addr.is_unicast() { - return Err(Error::Malformed); + return None; } if flags.contains(NdiscNeighborFlags::OVERRIDE) || !self @@ -1893,7 +1890,7 @@ impl<'a> InterfaceInner<'a> { .fill(ip_addr, lladdr, self.now) } } - Ok(None) + None } NdiscRepr::NeighborSolicit { target_addr, @@ -1901,9 +1898,9 @@ impl<'a> InterfaceInner<'a> { .. } => { if let Some(lladdr) = lladdr { - let lladdr = lladdr.parse(self.caps.medium)?; + let lladdr = check!(lladdr.parse(self.caps.medium)); if !lladdr.is_unicast() || !target_addr.is_unicast() { - return Err(Error::Malformed); + return None; } self.neighbor_cache.as_mut().unwrap().fill( ip_repr.src_addr.into(), @@ -1926,12 +1923,12 @@ impl<'a> InterfaceInner<'a> { hop_limit: 0xff, payload_len: advert.buffer_len(), }; - Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) + Some(IpPacket::Icmpv6((ip_repr, advert))) } else { - Ok(None) + None } } - _ => Ok(None), + _ => None, } } @@ -1942,23 +1939,23 @@ impl<'a> InterfaceInner<'a> { ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, ip_payload: &'frame [u8], - ) -> Result>> { - let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?; - let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?; - for result in hbh_repr.options() { - let opt_repr = result?; + ) -> Option> { + let hbh_pkt = check!(Ipv6HopByHopHeader::new_checked(ip_payload)); + let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_pkt)); + for opt_repr in hbh_repr.options() { + let opt_repr = check!(opt_repr); match opt_repr { Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), Ipv6OptionRepr::Unknown { type_, .. } => { match Ipv6OptionFailureType::from(type_) { Ipv6OptionFailureType::Skip => (), Ipv6OptionFailureType::Discard => { - return Ok(None); + return None; } _ => { // FIXME(dlrobertson): Send an ICMPv6 parameter problem message // here. - return Err(Error::Unrecognized); + return None; } } } @@ -1979,9 +1976,9 @@ impl<'a> InterfaceInner<'a> { _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], - ) -> Result>> { - let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?; - let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)?; + ) -> Option> { + let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload)); + let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)); #[cfg(feature = "socket-icmp")] let mut handled_by_icmp_socket = false; @@ -2011,22 +2008,22 @@ impl<'a> InterfaceInner<'a> { data, }; match ip_repr { - IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)), + IpRepr::Ipv4(ipv4_repr) => self.icmpv4_reply(ipv4_repr, icmp_reply_repr), #[allow(unreachable_patterns)] _ => unreachable!(), } } // Ignore any echo replies. - Icmpv4Repr::EchoReply { .. } => Ok(None), + Icmpv4Repr::EchoReply { .. } => None, // Don't report an error if a packet with unknown type // has been handled by an ICMP socket #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => Ok(None), + _ if handled_by_icmp_socket => None, // FIXME: do something correct here? - _ => Err(Error::Unrecognized), + _ => None, } } @@ -2100,10 +2097,15 @@ impl<'a> InterfaceInner<'a> { ip_repr: IpRepr, handled_by_raw_socket: bool, ip_payload: &'frame [u8], - ) -> Result>> { + ) -> Option> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let udp_packet = UdpPacket::new_checked(ip_payload)?; - let udp_repr = UdpRepr::parse(&udp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &src_addr, + &dst_addr, + &self.caps.checksum + )); let udp_payload = udp_packet.payload(); #[cfg(feature = "socket-udp")] @@ -2113,7 +2115,7 @@ impl<'a> InterfaceInner<'a> { { if udp_socket.accepts(self, &ip_repr, &udp_repr) { udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); - return Ok(None); + return None; } } @@ -2124,16 +2126,16 @@ impl<'a> InterfaceInner<'a> { { if dns_socket.accepts(&ip_repr, &udp_repr) { dns_socket.process(self, &ip_repr, &udp_repr, udp_payload); - return Ok(None); + return None; } } // The packet wasn't handled by a socket, send an ICMP port unreachable packet. match ip_repr { #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) if handled_by_raw_socket => Ok(None), + IpRepr::Ipv4(_) if handled_by_raw_socket => None, #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) if handled_by_raw_socket => Ok(None), + IpRepr::Ipv6(_) if handled_by_raw_socket => None, #[cfg(feature = "proto-ipv4")] IpRepr::Ipv4(ipv4_repr) => { let payload_len = @@ -2143,7 +2145,7 @@ impl<'a> InterfaceInner<'a> { header: ipv4_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)) + self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr) } #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(ipv6_repr) => { @@ -2154,7 +2156,7 @@ impl<'a> InterfaceInner<'a> { header: ipv6_repr, data: &ip_payload[0..payload_len], }; - Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)) + self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr) } } } @@ -2165,30 +2167,33 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], - ) -> Result>> { + ) -> Option> { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let tcp_packet = TcpPacket::new_checked(ip_payload)?; - let tcp_repr = TcpRepr::parse(&tcp_packet, &src_addr, &dst_addr, &self.caps.checksum)?; + let tcp_packet = check!(TcpPacket::new_checked(ip_payload)); + let tcp_repr = check!(TcpRepr::parse( + &tcp_packet, + &src_addr, + &dst_addr, + &self.caps.checksum + )); for tcp_socket in sockets .iter_mut() .filter_map(|i| tcp::Socket::downcast(&mut i.socket)) { if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { - return Ok(tcp_socket + return tcp_socket .process(self, &ip_repr, &tcp_repr) - .map(IpPacket::Tcp)); + .map(IpPacket::Tcp); } } if tcp_repr.control == TcpControl::Rst { // Never reply to a TCP RST packet with another TCP RST packet. - Ok(None) + None } else { // The packet wasn't handled by a socket, send a TCP RST packet. - Ok(Some(IpPacket::Tcp(tcp::Socket::rst_reply( - &ip_repr, &tcp_repr, - )))) + Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr))) } } @@ -2797,10 +2802,7 @@ 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 iface.sockets, &frame), - Ok(None) - ); + assert_eq!(iface.inner.process_ipv4(&mut iface.sockets, &frame), None); } #[test] @@ -2828,10 +2830,7 @@ 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 iface.sockets, &frame), - Ok(None) - ); + assert_eq!(iface.inner.process_ipv6(&mut iface.sockets, &frame), None); } #[test] @@ -2882,7 +2881,7 @@ mod test { // And we correctly handle no payload. assert_eq!( iface.inner.process_ipv4(&mut iface.sockets, &frame), - Ok(Some(expected_repr)) + Some(expected_repr) ); } @@ -3008,7 +3007,7 @@ mod test { iface .inner .process_udp(&mut iface.sockets, ip_repr, false, data), - Ok(Some(expected_repr)) + Some(expected_repr) ); let ip_repr = IpRepr::Ipv4(Ipv4Repr { @@ -3039,7 +3038,7 @@ mod test { false, packet_broadcast.into_inner() ), - Ok(None) + None ); } @@ -3109,7 +3108,7 @@ mod test { iface .inner .process_udp(&mut iface.sockets, ip_repr, false, packet.into_inner()), - Ok(None) + None ); // Make sure the payload to the UDP packet processed by process_udp is @@ -3180,7 +3179,7 @@ mod test { assert_eq!( iface.inner.process_ipv4(&mut iface.sockets, &frame), - Ok(Some(expected_packet)) + Some(expected_packet) ); } @@ -3292,20 +3291,14 @@ mod test { iface .inner .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), - Ok(Some(IpPacket::Icmpv4(( - expected_ip_repr, - expected_icmp_repr - )))) + Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) ); #[cfg(feature = "proto-ipv6")] assert_eq!( iface .inner .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), - Ok(Some(IpPacket::Icmpv6(( - expected_ip_repr, - expected_icmp_repr - )))) + Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) ); } @@ -3341,13 +3334,13 @@ mod test { iface .inner .process_ethernet(&mut iface.sockets, frame.into_inner()), - Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, target_hardware_addr: remote_hw_addr, target_protocol_addr: remote_ip_addr - }))) + })) ); // Ensure the address of the requestor was entered in the cache @@ -3416,10 +3409,10 @@ mod test { iface .inner .process_ethernet(&mut iface.sockets, frame.into_inner()), - Ok(Some(EthernetPacket::Ip(IpPacket::Icmpv6(( + Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected - ))))) + )))) ); // Ensure the address of the requestor was entered in the cache @@ -3463,7 +3456,7 @@ mod test { iface .inner .process_ethernet(&mut iface.sockets, frame.into_inner()), - Ok(None) + None ); // Ensure the address of the requestor was NOT entered in the cache @@ -3511,13 +3504,13 @@ mod test { iface .inner .process_ethernet(&mut iface.sockets, frame.into_inner()), - Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, target_hardware_addr: remote_hw_addr, target_protocol_addr: remote_ip_addr - }))) + })) ); // Ensure the address of the requestor was entered in the cache @@ -3603,7 +3596,7 @@ mod test { iface .inner .process_icmpv4(&mut iface.sockets, ip_repr, icmp_data), - Ok(Some(IpPacket::Icmpv4((ipv4_reply, echo_reply)))) + Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) ); let socket = iface.get_socket::(socket_handle); @@ -3699,7 +3692,7 @@ mod test { // error message to be sent to the sender. assert_eq!( iface.inner.process_ipv6(&mut iface.sockets, &frame), - Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr)))) + Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) ); } @@ -3863,10 +3856,7 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!( - iface.inner.process_ipv4(&mut iface.sockets, &frame), - Ok(None) - ); + assert_eq!(iface.inner.process_ipv4(&mut iface.sockets, &frame), None); } #[test] @@ -3948,10 +3938,7 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!( - iface.inner.process_ipv4(&mut iface.sockets, &frame), - Ok(None) - ); + assert_eq!(iface.inner.process_ipv4(&mut iface.sockets, &frame), None); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP let socket = iface.get_socket::(udp_socket_handle); From be5c0c22227bca6f53131ce238a3bf27df805d1e Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Fri, 20 May 2022 09:59:36 -0700 Subject: [PATCH 335/566] dns: fix label-length validation The label length is represented by a single byte with the two most-significant bits reserved, which gives a maximum length of 63. --- src/socket/dns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 5788adb52..744e8231a 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -178,7 +178,7 @@ impl<'a> DnsSocket<'a> { let mut raw_name: Vec = Vec::new(); for s in name.split(|&c| c == b'.') { - if s.len() > 255 { + if s.len() > 63 { net_trace!("invalid name: too long label"); return Err(Error::Illegal); } From 43329e696ef22233981468bf2885159b81c0fa03 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 23:35:12 +0200 Subject: [PATCH 336/566] socket/dns: add own error enums for public API. --- examples/dns.rs | 5 +- src/socket/dns.rs | 119 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/examples/dns.rs b/examples/dns.rs index e48d2e349..c3b8cd3a1 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -10,12 +10,11 @@ mod utils; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; use smoltcp::phy::Device; use smoltcp::phy::{wait as phy_wait, Medium}; -use smoltcp::socket::dns; +use smoltcp::socket::dns::{self, GetQueryResultError}; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, }; -use smoltcp::Error; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; @@ -90,7 +89,7 @@ fn main() { println!("Query done: {:?}", addrs); break; } - Err(Error::Exhausted) => {} // not done yet + Err(GetQueryResultError::Pending) => {} // not done yet Err(e) => panic!("query failed: {:?}", e), } diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 26e42e71c..75f5e0351 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -8,7 +8,7 @@ use crate::socket::{Context, PollAt}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; -use crate::{Error, Result}; +use crate::Error; #[cfg(feature = "async")] use super::WakerRegistration; @@ -21,6 +21,25 @@ const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs +/// Error returned by [`Socket::start_query`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum StartQueryError { + NoFreeSlot, + InvalidName, + NameTooLong, +} + +/// Error returned by [`Socket::get_query_result`] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum GetQueryResultError { + /// Query is not done yet. + Pending, + /// Query failed. + Failed, +} + /// State for an in-progress DNS query. /// /// The only reason this struct is public is to allow the socket state @@ -139,20 +158,20 @@ impl<'a> Socket<'a> { self.hop_limit = hop_limit } - fn find_free_query(&mut self) -> Result { + fn find_free_query(&mut self) -> Option { for (i, q) in self.queries.iter().enumerate() { if q.is_none() { - return Ok(QueryHandle(i)); + return Some(QueryHandle(i)); } } match self.queries { - ManagedSlice::Borrowed(_) => Err(Error::Exhausted), + ManagedSlice::Borrowed(_) => None, #[cfg(any(feature = "std", feature = "alloc"))] ManagedSlice::Owned(ref mut queries) => { queries.push(None); let index = queries.len() - 1; - Ok(QueryHandle(index)) + Some(QueryHandle(index)) } } } @@ -162,12 +181,16 @@ impl<'a> Socket<'a> { /// `name` is specified in human-friendly format, such as `"rust-lang.org"`. /// It accepts names both with and without trailing dot, and they're treated /// the same (there's no support for DNS search path). - pub fn start_query(&mut self, cx: &mut Context, name: &str) -> Result { + pub fn start_query( + &mut self, + cx: &mut Context, + name: &str, + ) -> Result { let mut name = name.as_bytes(); if name.is_empty() { net_trace!("invalid name: zero length"); - return Err(Error::Illegal); + return Err(StartQueryError::InvalidName); } // Remove trailing dot, if any @@ -180,22 +203,26 @@ impl<'a> Socket<'a> { for s in name.split(|&c| c == b'.') { if s.len() > 255 { net_trace!("invalid name: too long label"); - return Err(Error::Illegal); + return Err(StartQueryError::InvalidName); } if s.is_empty() { net_trace!("invalid name: zero length label"); - return Err(Error::Illegal); + return Err(StartQueryError::InvalidName); } // Push label - raw_name.push(s.len() as u8).map_err(|_| Error::Exhausted)?; + raw_name + .push(s.len() as u8) + .map_err(|_| StartQueryError::NameTooLong)?; raw_name .extend_from_slice(s) - .map_err(|_| Error::Exhausted)?; + .map_err(|_| StartQueryError::NameTooLong)?; } // Push terminator. - raw_name.push(0x00).map_err(|_| Error::Exhausted)?; + raw_name + .push(0x00) + .map_err(|_| StartQueryError::NameTooLong)?; self.start_query_raw(cx, &raw_name) } @@ -204,12 +231,16 @@ impl<'a> Socket<'a> { /// `b"\x09rust-lang\x03org\x00"` /// /// You probably want to use [`start_query`] instead. - pub fn start_query_raw(&mut self, cx: &mut Context, raw_name: &[u8]) -> Result { - let handle = self.find_free_query()?; + pub fn start_query_raw( + &mut self, + cx: &mut Context, + raw_name: &[u8], + ) -> Result { + let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?; self.queries[handle.0] = Some(DnsQuery { state: State::Pending(PendingQuery { - name: Vec::from_slice(raw_name).map_err(|_| Error::Exhausted)?, + name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?, type_: Type::A, txid: cx.rand().rand_u16(), port: cx.rand().rand_source_port(), @@ -224,15 +255,21 @@ impl<'a> Socket<'a> { Ok(handle) } + /// Get the result of a query. + /// + /// If the query is completed, the query slot is automatically freed. + /// + /// # Panics + /// Panics if the QueryHandle corresponds to a free slot. pub fn get_query_result( &mut self, handle: QueryHandle, - ) -> Result> { - let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; - let q = slot.as_mut().ok_or(Error::Illegal)?; + ) -> Result, GetQueryResultError> { + let slot = &mut self.queries[handle.0]; + let q = slot.as_mut().unwrap(); match &mut q.state { // Query is not done yet. - State::Pending(_) => Err(Error::Exhausted), + State::Pending(_) => Err(GetQueryResultError::Pending), // Query is done State::Completed(q) => { let res = q.addresses.clone(); @@ -241,25 +278,38 @@ impl<'a> Socket<'a> { } State::Failure => { *slot = None; // Free up the slot for recycling. - Err(Error::Unaddressable) + Err(GetQueryResultError::Failed) } } } - pub fn cancel_query(&mut self, handle: QueryHandle) -> Result<()> { - let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; + /// Cancels a query, freeing the slot. + /// + /// # Panics + /// + /// Panics if the QueryHandle corresponds to an already free slot. + pub fn cancel_query(&mut self, handle: QueryHandle) { + let slot = &mut self.queries[handle.0]; if slot.is_none() { - return Err(Error::Illegal); + panic!("Canceling query in a free slot.") } *slot = None; // Free up the slot for recycling. - Ok(()) } + /// Assign a waker to a query slot + /// + /// The waker will be woken when the query completes, either successfully or failed. + /// + /// # Panics + /// + /// Panics if the QueryHandle corresponds to an already free slot. #[cfg(feature = "async")] - pub fn register_query_waker(&mut self, handle: QueryHandle, waker: &Waker) -> Result<()> { - let slot = self.queries.get_mut(handle.0).ok_or(Error::Illegal)?; - slot.as_mut().ok_or(Error::Illegal)?.waker.register(waker); - Ok(()) + pub fn register_query_waker(&mut self, handle: QueryHandle, waker: &Waker) { + self.queries[handle.0] + .as_mut() + .unwrap() + .waker + .register(waker); } pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool { @@ -424,9 +474,9 @@ impl<'a> Socket<'a> { net_trace!("no query matched"); } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<()>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), Error>, { let hop_limit = self.hop_limit.unwrap_or(64); @@ -566,18 +616,17 @@ fn eq_names<'a>( fn copy_name<'a, const N: usize>( dest: &mut Vec, name: impl Iterator>, -) -> Result<()> { +) -> Result<(), wire::Error> { dest.truncate(0); for label in name { let label = label?; - dest.push(label.len() as u8).map_err(|_| Error::Truncated)?; - dest.extend_from_slice(label) - .map_err(|_| Error::Truncated)?; + dest.push(label.len() as u8).map_err(|_| wire::Error)?; + dest.extend_from_slice(label).map_err(|_| wire::Error)?; } // Write terminator 0x00 - dest.push(0).map_err(|_| Error::Truncated)?; + dest.push(0).map_err(|_| wire::Error)?; Ok(()) } From 629f0bce7991bfb8e73aeeb3b39982338d0eb97b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 23:38:17 +0200 Subject: [PATCH 337/566] socket: make dispatch infallible, except for emit errors. socket dispatch can't be allowed to fail, it's too late to handle errors there. For example, you can push a malformed packet to a raw::Socket. Its dispatch() would detect that and error out, but *not* pop the packet from the queue, resulting in the socket getting stuck forever! Instead, all errors are now logged (and the offending packet is dropped if applicable). Also, Err(Exhausted) was returned as "no error, but I have no packets to emit", but that's not really useful to Interface, because it can already see if the socket emitted packets or not. --- src/socket/dhcpv4.rs | 17 +++-- src/socket/dns.rs | 12 ++-- src/socket/icmp.rs | 161 ++++++++++++++++++++++++++----------------- src/socket/raw.rs | 144 +++++++++++++++++++++----------------- src/socket/tcp.rs | 16 ++--- src/socket/udp.rs | 92 ++++++++++++++----------- 6 files changed, 250 insertions(+), 192 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 8c37c223e..d4f8649b5 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -6,7 +6,6 @@ use crate::wire::{ DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr, UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, }; -use crate::{Error, Result}; use super::PollAt; @@ -376,16 +375,16 @@ impl Socket { 0x12345678 } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<()> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>, + F: FnOnce(&mut Context, (Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<(), E>, { // note: Dhcpv4Socket is only usable in ethernet mediums, so the // unwrap can never fail. let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr() { addr } else { - return Err(Error::Malformed); + panic!("using DHCPv4 socket with a non-ethernet hardware address."); }; // Worst case biggest IPv4 header length. @@ -432,7 +431,7 @@ impl Socket { match &mut self.state { ClientState::Discovering(state) => { if cx.now() < state.retry_at { - return Err(Error::Exhausted); + return Ok(()); } // send packet @@ -451,13 +450,12 @@ impl Socket { } ClientState::Requesting(state) => { if cx.now() < state.retry_at { - return Err(Error::Exhausted); + return Ok(()); } if state.retry >= REQUEST_RETRIES { net_debug!("DHCP request retries exceeded, restarting discovery"); self.reset(); - // return Ok so we get polled again return Ok(()); } @@ -489,7 +487,7 @@ impl Socket { } if cx.now() < state.renew_at { - return Err(Error::Exhausted); + return Ok(()); } ipv4_repr.src_addr = state.config.address.address(); @@ -553,6 +551,7 @@ mod test { use super::*; use crate::wire::EthernetAddress; + use crate::Error; // =========================================================================================// // Helper functions @@ -622,7 +621,7 @@ mod test { None => panic!("Too many reprs emitted"), } i += 1; - Ok(()) + Ok::<_, Error>(()) }); } diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 75f5e0351..fa7f9a20f 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -8,7 +8,6 @@ use crate::socket::{Context, PollAt}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; -use crate::Error; #[cfg(feature = "async")] use super::WakerRegistration; @@ -474,9 +473,9 @@ impl<'a> Socket<'a> { net_trace!("no query matched"); } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), Error>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>, { let hop_limit = self.hop_limit.unwrap_or(64); @@ -556,10 +555,7 @@ impl<'a> Socket<'a> { udp_repr.src_port ); - if let Err(e) = emit(cx, (ip_repr, udp_repr, payload)) { - net_trace!("DNS emit error {:?}", e); - return Ok(()); - } + emit(cx, (ip_repr, udp_repr, payload))?; pq.retransmit_at = cx.now() + pq.delay; pq.delay = MAX_RETRANSMIT_DELAY.min(pq.delay * 2); @@ -569,7 +565,7 @@ impl<'a> Socket<'a> { } // Nothing to dispatch - Err(Error::Exhausted) + Ok(()) } pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt { diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 8c93c2579..f1416ccc1 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -6,8 +6,8 @@ use crate::phy::ChecksumCapabilities; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::socket::{Context, PollAt}; -use crate::Error; +use crate::storage::Empty; use crate::wire::IcmpRepr; #[cfg(feature = "proto-ipv4")] use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Repr}; @@ -451,66 +451,98 @@ impl<'a> Socket<'a> { self.rx_waker.wake(); } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, IcmpRepr)) -> Result<(), Error>, + F: FnOnce(&mut Context, (IpRepr, IcmpRepr)) -> Result<(), E>, { let hop_limit = self.hop_limit.unwrap_or(64); - self.tx_buffer - .dequeue_with(|remote_endpoint, packet_buf| { - net_trace!( - "icmp:{}: sending {} octets", - remote_endpoint, - packet_buf.len() - ); - match *remote_endpoint { - #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(dst_addr) => { - let src_addr = match cx.get_source_address_ipv4(dst_addr) { - Some(addr) => addr, - None => return Err(Error::Unaddressable), - }; - let packet = Icmpv4Packet::new_unchecked(&*packet_buf); - let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?; - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Icmp, - payload_len: repr.buffer_len(), - hop_limit: hop_limit, - }); - emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) - } - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(dst_addr) => { - let src_addr = match cx.get_source_address_ipv6(dst_addr) { - Some(addr) => addr, - None => return Err(Error::Unaddressable), - }; - let packet = Icmpv6Packet::new_unchecked(&*packet_buf); - let repr = Icmpv6Repr::parse( - &src_addr.into(), - &dst_addr.into(), - &packet, - &ChecksumCapabilities::ignored(), - )?; - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Icmpv6, - payload_len: repr.buffer_len(), - hop_limit: hop_limit, - }); - emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) - } + let res = self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| { + net_trace!( + "icmp:{}: sending {} octets", + remote_endpoint, + packet_buf.len() + ); + match *remote_endpoint { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(dst_addr) => { + let src_addr = match cx.get_source_address_ipv4(dst_addr) { + Some(addr) => addr, + None => { + net_trace!( + "icmp:{}: not find suitable source address, dropping", + remote_endpoint + ); + return Ok(()); + } + }; + let packet = Icmpv4Packet::new_unchecked(&*packet_buf); + let repr = match Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored()) { + Ok(x) => x, + Err(_) => { + net_trace!( + "icmp:{}: malformed packet in queue, dropping", + remote_endpoint + ); + return Ok(()); + } + }; + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Icmp, + payload_len: repr.buffer_len(), + hop_limit: hop_limit, + }); + emit(cx, (ip_repr, IcmpRepr::Ipv4(repr))) } - }) - .map_err(|_| Error::Exhausted)??; - - #[cfg(feature = "async")] - self.tx_waker.wake(); - - Ok(()) + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(dst_addr) => { + let src_addr = match cx.get_source_address_ipv6(dst_addr) { + Some(addr) => addr, + None => { + net_trace!( + "icmp:{}: not find suitable source address, dropping", + remote_endpoint + ); + return Ok(()); + } + }; + let packet = Icmpv6Packet::new_unchecked(&*packet_buf); + let repr = match Icmpv6Repr::parse( + &src_addr.into(), + &dst_addr.into(), + &packet, + &ChecksumCapabilities::ignored(), + ) { + Ok(x) => x, + Err(_) => { + net_trace!( + "icmp:{}: malformed packet in queue, dropping", + remote_endpoint + ); + return Ok(()); + } + }; + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Icmpv6, + payload_len: repr.buffer_len(), + hop_limit: hop_limit, + }); + emit(cx, (ip_repr, IcmpRepr::Ipv6(repr))) + } + } + }); + match res { + Err(Empty) => Ok(()), + Ok(Err(e)) => Err(e), + Ok(Ok(())) => { + #[cfg(feature = "async")] + self.tx_waker.wake(); + Ok(()) + } + } } pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { @@ -552,8 +584,8 @@ mod tests_common { #[cfg(all(test, feature = "proto-ipv4"))] mod test_ipv4 { use super::tests_common::*; - use crate::wire::{Icmpv4DstUnreachable, IpEndpoint, Ipv4Address}; + use crate::Error; const REMOTE_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 2]); const LOCAL_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 1]); @@ -602,7 +634,7 @@ mod test_ipv4 { assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Err(Error::Exhausted) + Ok::<_, ()>(()) ); // This buffer is too long @@ -641,7 +673,7 @@ mod test_ipv4 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); @@ -677,7 +709,7 @@ mod test_ipv4 { hop_limit: 0x2a, }) ); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); @@ -795,6 +827,7 @@ mod test_ipv6 { use super::tests_common::*; use crate::wire::{Icmpv6DstUnreachable, IpEndpoint, Ipv6Address}; + use crate::Error; const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); @@ -844,7 +877,7 @@ mod test_ipv6 { assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Err(Error::Exhausted) + Ok::<_, Error>(()) ); // This buffer is too long @@ -888,7 +921,7 @@ mod test_ipv6 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); @@ -929,7 +962,7 @@ mod test_ipv6 { hop_limit: 0x2a, }) ); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 021ef4af8..0ce357cf3 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -3,12 +3,11 @@ use core::cmp::min; use core::task::Waker; use crate::iface::Context; -use crate::phy::ChecksumCapabilities; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; -use crate::Error; +use crate::storage::Empty; use crate::wire::{IpProtocol, IpRepr, IpVersion}; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Packet, Ipv4Repr}; @@ -265,21 +264,27 @@ impl<'a> Socket<'a> { self.rx_waker.wake(); } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<(), Error>, + F: FnOnce(&mut Context, (IpRepr, &[u8])) -> Result<(), E>, { - fn prepare<'a>( - next_header: IpProtocol, - buffer: &'a mut [u8], - _checksum_caps: &ChecksumCapabilities, - ) -> Result<(IpRepr, &'a [u8]), Error> { - match IpVersion::of_packet(buffer)? { + let ip_protocol = self.ip_protocol; + let ip_version = self.ip_version; + let _checksum_caps = &cx.checksum_caps(); + let res = self.tx_buffer.dequeue_with(|&mut (), buffer| { + match IpVersion::of_packet(buffer) { #[cfg(feature = "proto-ipv4")] - IpVersion::Ipv4 => { - let mut packet = Ipv4Packet::new_checked(buffer)?; - if packet.next_header() != next_header { - return Err(Error::Unaddressable); + Ok(IpVersion::Ipv4) => { + let mut packet = match Ipv4Packet::new_checked(buffer) { + Ok(x) => x, + Err(_) => { + net_trace!("raw: malformed ipv6 packet in queue, dropping."); + return Ok(()); + } + }; + if packet.next_header() != ip_protocol { + net_trace!("raw: sent packet with wrong ip protocol, dropping."); + return Ok(()); } if _checksum_caps.ipv4.tx() { packet.fill_checksum(); @@ -289,55 +294,57 @@ impl<'a> Socket<'a> { packet.set_checksum(0); } - let packet = Ipv4Packet::new_checked(&*packet.into_inner())?; - let ipv4_repr = Ipv4Repr::parse(&packet, _checksum_caps)?; - Ok((IpRepr::Ipv4(ipv4_repr), packet.payload())) + let packet = Ipv4Packet::new_unchecked(&*packet.into_inner()); + let ipv4_repr = match Ipv4Repr::parse(&packet, _checksum_caps) { + Ok(x) => x, + Err(_) => { + net_trace!("raw: malformed ipv4 packet in queue, dropping."); + return Ok(()); + } + }; + net_trace!("raw:{}:{}: sending", ip_version, ip_protocol); + emit(cx, (IpRepr::Ipv4(ipv4_repr), packet.payload())) } #[cfg(feature = "proto-ipv6")] - IpVersion::Ipv6 => { - let packet = Ipv6Packet::new_checked(buffer)?; - if packet.next_header() != next_header { - return Err(Error::Unaddressable); + Ok(IpVersion::Ipv6) => { + let packet = match Ipv6Packet::new_checked(buffer) { + Ok(x) => x, + Err(_) => { + net_trace!("raw: malformed ipv6 packet in queue, dropping."); + return Ok(()); + } + }; + if packet.next_header() != ip_protocol { + net_trace!("raw: sent ipv6 packet with wrong ip protocol, dropping."); + return Ok(()); } let packet = Ipv6Packet::new_unchecked(&*packet.into_inner()); - let ipv6_repr = Ipv6Repr::parse(&packet)?; - Ok((IpRepr::Ipv6(ipv6_repr), packet.payload())) + let ipv6_repr = match Ipv6Repr::parse(&packet) { + Ok(x) => x, + Err(_) => { + net_trace!("raw: malformed ipv6 packet in queue, dropping."); + return Ok(()); + } + }; + + net_trace!("raw:{}:{}: sending", ip_version, ip_protocol); + emit(cx, (IpRepr::Ipv6(ipv6_repr), packet.payload())) + } + Err(_) => { + net_trace!("raw: sent packet with invalid IP version, dropping."); + Ok(()) } } + }); + match res { + Err(Empty) => Ok(()), + Ok(Err(e)) => Err(e), + Ok(Ok(())) => { + #[cfg(feature = "async")] + self.tx_waker.wake(); + Ok(()) + } } - - 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, &cx.checksum_caps()) { - Ok((ip_repr, raw_packet)) => { - net_trace!( - "raw:{}:{}: sending {} octets", - ip_version, - ip_protocol, - ip_repr.buffer_len() + raw_packet.len() - ); - emit(cx, (ip_repr, raw_packet)) - } - Err(error) => { - net_debug!( - "raw:{}:{}: dropping outgoing packet ({})", - ip_version, - ip_protocol, - error - ); - // Return Ok(()) so the packet is dequeued. - Ok(()) - } - } - }) - .map_err(|_| Error::Exhausted)??; - - #[cfg(feature = "async")] - self.tx_waker.wake(); - - Ok(()) } pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { @@ -357,6 +364,7 @@ mod test { use crate::wire::{Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Repr}; + use crate::Error; fn buffer(packets: usize) -> PacketBuffer<'static> { PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 48 * packets]) @@ -453,7 +461,7 @@ mod test { assert!(socket.can_send()); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Err(Error::Exhausted) + Ok::<_, Error>(()) ); assert_eq!(socket.send_slice(&$packet[..]), Ok(())); @@ -474,7 +482,7 @@ mod test { socket.dispatch(&mut cx, |_, (ip_repr, ip_payload)| { assert_eq!(ip_repr, $hdr); assert_eq!(ip_payload, &$payload); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); @@ -539,13 +547,19 @@ mod test { Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); + assert_eq!( + socket.dispatch(&mut cx, |_, _| unreachable!()), + Ok::<_, Error>(()) + ); let mut wrong_protocol = ipv4_locals::PACKET_BYTES; Ipv4Packet::new_unchecked(&mut wrong_protocol).set_next_header(IpProtocol::Tcp); assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); - assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); + assert_eq!( + socket.dispatch(&mut cx, |_, _| unreachable!()), + Ok::<_, Error>(()) + ); } #[cfg(feature = "proto-ipv6")] { @@ -556,13 +570,19 @@ mod test { Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); - assert_eq!(socket.dispatch(&mut cx, |_, _| unreachable!()), Ok(())); + assert_eq!( + socket.dispatch(&mut cx, |_, _| unreachable!()), + Ok::<_, Error>(()) + ); 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(&mut cx, |_, _| unreachable!()), Ok(())); + assert_eq!( + socket.dispatch(&mut cx, |_, _| unreachable!()), + Ok::<_, Error>(()) + ); } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 3c2e64417..fd498a14c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -16,7 +16,6 @@ use crate::wire::{ IpAddress, IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, TcpControl, TcpRepr, TcpSeqNumber, TCP_HEADER_LEN, }; -use crate::Error; macro_rules! tcp_trace { ($($arg:expr),*) => (net_log!(trace, $($arg),*)); @@ -1934,12 +1933,12 @@ impl<'a> Socket<'a> { } } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<(), Error>, + F: FnOnce(&mut Context, (IpRepr, TcpRepr)) -> Result<(), E>, { if self.tuple.is_none() { - return Err(Error::Exhausted); + return Ok(()); } if self.remote_last_ts.is_none() { @@ -1999,9 +1998,9 @@ impl<'a> Socket<'a> { // If we have spent enough time in the TIME-WAIT state, close the socket. tcp_trace!("TIME-WAIT timer expired"); self.reset(); - return Err(Error::Exhausted); + return Ok(()); } else { - return Err(Error::Exhausted); + return Ok(()); } // NOTE(unwrap): we check tuple is not None the first thing in this function. @@ -2041,7 +2040,7 @@ impl<'a> Socket<'a> { } // We never transmit anything in the LISTEN state. - State::Listen => return Err(Error::Exhausted), + State::Listen => return Ok(()), // We transmit a SYN in the SYN-SENT state. // We transmit a SYN|ACK in the SYN-RECEIVED state. @@ -2270,6 +2269,7 @@ impl<'a> fmt::Write for Socket<'a> { mod test { use super::*; use crate::wire::IpRepr; + use crate::Error; use core::i32; use std::ops::{Deref, DerefMut}; use std::vec::Vec; @@ -6168,7 +6168,7 @@ mod test { assert_eq!( s.socket.dispatch(&mut s.cx, |_, (ip_repr, _)| { assert_eq!(ip_repr.hop_limit(), 0x2a); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index e100e0c7c..135585322 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -6,8 +6,8 @@ use crate::iface::Context; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; +use crate::storage::Empty; use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; -use crate::Error; /// A UDP packet metadata. pub type PacketMetadata = crate::storage::PacketMetadata; @@ -383,49 +383,58 @@ impl<'a> Socket<'a> { self.rx_waker.wake(); } - pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), Error> + pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), Error>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); - self.tx_buffer - .dequeue_with(|remote_endpoint, payload_buf| { - let src_addr = match endpoint.addr { + let res = self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| { + let src_addr = match endpoint.addr { + Some(addr) => addr, + None => match cx.get_source_address(remote_endpoint.addr) { Some(addr) => addr, - None => match cx.get_source_address(remote_endpoint.addr) { - Some(addr) => addr, - None => return Err(Error::Unaddressable), - }, - }; - - net_trace!( - "udp:{}:{}: sending {} octets", - endpoint, - remote_endpoint, - payload_buf.len() - ); - - let repr = UdpRepr { - src_port: endpoint.port, - dst_port: remote_endpoint.port, - }; - let ip_repr = IpRepr::new( - src_addr, - remote_endpoint.addr, - IpProtocol::Udp, - repr.header_len() + payload_buf.len(), - hop_limit, - ); - emit(cx, (ip_repr, repr, payload_buf)) - }) - .map_err(|_| Error::Exhausted)??; - - #[cfg(feature = "async")] - self.tx_waker.wake(); - - Ok(()) + None => { + net_trace!( + "udp:{}:{}: cannot find suitable source address, dropping.", + endpoint, + remote_endpoint + ); + return Ok(()); + } + }, + }; + + net_trace!( + "udp:{}:{}: sending {} octets", + endpoint, + remote_endpoint, + payload_buf.len() + ); + + let repr = UdpRepr { + src_port: endpoint.port, + dst_port: remote_endpoint.port, + }; + let ip_repr = IpRepr::new( + src_addr, + remote_endpoint.addr, + IpProtocol::Udp, + repr.header_len() + payload_buf.len(), + hop_limit, + ); + emit(cx, (ip_repr, repr, payload_buf)) + }); + match res { + Err(Empty) => Ok(()), + Ok(Err(e)) => Err(e), + Ok(Ok(())) => { + #[cfg(feature = "async")] + self.tx_waker.wake(); + Ok(()) + } + } } pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { @@ -441,6 +450,7 @@ impl<'a> Socket<'a> { mod test { use super::*; use crate::wire::{IpRepr, UdpRepr}; + use crate::Error; fn buffer(packets: usize) -> PacketBuffer<'static> { PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets]) @@ -589,7 +599,7 @@ mod test { assert!(socket.can_send()); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Err(Error::Exhausted) + Ok::<_, Error>(()) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(())); @@ -615,7 +625,7 @@ mod test { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); @@ -711,7 +721,7 @@ mod test { hop_limit: 0x2a, }) ); - Ok(()) + Ok::<_, Error>(()) }), Ok(()) ); From 2841d5ef4a55202a52de8d1c1af0917828088880 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 May 2022 23:58:49 +0200 Subject: [PATCH 338/566] iface: make socket_egress infallible. --- src/iface/interface.rs | 52 +++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 8c07cd80d..6de7b4380 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -773,7 +773,7 @@ where let mut readiness_may_have_changed = false; loop { let processed_any = self.socket_ingress(); - let emitted_any = self.socket_egress()?; + let emitted_any = self.socket_egress(); #[cfg(feature = "proto-igmp")] self.igmp_egress()?; @@ -880,7 +880,7 @@ where processed_any } - fn socket_egress(&mut self) -> Result { + fn socket_egress(&mut self) -> bool { let Self { device, inner, @@ -899,58 +899,53 @@ where } let mut neighbor_addr = None; - let mut device_result = Ok(()); - - macro_rules! respond { - ($inner:expr, $response:expr) => {{ - 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, response); - device_result - }}; - } + let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { + neighbor_addr = Some(response.ip_repr().dst_addr()); + let tx_token = device.transmit().ok_or(Error::Exhausted)?; + inner.dispatch_ip(tx_token, response)?; + emitted_any = true; + Ok(()) + }; - let socket_result = match &mut item.socket { + let result = match &mut item.socket { #[cfg(feature = "socket-raw")] Socket::Raw(socket) => socket.dispatch(inner, |inner, response| { - respond!(inner, IpPacket::Raw(response)) + respond(inner, IpPacket::Raw(response)) }), #[cfg(feature = "socket-icmp")] Socket::Icmp(socket) => socket.dispatch(inner, |inner, response| match response { #[cfg(feature = "proto-ipv4")] (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { - respond!(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) + respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) } #[cfg(feature = "proto-ipv6")] (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { - respond!(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) + respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) } #[allow(unreachable_patterns)] _ => unreachable!(), }), #[cfg(feature = "socket-udp")] Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { - respond!(inner, IpPacket::Udp(response)) + respond(inner, IpPacket::Udp(response)) }), #[cfg(feature = "socket-tcp")] Socket::Tcp(socket) => socket.dispatch(inner, |inner, response| { - respond!(inner, IpPacket::Tcp(response)) + respond(inner, IpPacket::Tcp(response)) }), #[cfg(feature = "socket-dhcpv4")] Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { - respond!(inner, IpPacket::Dhcpv4(response)) + respond(inner, IpPacket::Dhcpv4(response)) }), #[cfg(feature = "socket-dns")] Socket::Dns(ref mut socket) => socket.dispatch(inner, |inner, response| { - respond!(inner, IpPacket::Udp(response)) + respond(inner, IpPacket::Udp(response)) }), }; - match (device_result, socket_result) { - (Err(Error::Exhausted), _) => break, // nowhere to transmit - (Ok(()), Err(Error::Exhausted)) => (), // nothing to transmit - (Err(Error::Unaddressable), _) => { + match result { + Err(Error::Exhausted) => break, // Device buffer full. + Err(Error::Unaddressable) => { // `NeighborCache` already takes care of rate limiting the neighbor discovery // requests from the socket. However, without an additional rate limiting // mechanism, we would spin on every socket that has yet to discover its @@ -961,18 +956,17 @@ where ); break; } - (Err(err), _) | (_, Err(err)) => { + Err(err) => { net_debug!( "{}: cannot dispatch egress packet: {}", item.meta.handle, err ); - return Err(err); } - (Ok(()), Ok(())) => emitted_any = true, + Ok(()) => {} } } - Ok(emitted_any) + emitted_any } /// Depending on `igmp_report_state` and the therein contained From d1107f4eadf3af82c5976df2cfb1ce9f3f66341b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 22 Feb 2022 12:04:13 +0100 Subject: [PATCH 339/566] sixlowpan: add fragmentation wire representation --- src/iface/interface.rs | 19 +- src/wire/mod.rs | 9 +- src/wire/sixlowpan.rs | 1363 +++++++++++++++++++++++++++------------- 3 files changed, 939 insertions(+), 452 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 6de7b4380..ce981f416 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1359,24 +1359,24 @@ impl<'a> InterfaceInner<'a> { match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { match check!(SixlowpanNhcPacket::dispatch(payload)) { - SixlowpanNhcPacket::ExtensionHeader(_) => { + SixlowpanNhcPacket::ExtHeader => { net_debug!("Extension headers are currently not supported for 6LoWPAN"); None } #[cfg(not(feature = "socket-udp"))] - SixlowpanNhcPacket::UdpHeader(_) => { + SixlowpanNhcPacket::UdpHeader => { net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); None } #[cfg(feature = "socket-udp")] - SixlowpanNhcPacket::UdpHeader(udp_packet) => { + SixlowpanNhcPacket::UdpHeader => { ipv6_repr.next_header = IpProtocol::Udp; // Handle the UDP - let udp_repr = check!(SixlowpanUdpRepr::parse( + let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload)); + let udp_repr = check!(SixlowpanUdpNhcRepr::parse( &udp_packet, &iphc_repr.src_addr, &iphc_repr.dst_addr, - udp_packet.checksum(), )); // Look for UDP sockets that will accept the UDP packet. @@ -2550,6 +2550,9 @@ impl<'a> InterfaceInner<'a> { ll_dst_addr: Some(dst_hardware_addr), next_header, hop_limit, + ecn: None, + dscp: None, + flow_label: None, }; tx_len += ieee_repr.buffer_len(); @@ -2559,7 +2562,7 @@ impl<'a> InterfaceInner<'a> { match &packet { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udp_repr, payload)) => { - let udp_repr = SixlowpanUdpRepr(*udp_repr); + let udp_repr = SixlowpanUdpNhcRepr(*udp_repr); tx_len += udp_repr.header_len() + payload.len(); } IpPacket::Icmpv6((_, icmp)) => { @@ -2587,9 +2590,9 @@ impl<'a> InterfaceInner<'a> { IpPacket::Udp((_, udp_repr, payload)) => { // 3. Create the header for 6LoWPAN UDP let mut udp_packet = - SixlowpanUdpPacket::new_unchecked(&mut tx_buffer[start..tx_len]); + SixlowpanUdpNhcPacket::new_unchecked(&mut tx_buffer[start..tx_len]); - SixlowpanUdpRepr(udp_repr).emit( + SixlowpanUdpNhcRepr(udp_repr).emit( &mut udp_packet, &iphc_repr.src_addr, &iphc_repr.dst_addr, diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 0a87862ac..ab3b9b167 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -144,13 +144,14 @@ pub use self::arp::{ #[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] pub use self::sixlowpan::{ + frag::{Key as SixlowpanFragKey, Packet as SixlowpanFragPacket, Repr as SixlowpanFragRepr}, iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr}, nhc::{ - ExtensionHeaderPacket as SixlowpanExtHeaderPacket, - ExtensionHeaderRepr as SixlowpanExtHeaderRepr, Packet as SixlowpanNhcPacket, - UdpNhcRepr as SixlowpanUdpRepr, UdpPacket as SixlowpanUdpPacket, + ExtHeaderPacket as SixlowpanExtHeaderPacket, ExtHeaderRepr as SixlowpanExtHeaderRepr, + NhcPacket as SixlowpanNhcPacket, UdpNhcPacket as SixlowpanUdpNhcPacket, + UdpNhcRepr as SixlowpanUdpNhcRepr, }, - NextHeader as SixlowpanNextHeader, + NextHeader as SixlowpanNextHeader, SixlowpanPacket, }; #[cfg(feature = "medium-ieee802154")] diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 13ca2fbb7..d52194726 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -7,75 +7,431 @@ use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; +/// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with +/// and without context information. The decompression with context information is not yet +/// implemented. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum NextHeader { - Compressed, - Uncompressed(IpProtocol), +pub enum UnresolvedAddress<'a> { + WithoutContext(AddressMode<'a>), + WithContext(AddressMode<'a>), + Reserved, } -/// A wrapper around the address provided in the 6LoWPAN_IPHC header. -/// This requires some context to convert it the an IPv6 address in some cases. -/// For 802.15.4 the context are the short/extended addresses. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Address<'a> { - Complete(ipv6::Address), - WithContext(&'a [u8]), - Elided, - Reserved, +pub enum AddressMode<'a> { + /// The full address is carried in-line. + FullInline(&'a [u8]), + /// The first 64-bits of the address are elided. The value of those bits + /// is the link-local prefix padded with zeros. The remaining 64 bits are + /// carried in-line. + InLine64bits(&'a [u8]), + /// The first 112 bits of the address are elided. The value of the first + /// 64 bits is the link-local prefix padded with zeros. The following 64 bits + /// are 0000:00ff:fe00:XXXX, where XXXX are the 16 bits carried in-line. + InLine16bits(&'a [u8]), + /// The address is fully elided. The first 64 bits of the address are + /// the link-local prefix padded with zeros. The remaining 64 bits are + /// computed from the encapsulating header (e.g., 802.15.4 or IPv6 source address) + /// as specified in Section 3.2.2. + FullyElided, + /// The address takes the form ffXX::00XX:XXXX:XXXX + Multicast48bits(&'a [u8]), + /// The address takes the form ffXX::00XX:XXXX. + Multicast32bits(&'a [u8]), + /// The address takes the form ff02::00XX. + Multicast8bits(&'a [u8]), + /// The unspecified address. + Unspecified, + NotSupported, } -impl<'a> Address<'a> { - /// Resolve the address provided by the IPHC encoding. - pub(crate) fn resolve(self, ll_addr: Option) -> Result { +const LINK_LOCAL_PREFIX: [u8; 2] = [0xfe, 0x80]; +const EUI64_MIDDLE_VALUE: [u8; 2] = [0xff, 0xfe]; + +impl<'a> UnresolvedAddress<'a> { + pub fn resolve(self, ll_address: Option) -> Result { + let mut bytes = [0; 16]; match self { - Address::Complete(addr) => Ok(addr), - Address::Elided => { - let mut bytes = [0; 16]; - bytes[0] = 0xfe; - bytes[1] = 0x80; - - match ll_addr { - Some(LlAddress::Short(ll)) => { - bytes[11] = 0xff; - bytes[12] = 0xfe; - bytes[14..].copy_from_slice(&ll); - } - Some(LlAddress::Extended(ll)) => { - bytes[8..].copy_from_slice(&LlAddress::Extended(ll).as_eui_64().unwrap()); + UnresolvedAddress::WithoutContext(mode) => match mode { + AddressMode::FullInline(addr) => Ok(ipv6::Address::from_bytes(addr)), + AddressMode::InLine64bits(inline) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[8..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + AddressMode::InLine16bits(inline) => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + AddressMode::FullyElided => { + bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); + match ll_address.unwrap() { + LlAddress::Short(ll) => { + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(&ll); + } + LlAddress::Extended(_) => { + bytes[8..].copy_from_slice(&ll_address.unwrap().as_eui_64().unwrap()); + } + LlAddress::Absent => return Err(Error), } - _ => return Err(Error), + Ok(ipv6::Address::from_bytes(&bytes[..])) } + AddressMode::Multicast48bits(inline) => { + bytes[0] = 0xff; + bytes[1] = inline[0]; + bytes[11..].copy_from_slice(&inline[1..][..5]); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + AddressMode::Multicast32bits(inline) => { + bytes[0] = 0xff; + bytes[1] = inline[0]; + bytes[13..].copy_from_slice(&inline[1..][..3]); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + AddressMode::Multicast8bits(inline) => { + bytes[0] = 0xff; + bytes[1] = 0x02; + bytes[15] = inline[0]; + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + _ => Err(Error), + }, + UnresolvedAddress::WithContext(mode) => match mode { + AddressMode::Unspecified => Ok(ipv6::Address::UNSPECIFIED), + AddressMode::NotSupported => Err(Error), + _ => Err(Error), + }, + UnresolvedAddress::Reserved => Err(Error), + } + } +} - Ok(ipv6::Address::from_bytes(&bytes)) - } - Address::WithContext(_) => Err(Error), - Address::Reserved => Err(Error), +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SixlowpanPacket { + FragmentHeader, + IphcHeader, +} + +const DISPATCH_FIRST_FRAGMENT_HEADER: u8 = 0b11000; +const DISPATCH_FRAGMENT_HEADER: u8 = 0b11100; +const DISPATCH_IPHC_HEADER: u8 = 0b011; +const DISPATCH_UDP_HEADER: u8 = 0b11110; +const DISPATCH_EXT_HEADER: u8 = 0b1110; + +impl SixlowpanPacket { + /// Returns the type of the 6LoWPAN header. + /// This can either be a fragment header or an IPHC header. + /// + /// # Errors + /// Returns `[Error::Unrecognized]` when neither the Fragment Header dispatch or the IPHC + /// dispatch is recognized. + pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result { + let raw = buffer.as_ref(); + + if raw[0] >> 3 == DISPATCH_FIRST_FRAGMENT_HEADER || raw[0] >> 3 == DISPATCH_FRAGMENT_HEADER + { + Ok(Self::FragmentHeader) + } else if raw[0] >> 5 == DISPATCH_IPHC_HEADER { + Ok(Self::IphcHeader) + } else { + Err(Error) } } } -pub mod iphc { - use super::{Error, Result}; - use crate::wire::ieee802154::Address as LlAddress; - use crate::wire::ipv6; - use crate::wire::IpProtocol; +pub mod frag { + //! Implementation of the fragment headers from [RFC 4944 § 5.3]. + //! + //! [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + + use super::{DISPATCH_FIRST_FRAGMENT_HEADER, DISPATCH_FRAGMENT_HEADER}; + use crate::{ + wire::{Ieee802154Address, Ieee802154Repr}, + Error, Result, + }; use byteorder::{ByteOrder, NetworkEndian}; - use super::Address; - use super::NextHeader; + /// Key used for identifying all the link fragments that belong to the same packet. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct Key { + ll_src_addr: Ieee802154Address, + ll_dst_addr: Ieee802154Address, + datagram_size: u16, + datagram_tag: u16, + } + + /// A read/write wrapper around a 6LoWPAN Fragment header. + /// [RFC 4944 § 5.3] specifies the format of the header. + /// + /// A First Fragment header has the following format: + /// ```txt + /// 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// |1 1 0 0 0| datagram_size | datagram_tag | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + /// + /// Subsequent fragment headers have the following format: + /// ```txt + /// 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// |1 1 1 0 0| datagram_size | datagram_tag | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// |datagram_offset| + /// +-+-+-+-+-+-+-+-+ + /// ``` + /// + /// [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + #[derive(Debug, Clone)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct Packet> { + buffer: T, + } + + pub const FIRST_FRAGMENT_HEADER_SIZE: usize = 4; + pub const NEXT_FRAGMENT_HEADER_SIZE: usize = 5; mod field { - #![allow(non_snake_case)] + use crate::wire::field::*; + + pub const DISPATCH: usize = 0; + pub const DATAGRAM_SIZE: Field = 0..2; + pub const DATAGRAM_TAG: Field = 2..4; + pub const DATAGRAM_OFFSET: usize = 4; + + pub const FIRST_FRAGMENT_REST: Rest = super::FIRST_FRAGMENT_HEADER_SIZE..; + pub const NEXT_FRAGMENT_REST: Rest = super::NEXT_FRAGMENT_HEADER_SIZE..; + } + + impl> Packet { + /// Input a raw octet buffer with a 6LoWPAN Fragment header structure. + pub fn new_unchecked(buffer: T) -> Self { + Self { buffer } + } + + /// Shorthand for a combination of [new_unchecked] and [check_len]. + /// + /// [new_unchecked]: #method.new_unchecked + /// [check_len]: #method.check_len + pub fn new_checked(buffer: T) -> Result { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + + let dispatch = packet.dispatch(); + + if dispatch != DISPATCH_FIRST_FRAGMENT_HEADER && dispatch != DISPATCH_FRAGMENT_HEADER { + return Err(Error::Malformed); + } + + Ok(packet) + } + + /// Ensure that no accessor method will panic if called. + /// Returns `Err(Error::Truncated)` if the buffer is too short. + pub fn check_len(&self) -> Result<()> { + let buffer = self.buffer.as_ref(); + if buffer.is_empty() { + return Err(Error::Truncated); + } + + match self.dispatch() { + DISPATCH_FIRST_FRAGMENT_HEADER if buffer.len() >= FIRST_FRAGMENT_HEADER_SIZE => { + Ok(()) + } + DISPATCH_FIRST_FRAGMENT_HEADER if buffer.len() < FIRST_FRAGMENT_HEADER_SIZE => { + Err(Error::Truncated) + } + DISPATCH_FRAGMENT_HEADER if buffer.len() >= NEXT_FRAGMENT_HEADER_SIZE => Ok(()), + DISPATCH_FRAGMENT_HEADER if buffer.len() < NEXT_FRAGMENT_HEADER_SIZE => { + Err(Error::Truncated) + } + _ => Err(Error::Unrecognized), + } + } + + /// Consumes the frame, returning the underlying buffer. + pub fn into_inner(self) -> T { + self.buffer + } + + /// Return the dispatch field. + pub fn dispatch(&self) -> u8 { + let raw = self.buffer.as_ref(); + raw[field::DISPATCH] >> 3 + } + + /// Return the total datagram size. + pub fn datagram_size(&self) -> u16 { + let raw = self.buffer.as_ref(); + NetworkEndian::read_u16(&raw[field::DATAGRAM_SIZE]) & 0b111_1111_1111 + } + + /// Return the datagram tag. + pub fn datagram_tag(&self) -> u16 { + let raw = self.buffer.as_ref(); + NetworkEndian::read_u16(&raw[field::DATAGRAM_TAG]) + } + + /// Return the datagram offset. + pub fn datagram_offset(&self) -> u8 { + match self.dispatch() { + DISPATCH_FIRST_FRAGMENT_HEADER => 0, + DISPATCH_FRAGMENT_HEADER => { + let raw = self.buffer.as_ref(); + raw[field::DATAGRAM_OFFSET] + } + _ => unreachable!(), + } + } + + /// Returns `true` when this header is from the first fragment of a link. + pub fn is_first_fragment(&self) -> bool { + self.dispatch() == DISPATCH_FIRST_FRAGMENT_HEADER + } + + /// Returns the key for identifying the packet it belongs to. + pub fn get_key(&self, ieee802154_repr: &Ieee802154Repr) -> Key { + Key { + ll_src_addr: ieee802154_repr.src_addr.unwrap(), + ll_dst_addr: ieee802154_repr.dst_addr.unwrap(), + datagram_size: self.datagram_size(), + datagram_tag: self.datagram_tag(), + } + } + } + + impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { + /// Return the payload. + pub fn payload(&self) -> &'a [u8] { + match self.dispatch() { + DISPATCH_FIRST_FRAGMENT_HEADER => { + let raw = self.buffer.as_ref(); + &raw[field::FIRST_FRAGMENT_REST] + } + DISPATCH_FRAGMENT_HEADER => { + let raw = self.buffer.as_ref(); + &raw[field::NEXT_FRAGMENT_REST] + } + _ => unreachable!(), + } + } + } + + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Packet { + fn set_dispatch_field(&mut self, value: u8) { + let raw = self.buffer.as_mut(); + raw[field::DISPATCH] = (raw[field::DISPATCH] & !(0b11111 << 3)) | (value << 3); + } + + fn set_datagram_size(&mut self, size: u16) { + let raw = self.buffer.as_mut(); + let mut v = NetworkEndian::read_u16(&raw[field::DATAGRAM_SIZE]); + v = (v & !0b111_1111_1111) | size; + + NetworkEndian::write_u16(&mut raw[field::DATAGRAM_SIZE], v); + } + + fn set_datagram_tag(&mut self, tag: u16) { + let raw = self.buffer.as_mut(); + NetworkEndian::write_u16(&mut raw[field::DATAGRAM_TAG], tag); + } + + fn set_datagram_offset(&mut self, offset: u8) { + let raw = self.buffer.as_mut(); + raw[field::DATAGRAM_OFFSET] = offset; + } + } + + /// A high-level representation of a 6LoWPAN Fragment header. + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Repr { + FirstFragment { size: u16, tag: u16 }, + Fragment { size: u16, tag: u16, offset: u8 }, + } + + impl Repr { + pub(crate) fn set_offset(&mut self, value: u8) { + match self { + Repr::FirstFragment { .. } => (), + Repr::Fragment { offset, .. } => *offset = value, + } + } + + /// Parse a 6LoWPAN Fragment header. + pub fn parse>(packet: &Packet) -> Result { + let size = packet.datagram_size(); + let tag = packet.datagram_tag(); + + match packet.dispatch() { + DISPATCH_FIRST_FRAGMENT_HEADER => Ok(Self::FirstFragment { size, tag }), + DISPATCH_FRAGMENT_HEADER => Ok(Self::Fragment { + size, + tag, + offset: packet.datagram_offset(), + }), + _ => Err(Error::Malformed), + } + } + + /// Returns the length of the Fragment header. + pub fn buffer_len(&self) -> usize { + match self { + Self::FirstFragment { .. } => field::FIRST_FRAGMENT_REST.start, + Self::Fragment { .. } => field::NEXT_FRAGMENT_REST.start, + } + } + + /// Emit a high-level representation into a 6LoWPAN Fragment header. + pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { + match self { + Self::FirstFragment { size, tag } => { + packet.set_dispatch_field(DISPATCH_FIRST_FRAGMENT_HEADER); + packet.set_datagram_size(*size); + packet.set_datagram_tag(*tag); + } + Self::Fragment { size, tag, offset } => { + packet.set_dispatch_field(DISPATCH_FRAGMENT_HEADER); + packet.set_datagram_size(*size); + packet.set_datagram_tag(*tag); + packet.set_datagram_offset(*offset); + } + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum NextHeader { + Compressed, + Uncompressed(IpProtocol), +} + +pub mod iphc { + //! Implementation of IP Header Compression from [RFC 6282 § 3.1]. + //! It defines the compression of IPv6 headers. + //! + //! [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1 + + use super::{AddressMode, Error, NextHeader, Result, UnresolvedAddress, DISPATCH_IPHC_HEADER}; + use crate::wire::{ieee802154::Address as LlAddress, ipv6, IpProtocol}; + use byteorder::{ByteOrder, NetworkEndian}; + mod field { use crate::wire::field::*; pub const IPHC_FIELD: Field = 0..2; } - const DISPATCH: u8 = 0b011; - macro_rules! get_field { ($name:ident, $mask:expr, $shift:expr) => { fn $name(&self) -> u8 { @@ -98,7 +454,37 @@ pub mod iphc { }; } - /// A read/write wrapper around a LOWPAN_IPHC frame buffer. + /// A read/write wrapper around a 6LoWPAN IPHC header. + /// [RFC 6282 § 3.1] specifies the format of the header. + /// + /// The header always start with the following base format (from [RFC 6282 § 3.1.1]): + /// ```txt + /// 0 1 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + /// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + /// | 0 | 1 | 1 | TF |NH | HLIM |CID|SAC| SAM | M |DAC| DAM | + /// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + /// ``` + /// With: + /// - TF: Traffic Class and Flow Label + /// - NH: Next Header + /// - HLIM: Hop Limit + /// - CID: Context Identifier Extension + /// - SAC: Source Address Compression + /// - SAM: Source Address Mode + /// - M: Multicast Compression + /// - DAC: Destination Address Compression + /// - DAM: Destination Address Mode + /// + /// Depending on the flags in the base format, the following fields are added to the header: + /// - Traffic Class and Flow Label + /// - Next Header + /// - Hop Limit + /// - IPv6 source address + /// - IPv6 destinatino address + /// + /// [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1 + /// [RFC 6282 § 3.1.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1.1 #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { @@ -106,8 +492,8 @@ pub mod iphc { } impl> Packet { - /// Input a raw octet buffer with a 6LoWPAN_IPHC frame structure. - pub fn new_unchecked(buffer: T) -> Packet { + /// Input a raw octet buffer with a 6LoWPAN IPHC header structure. + pub fn new_unchecked(buffer: T) -> Self { Packet { buffer } } @@ -115,7 +501,7 @@ pub mod iphc { /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len - pub fn new_checked(buffer: T) -> Result> { + pub fn new_checked(buffer: T) -> Result { let packet = Self::new_unchecked(buffer); packet.check_len()?; Ok(packet) @@ -148,7 +534,7 @@ pub mod iphc { self.buffer } - /// Return the Next Header field of this IPHC packet. + /// Return the Next Header field. pub fn next_header(&self) -> NextHeader { let nh = self.nh_field(); @@ -166,7 +552,7 @@ pub mod iphc { } } - /// Return the Hop Limit of this IPHC packet. + /// Return the Hop Limit. pub fn hop_limit(&self) -> u8 { match self.hlim_field() { 0b00 => { @@ -184,7 +570,7 @@ pub mod iphc { } } - /// Return the Source Context Identifier of this IPHC packet. + /// Return the Source Context Identifier. pub fn src_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); @@ -194,7 +580,7 @@ pub mod iphc { } } - /// Return the Destination Context Identifier of this IPHC packet. + /// Return the Destination Context Identifier. pub fn dst_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); @@ -204,8 +590,50 @@ pub mod iphc { } } - /// Return the Source Address of this IPHC packet. - pub fn src_addr(&self) -> Result
{ + /// Return the ECN field (when it is inlined). + pub fn ecn_field(&self) -> Option { + match self.tf_field() { + 0b00 | 0b01 | 0b10 => { + let start = self.ip_fields_start() as usize; + Some(self.buffer.as_ref()[start..][0] & 0b1100_0000) + } + 0b11 => None, + _ => unreachable!(), + } + } + + /// Return the DSCP field (when it is inlined). + pub fn dscp_field(&self) -> Option { + match self.tf_field() { + 0b00 | 0b10 => { + let start = self.ip_fields_start() as usize; + Some(self.buffer.as_ref()[start..][0] & 0b1111_11) + } + 0b01 | 0b11 => None, + _ => unreachable!(), + } + } + + /// Return the flow label field (when it is inlined). + pub fn flow_label_field(&self) -> Option { + match self.tf_field() { + 0b00 => { + let start = self.ip_fields_start() as usize; + let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]); + Some(raw & 0xfffff) + } + 0b01 => { + let start = self.ip_fields_start() as usize; + let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]) >> 8; + Some(raw & 0xfffff) + } + 0b10 | 0b11 => None, + _ => unreachable!(), + } + } + + /// Return the Source Address. + pub fn src_addr(&self) -> Result { let start = (self.ip_fields_start() + self.traffic_class_size() + self.next_header_size() @@ -213,87 +641,34 @@ pub mod iphc { match (self.sac_field(), self.sam_field()) { (0, 0b00) => { - // The full address is carried in-line. let data = self.buffer.as_ref(); - Ok(Address::Complete(ipv6::Address::from_bytes( - &data[start..start + 16], + Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], ))) } (0, 0b01) => { - // The first 64-bits of the address is elided. - // The value of those bits is the link-local prefix padded with zeros. - // The remaining 64-bits are carried in-line. let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - // Link-local prefix - bytes[0] = 0xfe; - bytes[1] = 0x80; - - bytes[8..].copy_from_slice(&data[start..start + 8]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )) } (0, 0b10) => { - // The first 112 bits of the address are elided. - // The value of the 64 bits is the link-local prefix padded with zeros. - // The following 64 bits are 0000:00ff:fe00:XXXX, - // where XXXX are the bits carried in-line. let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - // Link-local prefix - bytes[0] = 0xfe; - bytes[1] = 0x80; - - bytes[11] = 0xff; - bytes[12] = 0xfe; - - bytes[14..].copy_from_slice(&data[start..start + 2]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) - } - (0, 0b11) => { - // The address is fully elided. - // The first 64 bits of the address are the link-local prefix padded with zeros. - // The remaining 64 bits are computed from the encapsulating header. - Ok(Address::Elided) - } - (1, 0b00) => Ok(Address::Complete(ipv6::Address::UNSPECIFIED)), - (1, 0b01) => { - // The address is derived using context information and the 64 bits carried in-line. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. - let data = self.buffer.as_ref(); - let bytes = &data[start..start + 8]; - - Ok(Address::WithContext(bytes)) - } - (1, 0b10) => { - // The address is derived using context information and the 16 bits carried in-line. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. - let data = self.buffer.as_ref(); - let bytes = &data[start..start + 2]; - - Ok(Address::WithContext(bytes)) - } - (1, 0b11) => { - // The address is fully elided and is derived using context information and the encapsulating header. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. - Ok(Address::WithContext(&[])) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )) } + (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), + (1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::Unspecified)), + (1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), _ => Err(Error), } } - /// Return the Destination Address of this IPHC packet. - pub fn dst_addr(&self) -> Result
{ + /// Return the Destination Address. + pub fn dst_addr(&self) -> Result { let start = (self.ip_fields_start() + self.traffic_class_size() + self.next_header_size() @@ -302,133 +677,54 @@ pub mod iphc { match (self.m_field(), self.dac_field(), self.dam_field()) { (0, 0, 0b00) => { - // The full address is carried in-line. let data = self.buffer.as_ref(); - Ok(Address::Complete(ipv6::Address::from_bytes( - &data[start..start + 16], + Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], ))) } (0, 0, 0b01) => { - // The first 64-bits of the address is elided. - // The value of those bits is the link-local prefix padded with zeros. - // The remaining 64-bits are carried in-line. let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - // Link-local prefix - bytes[0] = 0xfe; - bytes[1] = 0x80; - - bytes[8..].copy_from_slice(&data[start..start + 8]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )) } (0, 0, 0b10) => { - // The first 112 bits of the address are elided. - // The value of the 64 bits is the link-local prefix padded with zeros. - // The following 64 bits are 0000:00ff:fe00:XXXX, - // where XXXX are the bits carried in-line. - let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - // Link-local prefix - bytes[0] = 0xfe; - bytes[1] = 0x80; - - bytes[11] = 0xff; - bytes[12] = 0xfe; - - bytes[14..].copy_from_slice(&data[start..start + 2]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) - } - (0, 0, 0b11) => { - // The address is fully elided. - // The first 64 bits of the address are the link-local prefix padded with zeros. - // The remaining 64 bits are computed from the encapsulating header. - Ok(Address::Elided) - } - (0, 1, 0b00) => Ok(Address::Reserved), - (0, 1, 0b01) => { - // The address is derived using context information and the 64 bits carried in-line. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. - let data = self.buffer.as_ref(); - let bytes = &data[start..start + 8]; - - Ok(Address::WithContext(bytes)) - } - (0, 1, 0b10) => { - // The address is derived using context information and the 16 bits carried in-line. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. let data = self.buffer.as_ref(); - let bytes = &data[start..start + 2]; - Ok(Address::WithContext(bytes)) - } - (0, 1, 0b11) => { - // The address is fully elided and is derived using context information and the encapsulating header. - // Bits covered by context information are always used. - // Any IID bits not covered by context information are always used. - // Any IID bits not covered by context information are directly from the corresponding bits carried in-line. - // Any remaining bits are zero. - Ok(Address::WithContext(&[])) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )) } + (0, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), + (0, 1, 0b00) => Ok(UnresolvedAddress::Reserved), + (0, 1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (0, 1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (0, 1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), (1, 0, 0b00) => { - // The full address is carried in-line. let data = self.buffer.as_ref(); - Ok(Address::Complete(ipv6::Address::from_bytes( - &data[start..start + 16], + Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], ))) } (1, 0, 0b01) => { - // The address takes the form ffXX::00XX:XXXX:XXXX let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - bytes[0] = 0xff; - bytes[1] = data[start]; - - bytes[11..].copy_from_slice(&data[start + 1..start + 6]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast48bits(&data[start..][..6]), + )) } (1, 0, 0b10) => { - // The address takes the form ffXX::00XX:XXXX let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - bytes[0] = 0xff; - bytes[1] = data[start]; - - bytes[13..].copy_from_slice(&data[start + 1..start + 4]); - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast32bits(&data[start..][..4]), + )) } (1, 0, 0b11) => { - // The address takes the form ff02::00XX let data = self.buffer.as_ref(); - let mut bytes = [0u8; 16]; - - bytes[0] = 0xff; - bytes[1] = 0x02; - - bytes[15] = data[start]; - - Ok(Address::Complete(ipv6::Address::from_bytes(&bytes))) - } - (1, 1, 0b00) => { - // This format is designed to match Unicast-Prefix-based IPv6 Multicast Addresses. - // The multicast takes the form ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX. - // X are octets that are carried in-line, in the order in which they appear. - // P are octets used to encode the prefix itself. - // L are octets used to encode the prefix length. - // The prefix information P and L is taken from the specified context. - Err(Error) + Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast8bits(&data[start..][..1]), + )) } - (1, 1, 0b01 | 0b10 | 0b11) => Ok(Address::Reserved), + (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 1, 0b01 | 0b10 | 0b11) => Ok(UnresolvedAddress::Reserved), _ => Err(Error), } } @@ -512,6 +808,18 @@ pub mod iphc { _ => unreachable!(), } } + + /// Return the length of the header. + pub fn header_len(&self) -> usize { + let mut len = self.ip_fields_start(); + len += self.traffic_class_size(); + len += self.next_header_size(); + len += self.hop_limit_size(); + len += self.src_address_size(); + len += self.dst_address_size(); + + len as usize + } } impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { @@ -556,7 +864,7 @@ pub mod iphc { raw[idx..idx + value.len()].copy_from_slice(value); } - /// Set the Next Header of this IPHC packet. + /// Set the Next Header. /// /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize { @@ -572,7 +880,7 @@ pub mod iphc { idx } - /// Set the Hop Limit of this IPHC packet. + /// Set the Hop Limit. /// /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to. fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize { @@ -752,7 +1060,7 @@ pub mod iphc { } } - /// A high-level representation of a LOWPAN_IPHC header. + /// A high-level representation of a 6LoWPAN IPHC header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { @@ -762,10 +1070,14 @@ pub mod iphc { pub ll_dst_addr: Option, pub next_header: NextHeader, pub hop_limit: u8, + // TODO(thvdveld): refactor the following fields into something else + pub ecn: Option, + pub dscp: Option, + pub flow_label: Option, } impl Repr { - /// Parse a LOWPAN_IPHC packet and return a high-level representation. + /// Parse a 6LoWPAN IPHC header and return a high-level representation. /// /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the /// IPv6 packets. @@ -773,11 +1085,11 @@ pub mod iphc { packet: &Packet<&T>, ll_src_addr: Option, ll_dst_addr: Option, - ) -> Result { + ) -> Result { // Ensure basic accessors will work. packet.check_len()?; - if packet.dispatch_field() != DISPATCH { + if packet.dispatch_field() != DISPATCH_IPHC_HEADER { // This is not an LOWPAN_IPHC packet. return Err(Error); } @@ -785,13 +1097,16 @@ pub mod iphc { let src_addr = packet.src_addr()?.resolve(ll_src_addr)?; let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr)?; - Ok(Repr { + Ok(Self { src_addr, ll_src_addr, dst_addr, ll_dst_addr, next_header: packet.next_header(), hop_limit: packet.hop_limit(), + ecn: packet.ecn_field(), + dscp: packet.dscp_field(), + flow_label: packet.flow_label_field(), }) } @@ -882,21 +1197,24 @@ pub mod iphc { 16 }; - // Add the size of the traffic flow. - // TODO(thvdveld): implement traffic flow for sixlowpan - len += 0; + len += match (self.ecn, self.dscp, self.flow_label) { + (Some(_), Some(_), Some(_)) => 4, + (Some(_), None, Some(_)) => 3, + (Some(_), Some(_), None) => 1, + (None, None, None) => 0, + _ => unreachable!(), + }; len } - /// Emit a high-level representation into a LOWPAN_IPHC packet. + /// Emit a high-level representation into a 6LoWPAN IPHC header. pub fn emit + AsMut<[u8]>>(&self, packet: &mut Packet) { let idx = 2; packet.set_dispatch_field(); - // SETTING THE TRAFFIC FLOW - // TODO(thvdveld): needs more work. + // FIXME(thvdveld): we don't set anything from the traffic flow. packet.set_tf_field(0b11); let idx = packet.set_next_header(self.next_header, idx); @@ -939,8 +1257,14 @@ pub mod iphc { assert_eq!(packet.dst_address_size(), 0); assert_eq!(packet.hop_limit(), 64); - assert_eq!(packet.src_addr(), Ok(Address::Elided)); - assert_eq!(packet.dst_addr(), Ok(Address::Elided)); + assert_eq!( + packet.src_addr(), + Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)) + ); + assert_eq!( + packet.dst_addr(), + Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)) + ); let bytes = [ 0x7e, 0xf7, // IPHC, @@ -966,24 +1290,32 @@ pub mod iphc { assert_eq!(packet.dst_address_size(), 0); assert_eq!(packet.hop_limit(), 64); - assert_eq!(packet.src_addr(), Ok(Address::WithContext(&[]))); - assert_eq!(packet.dst_addr(), Ok(Address::WithContext(&[]))); + assert_eq!( + packet.src_addr(), + Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + ); + assert_eq!( + packet.dst_addr(), + Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + ); } } } pub mod nhc { - use super::{Error, Result}; - use crate::wire::ip::checksum; - use crate::wire::ip::Address as IpAddress; - use crate::wire::ipv6; - use crate::wire::udp::Repr as UdpRepr; - use crate::wire::IpProtocol; + //! Implementation of Next Header Compression from [RFC 6282 § 4]. + //! + //! [RFC 6282 § 4]: https://datatracker.ietf.org/doc/html/rfc6282#section-4 + use super::{Error, NextHeader, Result, DISPATCH_EXT_HEADER, DISPATCH_UDP_HEADER}; + use crate::wire::{ + ip::{checksum, Address as IpAddress}, + ipv6, + udp::Repr as UdpRepr, + IpProtocol, + }; use byteorder::{ByteOrder, NetworkEndian}; use ipv6::Address; - use super::NextHeader; - macro_rules! get_field { ($name:ident, $mask:expr, $shift:expr) => { fn $name(&self) -> u8 { @@ -1005,29 +1337,45 @@ pub mod nhc { }; } - /// A read/write wrapper around a LOWPAN_NHC frame buffer. #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Packet> { - ExtensionHeader(ExtensionHeaderPacket), - UdpHeader(UdpPacket), + /// A read/write wrapper around a 6LoWPAN_NHC Header. + /// [RFC 6282 § 4.2] specifies the format of the header. + /// + /// The header has the following format: + /// ```txt + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | 1 | 1 | 1 | 0 | EID |NH | + /// +---+---+---+---+---+---+---+---+ + /// ``` + /// + /// With: + /// - EID: the extension header ID + /// - NH: Next Header + /// + /// [RFC 6282 § 4.2]: https://datatracker.ietf.org/doc/html/rfc6282#section-4.2 + pub enum NhcPacket { + ExtHeader, + UdpHeader, } - impl> Packet { - pub fn dispatch(buffer: T) -> Result> { + impl NhcPacket { + /// Returns the type of the Next Header header. + /// This can either be an Extenstion header or an 6LoWPAN Udp header. + /// + /// # Errors + /// Returns `[Error::Unrecognized]` when neither the Extension Header dispatch or the Udp + /// dispatch is recognized. + pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result { let raw = buffer.as_ref(); - #[cfg(feature = "std")] - println!("{:02x?}", raw[0]); - - if raw[0] >> 4 == 0b1110 { + if raw[0] >> 4 == DISPATCH_EXT_HEADER { // We have a compressed IPv6 Extension Header. - Ok(Packet::ExtensionHeader(ExtensionHeaderPacket::new_checked( - buffer, - )?)) - } else if raw[0] >> 3 == 0b11110 { + Ok(Self::ExtHeader) + } else if raw[0] >> 3 == DISPATCH_UDP_HEADER { // We have a compressed UDP header. - Ok(Packet::UdpHeader(UdpPacket::new_checked(buffer)?)) + Ok(Self::UdpHeader) } else { Err(Error) } @@ -1036,7 +1384,7 @@ pub mod nhc { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum ExtensionHeaderId { + pub enum ExtHeaderId { HopByHopHeader, RoutingHeader, FragmentHeader, @@ -1046,40 +1394,38 @@ pub mod nhc { Reserved, } - impl From for IpProtocol { - fn from(val: ExtensionHeaderId) -> Self { + impl From for IpProtocol { + fn from(val: ExtHeaderId) -> Self { match val { - ExtensionHeaderId::HopByHopHeader => IpProtocol::HopByHop, - ExtensionHeaderId::RoutingHeader => IpProtocol::Ipv6Route, - ExtensionHeaderId::FragmentHeader => IpProtocol::Ipv6Frag, - ExtensionHeaderId::DestinationOptionsHeader => IpProtocol::Ipv6Opts, - ExtensionHeaderId::MobilityHeader => IpProtocol::Unknown(0), - ExtensionHeaderId::Header => IpProtocol::Unknown(0), - ExtensionHeaderId::Reserved => IpProtocol::Unknown(0), + ExtHeaderId::HopByHopHeader => Self::HopByHop, + ExtHeaderId::RoutingHeader => Self::Ipv6Route, + ExtHeaderId::FragmentHeader => Self::Ipv6Frag, + ExtHeaderId::DestinationOptionsHeader => Self::Ipv6Opts, + ExtHeaderId::MobilityHeader => Self::Unknown(0), + ExtHeaderId::Header => Self::Unknown(0), + ExtHeaderId::Reserved => Self::Unknown(0), } } } - pub(crate) const EXT_HEADER_DISPATCH: u8 = 0b1110; - - /// A read/write wrapper around a LOWPAN_NHC Next Header frame buffer. + /// A read/write wrapper around a 6LoWPAN NHC Extension header. #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct ExtensionHeaderPacket> { + pub struct ExtHeaderPacket> { buffer: T, } - impl> ExtensionHeaderPacket { - /// Input a raw octet buffer with a LOWPAN_NHC Extension Header frame structure. - pub fn new_unchecked(buffer: T) -> ExtensionHeaderPacket { - ExtensionHeaderPacket { buffer } + impl> ExtHeaderPacket { + /// Input a raw octet buffer with a 6LoWPAN NHC Extension Header structure. + pub fn new_unchecked(buffer: T) -> Self { + ExtHeaderPacket { buffer } } /// Shorthand for a combination of [new_unchecked] and [check_len]. /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len - pub fn new_checked(buffer: T) -> Result> { + pub fn new_checked(buffer: T) -> Result { let packet = Self::new_unchecked(buffer); packet.check_len()?; Ok(packet) @@ -1106,15 +1452,15 @@ pub mod nhc { get_field!(nh_field, 0b1, 0); /// Return the Extension Header ID. - pub fn extension_header_id(&self) -> ExtensionHeaderId { + pub fn extension_header_id(&self) -> ExtHeaderId { match self.eid_field() { - 0 => ExtensionHeaderId::HopByHopHeader, - 1 => ExtensionHeaderId::RoutingHeader, - 2 => ExtensionHeaderId::FragmentHeader, - 3 => ExtensionHeaderId::DestinationOptionsHeader, - 4 => ExtensionHeaderId::MobilityHeader, - 5 | 6 => ExtensionHeaderId::Reserved, - 7 => ExtensionHeaderId::Header, + 0 => ExtHeaderId::HopByHopHeader, + 1 => ExtHeaderId::RoutingHeader, + 2 => ExtHeaderId::FragmentHeader, + 3 => ExtHeaderId::DestinationOptionsHeader, + 4 => ExtHeaderId::MobilityHeader, + 5 | 6 => ExtHeaderId::Reserved, + 7 => ExtHeaderId::Header, _ => unreachable!(), } } @@ -1152,7 +1498,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + ?Sized> ExtensionHeaderPacket<&'a T> { + impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> { /// Return a pointer to the payload. pub fn payload(&self) -> &'a [u8] { let start = 2 + self.next_header_size(); @@ -1160,7 +1506,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> ExtensionHeaderPacket { + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { let start = 2 + self.next_header_size(); @@ -1170,21 +1516,21 @@ pub mod nhc { /// Set the dispatch field to `0b1110`. fn set_dispatch_field(&mut self) { let data = self.buffer.as_mut(); - data[0] = (data[0] & !(0b1111 << 4)) | (EXT_HEADER_DISPATCH << 4); + data[0] = (data[0] & !(0b1111 << 4)) | (DISPATCH_EXT_HEADER << 4); } set_field!(set_eid_field, 0b111, 1); set_field!(set_nh_field, 0b1, 0); /// Set the Extension Header ID field. - fn set_extension_header_id(&mut self, ext_header_id: ExtensionHeaderId) { + fn set_extension_header_id(&mut self, ext_header_id: ExtHeaderId) { let id = match ext_header_id { - ExtensionHeaderId::HopByHopHeader => 0, - ExtensionHeaderId::RoutingHeader => 1, - ExtensionHeaderId::FragmentHeader => 2, - ExtensionHeaderId::DestinationOptionsHeader => 3, - ExtensionHeaderId::MobilityHeader => 4, - ExtensionHeaderId::Header => 7, + ExtHeaderId::HopByHopHeader => 0, + ExtHeaderId::RoutingHeader => 1, + ExtHeaderId::FragmentHeader => 2, + ExtHeaderId::DestinationOptionsHeader => 3, + ExtHeaderId::MobilityHeader => 4, + ExtHeaderId::Header => 7, _ => unreachable!(), }; @@ -1214,28 +1560,26 @@ pub mod nhc { } } - /// A high-level representation of an LOWPAN_NHC Extension Header header. + /// A high-level representation of an 6LoWPAN NHC Extension header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct ExtensionHeaderRepr { - ext_header_id: ExtensionHeaderId, + pub struct ExtHeaderRepr { + ext_header_id: ExtHeaderId, next_header: NextHeader, length: u8, } - impl ExtensionHeaderRepr { - /// Parse a LOWPAN_NHC Extension Header packet and return a high-level representation. - pub fn parse + ?Sized>( - packet: &ExtensionHeaderPacket<&T>, - ) -> Result { + impl ExtHeaderRepr { + /// Parse a 6LoWPAN NHC Extension Header packet and return a high-level representation. + pub fn parse + ?Sized>(packet: &ExtHeaderPacket<&T>) -> Result { // Ensure basic accessors will work. packet.check_len()?; - if packet.dispatch_field() != EXT_HEADER_DISPATCH { + if packet.dispatch_field() != DISPATCH_EXT_HEADER { return Err(Error); } - Ok(ExtensionHeaderRepr { + Ok(Self { ext_header_id: packet.extension_header_id(), next_header: packet.next_header(), length: packet.payload().len() as u8, @@ -1255,8 +1599,8 @@ pub mod nhc { len } - /// Emit a high-level representation into a LOWPAN_NHC Extension Header packet. - pub fn emit + AsMut<[u8]>>(&self, packet: &mut ExtensionHeaderPacket) { + /// Emit a high-level representaiton into a 6LoWPAN NHC Extension Header packet. + pub fn emit + AsMut<[u8]>>(&self, packet: &mut ExtHeaderPacket) { packet.set_dispatch_field(); packet.set_extension_header_id(self.ext_header_id); packet.set_next_header(self.next_header); @@ -1264,33 +1608,45 @@ pub mod nhc { } } - pub(crate) const UDP_DISPATCH: u8 = 0b11110; - - /// A read/write wrapper around a 6LoWPAN_NHC_UDP frame buffer. + /// A read/write wrapper around a 6LoWPAN_NHC UDP frame. + /// [RFC 6282 § 4.3] specifies the format of the header. + /// + /// The base header has the following formath: + /// ```txt + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | 1 | 1 | 1 | 1 | 0 | C | P | + /// +---+---+---+---+---+---+---+---+ + /// With: + /// - C: checksum, specifies if the checksum is elided. + /// - P: ports, specifies if the ports are elided. + /// ``` + /// + /// [RFC 6282 § 4.3]: https://datatracker.ietf.org/doc/html/rfc6282#section-4.3 #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct UdpPacket> { + pub struct UdpNhcPacket> { buffer: T, } - impl> UdpPacket { + impl> UdpNhcPacket { /// Input a raw octet buffer with a LOWPAN_NHC frame structure for UDP. - pub fn new_unchecked(buffer: T) -> UdpPacket { - UdpPacket { buffer } + pub fn new_unchecked(buffer: T) -> Self { + Self { buffer } } /// Shorthand for a combination of [new_unchecked] and [check_len]. /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len - pub fn new_checked(buffer: T) -> Result> { + pub fn new_checked(buffer: T) -> Result { let packet = Self::new_unchecked(buffer); packet.check_len()?; Ok(packet) } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error)` if the buffer is too short. + /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); @@ -1397,7 +1753,7 @@ pub mod nhc { } // Return the size of the checksum field. - fn checksum_size(&self) -> usize { + pub(crate) fn checksum_size(&self) -> usize { match self.checksum_field() { 0b0 => 2, 0b1 => 0, @@ -1406,7 +1762,7 @@ pub mod nhc { } /// Returns the total size of both port numbers. - fn ports_size(&self) -> usize { + pub(crate) fn ports_size(&self) -> usize { match self.ports_field() { 0b00 => 4, // 16 bits + 16 bits 0b01 => 3, // 16 bits + 8 bits @@ -1417,7 +1773,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + ?Sized> UdpPacket<&'a T> { + impl<'a, T: AsRef<[u8]> + ?Sized> UdpNhcPacket<&'a T> { /// Return a pointer to the payload. pub fn payload(&self) -> &'a [u8] { let start = 1 + self.ports_size() + self.checksum_size(); @@ -1425,7 +1781,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpPacket { + impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpNhcPacket { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { let start = 1 + self.ports_size() + 2; // XXX(thvdveld): we assume we put the checksum inlined. @@ -1435,7 +1791,7 @@ pub mod nhc { /// Set the dispatch field to `0b11110`. fn set_dispatch_field(&mut self) { let data = self.buffer.as_mut(); - data[0] = (data[0] & !(0b11111 << 3)) | (UDP_DISPATCH << 3); + data[0] = (data[0] & !(0b11111 << 3)) | (DISPATCH_UDP_HEADER << 3); } set_field!(set_checksum_field, 0b1, 2); @@ -1487,23 +1843,21 @@ pub mod nhc { } } - /// A high-level representation of a LOWPAN_NHC UDP header. + /// A high-level representation of a 6LoWPAN NHC UDP header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UdpNhcRepr(pub UdpRepr); impl<'a> UdpNhcRepr { - /// Parse a LOWWPAN_NHC UDP packet and return a high-level representation. + /// Parse a 6LoWPAN NHC UDP packet and return a high-level representation. pub fn parse + ?Sized>( - packet: &UdpPacket<&'a T>, + packet: &UdpNhcPacket<&'a T>, src_addr: &ipv6::Address, dst_addr: &ipv6::Address, - _checksum: Option, - ) -> Result { - // Ensure basic accessors will work. + ) -> Result { packet.check_len()?; - if packet.dispatch_field() != UDP_DISPATCH { + if packet.dispatch_field() != DISPATCH_UDP_HEADER { return Err(Error); } @@ -1530,7 +1884,7 @@ pub mod nhc { return Err(Error); }; - Ok(UdpNhcRepr(UdpRepr { + Ok(Self(UdpRepr { src_port: packet.src_port(), dst_port: packet.dst_port(), })) @@ -1553,7 +1907,7 @@ pub mod nhc { /// Emit a high-level representation into a LOWPAN_NHC UDP header. pub fn emit + AsMut<[u8]>>( &self, - packet: &mut UdpPacket, + packet: &mut UdpNhcPacket, src_addr: &Address, dst_addr: &Address, payload_len: usize, @@ -1602,45 +1956,39 @@ pub mod nhc { fn ext_header_nhc_fields() { let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]; - let packet = ExtensionHeaderPacket::new_checked(&bytes[..]).unwrap(); - assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH); + let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap(); + assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER); assert_eq!(packet.length_field(), 6); - assert_eq!( - packet.extension_header_id(), - ExtensionHeaderId::RoutingHeader - ); + assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader); assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); } #[test] fn ext_header_emit() { - let ext_header = ExtensionHeaderRepr { - ext_header_id: ExtensionHeaderId::RoutingHeader, + let ext_header = ExtHeaderRepr { + ext_header_id: ExtHeaderId::RoutingHeader, next_header: NextHeader::Compressed, length: 6, }; let len = ext_header.buffer_len(); let mut buffer = [0u8; 127]; - let mut packet = ExtensionHeaderPacket::new_unchecked(&mut buffer[..len]); + let mut packet = ExtHeaderPacket::new_unchecked(&mut buffer[..len]); ext_header.emit(&mut packet); - assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH); + assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER); assert_eq!(packet.next_header(), NextHeader::Compressed); assert_eq!(packet.length_field(), 6); - assert_eq!( - packet.extension_header_id(), - ExtensionHeaderId::RoutingHeader - ); + assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader); } #[test] fn udp_nhc_fields() { let bytes = [0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4]; - let packet = UdpPacket::new_checked(&bytes[..]).unwrap(); - assert_eq!(packet.dispatch_field(), UDP_DISPATCH); + let packet = UdpNhcPacket::new_checked(&bytes[..]).unwrap(); + assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER); assert_eq!(packet.checksum(), Some(0x28c4)); assert_eq!(packet.src_port(), 5678); assert_eq!(packet.dst_port(), 8765); @@ -1660,12 +2008,12 @@ pub mod nhc { let len = udp.header_len() + payload.len(); let mut buffer = [0u8; 127]; - let mut packet = UdpPacket::new_unchecked(&mut buffer[..len]); + let mut packet = UdpNhcPacket::new_unchecked(&mut buffer[..len]); udp.emit(&mut packet, &src_addr, &dst_addr, payload.len(), |buf| { buf.copy_from_slice(&payload[..]) }); - assert_eq!(packet.dispatch_field(), UDP_DISPATCH); + assert_eq!(packet.dispatch_field(), DISPATCH_UDP_HEADER); assert_eq!(packet.src_port(), 0xf0b1); assert_eq!(packet.dst_port(), 0xf001); assert_eq!(packet.payload_mut(), b"Hello World!"); @@ -1675,88 +2023,223 @@ pub mod nhc { #[cfg(test)] mod test { - //use super::*; - - //#[test] - //fn ieee802154_udp() { - //use crate::wire::ieee802154::Frame as Ieee802154Frame; - //use crate::wire::ieee802154::Repr as Ieee802154Repr; - //use crate::wire::ipv6routing; - - //// This data is captured using Wireshark from the communication between a RPL 6LoWPAN server - //// and a RPL 6LoWPAN client. - //// The frame is thus an IEEE802.15.4 frame, containing a 6LoWPAN packet, - //// containing a RPL extension header and an UDP header. - //let bytes: &[u8] = &[ - //0x61, 0xdc, 0xdd, 0xcd, 0xab, 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, 0xbf, - //0x9b, 0x15, 0x06, 0x00, 0x4b, 0x12, 0x00, 0x7e, 0xf7, 0x00, 0xe3, 0x06, 0x03, 0x00, - //0xff, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4, 0x68, 0x65, 0x6c, - //0x6c, 0x6f, 0x20, 0x36, 0x35, 0x18, 0xb9, - //]; - - //let ieee802154_frame = Ieee802154Frame::new_checked(bytes).unwrap(); - //let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap(); - - //let iphc_frame = iphc::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap(); - //let iphc_repr = iphc::Repr::parse( - //&iphc_frame, - //ieee802154_repr.src_addr, - //ieee802154_repr.dst_addr, - //) - //.unwrap(); - - //// The next header is compressed. - //assert_eq!(iphc_repr.next_header, NextHeader::Compressed); - - //// We dispatch the NHC packet. - //let nhc_packet = nhc::Packet::dispatch(iphc_frame.payload()).unwrap(); - - //let udp_payload = match nhc_packet { - //nhc::Packet::ExtensionHeader(ext_packet) => { - //// The next header is compressed (it is the UDP NHC compressed header). - //assert_eq!(ext_packet.next_header(), NextHeader::Compressed); - //assert_eq!(ext_packet.length_field(), 6); - //let payload = ext_packet.payload(); - - //let length = ext_packet.length_field() as usize; - //let ext_packet_payload = &payload[..length]; - - //match ext_packet.extension_header_id() { - //nhc::ExtensionHeaderId::RoutingHeader => { - //// We are not interested in the Next Header protocol. - //let proto = ipv6::Protocol::Unknown(0); - //let mut new_payload = [0; 8]; - - //new_payload[0] = proto.into(); - //new_payload[1] = (2 + length - 8) as u8; - //new_payload[2..].copy_from_slice(ext_packet_payload); - - //let routing = ipv6routing::Header::new_checked(new_payload).unwrap(); - - //assert_eq!(routing.routing_type(), ipv6routing::Type::Rpl); - //assert_eq!(routing.segments_left(), 0); - //assert_eq!(routing.cmpr_e(), 0xf); - //assert_eq!(routing.cmpr_i(), 0xf); - //} - //_ => unreachable!(), - //} - - //&payload[length..] - //} - //_ => unreachable!(), - //}; - - //let udp_nhc_frame = nhc::UdpPacket::new_checked(udp_payload).unwrap(); - //let udp_repr = nhc::UdpNhcRepr::parse( - //&udp_nhc_frame, - //&iphc_repr.src_addr, - //&iphc_repr.dst_addr, - //None, - //) - //.unwrap(); - - //assert_eq!(udp_repr.src_port, 5678); - //assert_eq!(udp_repr.dst_port, 8765); - //assert_eq!(udp_nhc_frame.checksum(), Some(0x28c4)); - //} + use super::*; + + #[test] + fn sixlowpan_fragment_emit() { + let repr = frag::Repr::FirstFragment { + size: 0xff, + tag: 0xabcd, + }; + let buffer = [0u8; 4]; + let mut packet = frag::Packet::new_unchecked(buffer); + + assert_eq!(repr.buffer_len(), 4); + repr.emit(&mut packet); + + assert_eq!(packet.datagram_size(), 0xff); + assert_eq!(packet.datagram_tag(), 0xabcd); + assert_eq!(packet.into_inner(), [0xc0, 0xff, 0xab, 0xcd]); + + let repr = frag::Repr::Fragment { + size: 0xff, + tag: 0xabcd, + offset: 0xcc, + }; + let buffer = [0u8; 5]; + let mut packet = frag::Packet::new_unchecked(buffer); + + assert_eq!(repr.buffer_len(), 5); + repr.emit(&mut packet); + + assert_eq!(packet.datagram_size(), 0xff); + assert_eq!(packet.datagram_tag(), 0xabcd); + assert_eq!(packet.into_inner(), [0xe0, 0xff, 0xab, 0xcd, 0xcc]); + } + + #[test] + fn sixlowpan_three_fragments() { + use crate::iface::{FragmentsCache, SixlowpanAssemblerInfo}; + use crate::time::Instant; + use crate::wire::ieee802154::Frame as Ieee802154Frame; + use crate::wire::ieee802154::Repr as Ieee802154Repr; + use crate::wire::Ieee802154Address; + use std::collections::BTreeMap; + + let mut frags_cache = FragmentsCache::new(vec![], BTreeMap::new()); + + let frame1: &[u8] = &[ + 0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, + 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xc1, 0x33, 0x00, 0x3f, 0x6e, 0x33, 0x02, + 0x35, 0x3d, 0xf0, 0xd2, 0x5f, 0x1b, 0x39, 0xb4, 0x6b, 0x4c, 0x6f, 0x72, 0x65, 0x6d, + 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, + 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, + 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x71, + 0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64, 0x69, 0x6f, 0x2c, 0x20, + 0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x72, + ]; + + let ieee802154_frame = Ieee802154Frame::new_checked(frame1).unwrap(); + let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap(); + + let sixlowpan_frame = + SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap(); + + let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame { + frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap() + } else { + unreachable!() + }; + + assert_eq!(frag.datagram_size(), 307); + assert_eq!(frag.datagram_tag(), 0x003f); + assert_eq!(frag.datagram_offset(), 0); + + let key = frag.get_key(&ieee802154_repr); + + let uncompressed = 40 + 8; + let compressed = 5 + 7; + + frags_cache + .reserve_with_key(&key) + .unwrap() + .start( + frag.datagram_size() as usize - uncompressed + compressed, + SixlowpanAssemblerInfo::new(uncompressed - compressed), + Instant::now(), + ) + .unwrap(); + frags_cache + .get_packet_assembler_mut(&key) + .unwrap() + .add(frag.payload(), 0, Instant::now()) + .unwrap(); + + let frame2: &[u8] = &[ + 0x41, 0xcc, 0x93, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, + 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x11, 0x75, 0x74, + 0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x73, 0x74, 0x69, + 0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, 0x6e, 0x63, 0x20, 0x65, + 0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65, 0x2e, 0x20, 0x4c, 0x6f, 0x72, + 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, + 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, + 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, + ]; + + let ieee802154_frame = Ieee802154Frame::new_checked(frame2).unwrap(); + let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap(); + + let sixlowpan_frame = + SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap(); + + let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame { + frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap() + } else { + unreachable!() + }; + + assert_eq!(frag.datagram_size(), 307); + assert_eq!(frag.datagram_tag(), 0x003f); + assert_eq!(frag.datagram_offset(), 136 / 8); + + let key = frag.get_key(&ieee802154_repr); + + frags_cache + .get_packet_assembler_mut(&key) + .unwrap() + .add( + frag.payload(), + frag.datagram_offset() as usize * 8, + Instant::now(), + ) + .unwrap(); + + let frame3: &[u8] = &[ + 0x41, 0xcc, 0x94, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, + 0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x1d, 0x2e, 0x20, + 0x41, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64, + 0x69, 0x6f, 0x2c, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65, + 0x6c, 0x20, 0x72, 0x75, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74, + 0x72, 0x69, 0x73, 0x74, 0x69, 0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, + 0x75, 0x6e, 0x63, 0x20, 0x65, 0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65, + 0x2e, 0x20, 0x0a, + ]; + + let ieee802154_frame = Ieee802154Frame::new_checked(frame3).unwrap(); + let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap(); + + let sixlowpan_frame = + SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap(); + + let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame { + frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap() + } else { + unreachable!() + }; + + assert_eq!(frag.datagram_size(), 307); + assert_eq!(frag.datagram_tag(), 0x003f); + assert_eq!(frag.datagram_offset(), 232 / 8); + + let key = frag.get_key(&ieee802154_repr); + + frags_cache + .get_packet_assembler_mut(&key) + .unwrap() + .add( + frag.payload(), + frag.datagram_offset() as usize * 8, + Instant::now(), + ) + .unwrap(); + + let assembled_packet = frags_cache.get_assembled_packet(&key).unwrap(); + + let sixlowpan_frame = SixlowpanPacket::dispatch(assembled_packet).unwrap(); + + let iphc = if let SixlowpanPacket::IphcHeader = sixlowpan_frame { + iphc::Packet::new_checked(assembled_packet).unwrap() + } else { + unreachable!() + }; + + let iphc_repr = + iphc::Repr::parse(&iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr).unwrap(); + + assert_eq!( + iphc_repr.dst_addr, + ipv6::Address::from_bytes(&[ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, + ]), + ); + assert_eq!( + iphc_repr.ll_dst_addr, + Some(Ieee802154Address::from_bytes(&[ + 0x1a, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + ])), + ); + assert_eq!(iphc_repr.next_header, NextHeader::Compressed); + assert_eq!(iphc_repr.hop_limit, 64); + + let sixlowpan_frame = nhc::NhcPacket::dispatch(iphc.payload()).unwrap(); + + let udp_hdr = if let nhc::NhcPacket::UdpHeader = sixlowpan_frame { + nhc::UdpNhcPacket::new_checked(iphc.payload()).unwrap() + } else { + unreachable!() + }; + + let payload = udp_hdr.payload(); + assert_eq!(String::from_utf8_lossy(payload), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. \n"); + + let udp_repr = + nhc::UdpNhcRepr::parse(&udp_hdr, &iphc_repr.src_addr, &iphc_repr.dst_addr).unwrap(); + + assert_eq!(udp_repr.src_port, 53855); + assert_eq!(udp_repr.dst_port, 6969); + assert_eq!(udp_hdr.checksum(), Some(0xb46b)); + } } From adf56a17015220a036d548f4e607899799e05581 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 22 Feb 2022 12:10:54 +0100 Subject: [PATCH 340/566] sixlowpan: add fragmentation logic in interface --- examples/sixlowpan.rs | 85 ++- fuzz/fuzz_targets/sixlowpan_udp_header.rs | 10 +- src/iface/fragmentation.rs | 27 +- src/iface/interface.rs | 652 ++++++++++++++++------ src/wire/sixlowpan.rs | 4 +- 5 files changed, 592 insertions(+), 186 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 6d5ce8fae..b31d54e0d 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -47,11 +47,13 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::udp; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp::socket::tcp; +use smoltcp::storage::RingBuffer; fn main() { utils::setup_logging(""); @@ -72,6 +74,10 @@ fn main() { let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); + let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); @@ -80,22 +86,44 @@ fn main() { 64, )]; + let cache = FragmentsCache::new(vec![], BTreeMap::new()); + + let buffer: Vec<(usize, managed::ManagedSlice<'_, u8>)> = (0..12) + .into_iter() + .map(|_| (0_usize, managed::ManagedSlice::from(vec![0; 127]))) + .collect(); + + let out_fragments_cache = RingBuffer::new(buffer); + let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) - .neighbor_cache(neighbor_cache); + .neighbor_cache(neighbor_cache) + .sixlowpan_fragments_cache(cache) + .out_fragments_cache(out_fragments_cache); let mut iface = builder.finalize(); let udp_handle = iface.add_socket(udp_socket); + let tcp_handle = iface.add_socket(tcp_socket); + + let socket = iface.get_socket::(tcp_handle); + socket.listen(50000).unwrap(); + + let mut tcp_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); + + let mut poll = true; + while poll { + match iface.poll(timestamp) { + Ok(r) => poll = r, + Err(e) => { + debug!("poll error: {}", e); + break; + } } } @@ -105,6 +133,7 @@ fn main() { socket.bind(6969).unwrap() } + let mut buffer = vec![0; 1500]; let client = match socket.recv() { Ok((data, endpoint)) => { debug!( @@ -112,17 +141,51 @@ fn main() { str::from_utf8(data).unwrap(), endpoint ); - Some(endpoint) + buffer[..data.len()].copy_from_slice(data); + Some((data.len(), endpoint)) } Err(_) => None, }; - if let Some(endpoint) = client { - let data = b"hello\n"; + if let Some((len, endpoint)) = client { debug!( "udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap() + str::from_utf8(&buffer[..len]).unwrap() ); - socket.send_slice(data, endpoint).unwrap(); + socket.send_slice(&buffer[..len], endpoint).unwrap(); + } + + let socket = iface.get_socket::(tcp_handle); + if socket.is_active() && !tcp_active { + debug!("connected"); + } else if !socket.is_active() && tcp_active { + debug!("disconnected"); + } + tcp_active = socket.is_active(); + + if socket.may_recv() { + let data = socket + .recv(|data| { + let data = data.to_owned(); + if !data.is_empty() { + debug!( + "recv data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + } + (data.len(), data) + }) + .unwrap(); + + if socket.can_send() && !data.is_empty() { + debug!( + "send data: {:?}", + str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") + ); + socket.send_slice(&data[..]).unwrap(); + } + } else if socket.may_send() { + debug!("close"); + socket.close(); } phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); diff --git a/fuzz/fuzz_targets/sixlowpan_udp_header.rs b/fuzz/fuzz_targets/sixlowpan_udp_header.rs index 9a2079b4d..de876a5e9 100644 --- a/fuzz/fuzz_targets/sixlowpan_udp_header.rs +++ b/fuzz/fuzz_targets/sixlowpan_udp_header.rs @@ -1,6 +1,6 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use smoltcp::wire::{Ipv6Address, SixlowpanUdpPacket, SixlowpanUdpRepr}; +use smoltcp::wire::{Ipv6Address, SixlowpanUdpNhcPacket, SixlowpanUdpNhcRepr}; #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] pub struct AddressFuzzer(pub [u8; 16]); @@ -16,21 +16,19 @@ struct SixlowpanUdpPacketFuzzer<'a> { data: &'a [u8], src_addr: AddressFuzzer, dst_addr: AddressFuzzer, - checksum: Option, } fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| { - if let Ok(ref frame) = SixlowpanUdpPacket::new_checked(fuzz.data) { - if let Ok(repr) = SixlowpanUdpRepr::parse( + if let Ok(ref frame) = SixlowpanUdpNhcPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanUdpNhcRepr::parse( frame, &fuzz.src_addr.into(), &fuzz.dst_addr.into(), - fuzz.checksum, ) { let payload = frame.payload(); let mut buffer = vec![0; repr.header_len() + payload.len()]; - let mut frame = SixlowpanUdpPacket::new_unchecked(&mut buffer[..]); + let mut frame = SixlowpanUdpNhcPacket::new_unchecked(&mut buffer[..]); repr.emit( &mut frame, &fuzz.src_addr.into(), diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 512d7195a..cebc11d5d 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -25,6 +25,7 @@ enum AssemblerState { total_size: usize, last_updated: Instant, started_on: Instant, + offset_correction: isize, }, } @@ -48,7 +49,12 @@ impl<'a> PacketAssembler<'a> { /// /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too small for holding all the /// fragments of a packet. - pub(crate) fn start(&mut self, total_size: usize, start_time: Instant) -> Result<()> { + pub(crate) fn start( + &mut self, + total_size: usize, + start_time: Instant, + offset_correction: isize, + ) -> Result<()> { match &mut self.buffer { ManagedSlice::Borrowed(b) if b.len() < total_size => { return Err(Error::PacketAssemblerBufferTooSmall); @@ -65,6 +71,7 @@ impl<'a> PacketAssembler<'a> { total_size, last_updated: start_time, started_on: start_time, + offset_correction, }; Ok(()) @@ -86,8 +93,12 @@ impl<'a> PacketAssembler<'a> { ref mut assembler, total_size, ref mut last_updated, + offset_correction, .. } => { + let offset = offset as isize + offset_correction; + let offset = if offset <= 0 { 0 } else { offset as usize }; + if offset + data.len() > total_size { return Err(Error::PacketAssemblerBufferTooSmall); } @@ -392,10 +403,10 @@ mod tests { let mut p_assembler = PacketAssembler::new(&mut storage[..]); assert_eq!( - p_assembler.start(2, Instant::now()), + p_assembler.start(2, Instant::now(), 0), Err(Error::PacketAssemblerBufferTooSmall) ); - assert_eq!(p_assembler.start(1, Instant::now()), Ok(())); + assert_eq!(p_assembler.start(1, Instant::now(), 0), Ok(())); let data = b"Hello World!"; assert_eq!( @@ -409,7 +420,7 @@ mod tests { let mut storage = [0u8; 5]; let mut p_assembler = PacketAssembler::new(&mut storage[..]); - p_assembler.start(5, Instant::now()).unwrap(); + p_assembler.start(5, Instant::now(), 0).unwrap(); let data = b"Rust"; p_assembler.add(&data[..], 0, Instant::now()).unwrap(); @@ -424,7 +435,7 @@ mod tests { let data = b"Hello World!"; - p_assembler.start(data.len(), Instant::now()).unwrap(); + p_assembler.start(data.len(), Instant::now(), 0).unwrap(); p_assembler.add(b"Hello ", 0, Instant::now()).unwrap(); assert_eq!( @@ -483,7 +494,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now()) + .start(0, Instant::now(), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -491,7 +502,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now()) + .start(0, Instant::now(), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -499,7 +510,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now()) + .start(0, Instant::now(), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ce981f416..8a0fc7ceb 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -5,7 +5,6 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; -#[allow(unused)] #[cfg(feature = "proto-sixlowpan")] use super::fragmentation::PacketAssemblerSet; use super::socket_set::SocketSet; @@ -20,10 +19,16 @@ use crate::socket::dhcpv4; #[cfg(feature = "socket-dns")] use crate::socket::dns; use crate::socket::*; +use crate::storage::RingBuffer; use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; +type OutBuffer<'a> = RingBuffer<'a, (usize, ManagedSlice<'a, u8>)>; + +#[cfg(feature = "proto-sixlowpan")] +type SixlowpanFragmentsBuffer<'a> = PacketAssemblerSet<'a, SixlowpanFragKey>; + macro_rules! check { ($e:expr) => { match $e { @@ -49,6 +54,11 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, sockets: SocketSet<'a>, inner: InterfaceInner<'a>, + // Currently this is behind the sixlowpan feature. However, this buffer could also be used for other protocols. + #[cfg(feature = "proto-sixlowpan")] + out_fragments: OutBuffer<'a>, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: SixlowpanFragmentsBuffer<'a>, } /// The device independent part of an Ethernet network interface. @@ -61,6 +71,7 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { pub struct InterfaceInner<'a> { caps: DeviceCapabilities, now: Instant, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option>, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -69,6 +80,8 @@ pub struct InterfaceInner<'a> { sequence_no: u8, #[cfg(feature = "medium-ieee802154")] pan_id: Option, + #[cfg(feature = "proto-sixlowpan")] + tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -99,6 +112,10 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, random_seed: u64, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: Option>, + #[cfg(feature = "proto-sixlowpan")] + out_fragments: Option>, } impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> @@ -108,7 +125,7 @@ where /// Create a builder used for creating a network interface using the /// given device and address. #[cfg_attr( - feature = "medium-ethernet", + all(feature = "medium-ethernet", not(feature = "proto-sixlowpan")), doc = r##" # Examples @@ -139,7 +156,7 @@ let iface = InterfaceBuilder::new(device, vec![]) SocketsT: Into>>, { InterfaceBuilder { - device: device, + device, sockets: SocketSet::new(sockets), #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -157,6 +174,11 @@ let iface = InterfaceBuilder::new(device, vec![]) #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), random_seed: 0, + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: None, + #[cfg(feature = "proto-sixlowpan")] + out_fragments: None, } } @@ -268,6 +290,18 @@ let iface = InterfaceBuilder::new(device, vec![]) self } + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_fragments_cache(mut self, storage: SixlowpanFragmentsBuffer<'a>) -> Self { + self.sixlowpan_fragments = Some(storage); + self + } + + #[cfg(feature = "proto-sixlowpan")] + pub fn out_fragments_cache(mut self, storage: OutBuffer<'a>) -> Self { + self.out_fragments = Some(storage); + self + } + /// Create a network interface using the previously provided configuration. /// /// # Panics @@ -337,9 +371,28 @@ let iface = InterfaceBuilder::new(device, vec![]) } } + #[cfg(feature = "proto-sixlowpan")] + let mut tag; + + #[cfg(feature = "proto-sixlowpan")] + loop { + tag = (rand.rand_u32() & 0xffff) as u16; + if tag != 0 { + break; + } + } + Interface { device: self.device, sockets: self.sockets, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: self + .sixlowpan_fragments + .expect("Cache for incoming 6LoWPAN fragments is required"), + #[cfg(feature = "proto-sixlowpan")] + out_fragments: self + .out_fragments + .expect("Cache for outgoing fragments is required"), inner: InterfaceInner { now: Instant::from_secs(0), caps, @@ -359,6 +412,8 @@ let iface = InterfaceBuilder::new(device, vec![]) sequence_no, #[cfg(feature = "medium-ieee802154")] pan_id: self.pan_id, + #[cfg(feature = "proto-sixlowpan")] + tag, rand, }, } @@ -654,7 +709,7 @@ where { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { Ok(false) @@ -686,7 +741,7 @@ where } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { Ok(false) @@ -771,6 +826,7 @@ where self.inner.now = timestamp; let mut readiness_may_have_changed = false; + loop { let processed_any = self.socket_ingress(); let emitted_any = self.socket_egress(); @@ -784,6 +840,19 @@ where break; } } + + #[cfg(feature = "proto-sixlowpan")] + while !self.out_fragments.is_empty() { + let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; + + // FIXME(thvdveld): remove the unwrap + let (len, buffer) = self.out_fragments.dequeue_one().unwrap(); + tx_token.consume(self.inner.now, *len, |tx_token| { + tx_token.copy_from_slice(&buffer[..*len]); + Ok(()) + })?; + } + Ok(readiness_may_have_changed) } @@ -838,7 +907,10 @@ where device, inner, sockets, - .. + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments, + #[cfg(feature = "proto-sixlowpan")] + out_fragments, } = self; while let Some((rx_token, tx_token)) = device.receive() { let res = rx_token.consume(inner.now, |frame| { @@ -854,15 +926,19 @@ where #[cfg(feature = "medium-ip")] Medium::Ip => { if let Some(packet) = inner.process_ip(sockets, &frame) { - if let Err(err) = inner.dispatch_ip(tx_token, packet) { + if let Err(err) = inner.dispatch_ip(tx_token, packet, None) { net_debug!("Failed to send response: {}", err); } } } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = inner.process_ieee802154(sockets, &frame) { - if let Err(err) = inner.dispatch_ieee802154(tx_token, packet) { + if let Some(packet) = + inner.process_ieee802154(sockets, &frame, sixlowpan_fragments) + { + if let Err(err) = + inner.dispatch_ip(tx_token, packet, Some(out_fragments)) + { net_debug!("Failed to send response: {}", err); } } @@ -885,6 +961,8 @@ where device, inner, sockets, + #[cfg(feature = "proto-sixlowpan")] + out_fragments, .. } = self; let _caps = device.capabilities(); @@ -901,8 +979,16 @@ where let mut neighbor_addr = None; let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); - let tx_token = device.transmit().ok_or(Error::Exhausted)?; - inner.dispatch_ip(tx_token, response)?; + // FIXME(thvdveld): remove unwrap + let tx_token = device.transmit().ok_or(Error::Exhausted).unwrap(); + #[cfg(feature = "proto-sixlowpan")] + { + inner.dispatch_ip(tx_token, response, Some(out_fragments)).unwrap(); + } + #[cfg(not(feature = "proto-sixlowpan"))] + { + check!(inner.dispatch_ip(tx_token, response, None)); + } emitted_any = true; Ok(()) }; @@ -982,7 +1068,7 @@ where 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, pkt)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; } self.inner.igmp_report_state = IgmpReportState::Inactive; @@ -1006,7 +1092,7 @@ where 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, pkt)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; } let next_timeout = (timeout + interval).max(self.inner.now); @@ -1142,7 +1228,10 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), #[cfg(feature = "medium-ieee802154")] - sequence_no: 0, + sequence_no: 1, + + #[cfg(feature = "proto-sixlowpan")] + tag: 1, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( @@ -1186,6 +1275,13 @@ impl<'a> InterfaceInner<'a> { no } + #[cfg(feature = "proto-sixlowpan")] + fn get_sixlowpan_fragment_tag(&mut self) -> u16 { + let tag = self.tag; + self.tag = self.tag.wrapping_add(1); + tag + } + /// Determine if the given `Ipv6Address` is the solicited node /// multicast address for a IPv6 addresses assigned to the interface. /// See [RFC 4291 § 2.7.1] for more details. @@ -1299,11 +1395,12 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "medium-ieee802154")] - fn process_ieee802154<'frame, T: AsRef<[u8]> + ?Sized>( + fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - sixlowpan_payload: &'frame T, - ) -> Option> { + sixlowpan_payload: &'payload T, + fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + ) -> Option> { let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); @@ -1319,25 +1416,130 @@ impl<'a> InterfaceInner<'a> { && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) { net_debug!( - "dropping {:?} because not our PAN id (or not broadcast)", + "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)", ieee802154_repr ); return None; } match ieee802154_frame.payload() { - Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload), + Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, fragments), None => None, } } #[cfg(feature = "proto-sixlowpan")] - fn process_sixlowpan<'frame, T: AsRef<[u8]> + ?Sized>( + fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, - payload: &'frame T, - ) -> Option> { + payload: &'payload T, + fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + ) -> Option> { + check!(fragments.remove_when(|frag| Ok( + self.now - frag.start_time().unwrap() > Duration::from_secs(60) + ))); + fragments.remove_discarded(); + + let payload = match check!(SixlowpanPacket::dispatch(payload)) { + SixlowpanPacket::FragmentHeader => { + // We have a fragment header, which means we cannot process the 6LoWPAN packet, + // unless we have a complete one after processing this fragment. + let frag = check!(SixlowpanFragPacket::new_checked(payload)); + + // The key specifies to which 6LoWPAN fragment it belongs too. + // It is based on the link layer addresses, the tag and the size. + let key = frag.get_key(ieee802154_repr); + + // The offset of this fragment in increments of 8 octets. + let offset = frag.datagram_offset() as usize * 8; + + if frag.is_first_fragment() { + // The first fragment contains the total size of the IPv6 packet. + // However, we received a packet that is compressed following the 6LoWPAN + // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN + // packet size. The packet size can be different because of first the + // compression of the IP header and when UDP is used (because the UDP header + // can also be compressed). Other headers are not compressed by 6LoWPAN. + + let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); + let iphc_repr = check!(SixlowpanIphcRepr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + )); + + // The uncompressed header size always starts with 40, since this is the size + // of a IPv6 header. + let mut uncompressed_header_size = 40; + let mut compressed_header_size = iphc.header_len(); + + // We need to check if we have an UDP packet, since this header can also be + // compressed by 6LoWPAN. We currently don't support extension headers yet. + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + SixlowpanNhcPacket::ExtHeader => { + net_debug!("6LoWPAN: extension headers not supported"); + return None; + } + SixlowpanNhcPacket::UdpHeader => { + let udp_packet = + check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); + + uncompressed_header_size += 8; + compressed_header_size += + 1 + udp_packet.ports_size() + udp_packet.checksum_size(); + } + } + } + SixlowpanNextHeader::Uncompressed(_) => (), + } + + // We reserve a spot in the packet assembler set and add the required + // information to the packet assembler. + // This information is the total size of the packet when it is fully assmbled. + // We also pass the header size, since this is needed when other fragments + // (other than the first one) are added. + check!(check!(fragments.reserve_with_key(&key)).start( + frag.datagram_size() as usize - uncompressed_header_size + + compressed_header_size, + self.now, + -((uncompressed_header_size - compressed_header_size) as isize), + )); + } + + let frags = check!(fragments.get_packet_assembler_mut(&key)); + + // Check if 60 seconds have passed since the start of the first fragment. + if self.now - frags.start_time().unwrap() > Duration::from_secs(60) { + frags.mark_discarded(); + return None; + } + + net_trace!("6LoWPAN: received packet fragment"); + + // Add the fragment to the packet assembler. + match frags.add(frag.payload(), offset, self.now) { + Ok(true) => { + net_trace!("6LoWPAN: fragmented packet now complete"); + check!(fragments.get_assembled_packet(&key)) + } + Ok(false) => { + return None; + } + Err(Error::PacketAssemblerOverlap) => { + net_trace!("6LoWPAN: overlap in packet"); + frags.mark_discarded(); + return None; + } + Err(_) => return None, + } + } + SixlowpanPacket::IphcHeader => payload.as_ref(), + }; + + // At this point we should have a valid 6LoWPAN packet. // The first header needs to be an IPHC header. let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload)); let iphc_repr = check!(SixlowpanIphcRepr::parse( @@ -1352,10 +1554,9 @@ impl<'a> InterfaceInner<'a> { dst_addr: iphc_repr.dst_addr, hop_limit: iphc_repr.hop_limit, next_header: IpProtocol::Unknown(0), - payload_len: iphc_repr.buffer_len(), + payload_len: 40, }; - // Currently we assume the next header is a UDP, so we ignore everything else. match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { match check!(SixlowpanNhcPacket::dispatch(payload)) { @@ -1370,9 +1571,10 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "socket-udp")] SixlowpanNhcPacket::UdpHeader => { - ipv6_repr.next_header = IpProtocol::Udp; - // Handle the UDP let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload)); + ipv6_repr.next_header = IpProtocol::Udp; + ipv6_repr.payload_len += 8 + udp_packet.payload().len(); + let udp_repr = check!(SixlowpanUdpNhcRepr::parse( &udp_packet, &iphc_repr.src_addr, @@ -1381,6 +1583,10 @@ impl<'a> InterfaceInner<'a> { // Look for UDP sockets that will accept the UDP packet. // If it does not accept the packet, then send an ICMP message. + // + // NOTE(thvdveld): this is currently the same code as in self.process_udp. + // However, we cannot use that one because the payload passed to it is a + // normal IPv6 UDP payload, which is not what we have here. for udp_socket in sockets .iter_mut() .filter_map(|i| udp::Socket::downcast(&mut i.socket)) @@ -1396,6 +1602,8 @@ impl<'a> InterfaceInner<'a> { } } + // When we are here then then there was no UDP socket that accepted the UDP + // message. let payload_len = icmp_reply_payload_len( payload.len(), IPV6_MIN_MTU, @@ -1415,8 +1623,14 @@ impl<'a> InterfaceInner<'a> { ipv6_repr.next_header = IpProtocol::Icmpv6; self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) } - _ => { - net_debug!("Headers other than ICMPv6 and compressed headers are currently not supported for 6LoWPAN"); + #[cfg(feature = "socket-tcp")] + IpProtocol::Tcp => { + ipv6_repr.next_header = nxt_hdr; + ipv6_repr.payload_len += payload.len(); + self.process_tcp(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) + } + proto => { + net_debug!("6LoWPAN: {} currently not supported", proto); None } }, @@ -2046,7 +2260,7 @@ impl<'a> InterfaceInner<'a> { Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() { Some(src_addr) => { let ipv4_reply_repr = Ipv4Repr { - src_addr: src_addr, + src_addr, dst_addr: ipv4_repr.src_addr, next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), @@ -2214,7 +2428,7 @@ impl<'a> InterfaceInner<'a> { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet), + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, None), } } @@ -2406,7 +2620,7 @@ impl<'a> InterfaceInner<'a> { solicit, )); - self.dispatch_ip(tx_token, packet)?; + self.dispatch_ip(tx_token, packet, None)?; } #[allow(unreachable_patterns)] @@ -2424,7 +2638,12 @@ impl<'a> InterfaceInner<'a> { } } - fn dispatch_ip(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { + fn dispatch_ip( + &mut self, + tx_token: Tx, + packet: IpPacket, + _out_fragments: Option<&mut OutBuffer<'_>>, + ) -> Result<()> { let ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); @@ -2471,17 +2690,6 @@ impl<'a> InterfaceInner<'a> { Ok(()) }) } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => self.dispatch_ieee802154(tx_token, packet), - } - } - - #[cfg(feature = "medium-ieee802154")] - fn dispatch_ieee802154(&mut self, tx_token: Tx, packet: IpPacket) -> Result<()> { - let ip_repr = packet.ip_repr(); - assert!(!ip_repr.dst_addr().is_unspecified()); - - match self.caps.medium { #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( @@ -2493,134 +2701,246 @@ impl<'a> InterfaceInner<'a> { _ => unreachable!(), }; - let ack_request = dst_hardware_addr.is_unicast(); + self.dispatch_ieee802154( + dst_hardware_addr, + &ip_repr, + tx_token, + packet, + _out_fragments, + ) + } + } + } + + #[cfg(feature = "medium-ieee802154")] + fn dispatch_ieee802154( + &mut self, + ll_dst_addr: Ieee802154Address, + ip_repr: &IpRepr, + tx_token: Tx, + packet: IpPacket, + out_fragments: Option<&mut OutBuffer<'_>>, + ) -> Result<()> { + // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. + // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to + // fragment it. + let ll_src_addr = self.hardware_addr.map_or_else( + || Err(Error::Malformed), + |addr| match addr { + HardwareAddress::Ieee802154(addr) => Ok(addr), + _ => Err(Error::Malformed), + }, + )?; - let ack_request = match packet { - IpPacket::Icmpv6(_) => false, - _ => ack_request, - }; + let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { + (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), + _ => return Err(Error::Unaddressable), + }; - let mut tx_len = 0; + // Create the IEEE802.15.4 header. + let mut ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(ll_dst_addr), + src_pan_id: self.pan_id, + src_addr: Some(ll_src_addr), + }; - let ll_src_addr = - if let Some(HardwareAddress::Ieee802154(addr)) = self.hardware_addr { - Some(addr) - } else { - return Err(Error::Malformed); - }; + // Create the 6LoWPAN IPHC header. + let iphc_repr = SixlowpanIphcRepr { + src_addr, + ll_src_addr: Some(ll_src_addr), + dst_addr, + ll_dst_addr: Some(ll_dst_addr), + next_header: match &packet { + IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp), + #[cfg(feature = "socket-udp")] + IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, + #[allow(unreachable_patterns)] + _ => return Err(Error::Unrecognized), + }, + hop_limit: ip_repr.hop_limit(), + ecn: None, + dscp: None, + flow_label: None, + }; - let ieee_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request, - sequence_number: Some(self.get_sequence_number()), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: self.pan_id, - dst_addr: Some(dst_hardware_addr), - src_pan_id: self.pan_id, - src_addr: ll_src_addr, - }; + // We will first emit every packet into this buffer. + // When we emitted everything we know if we need fragmentation or not. + // This method is not ideal, but emitting UDP frames require that the full buffer is passed + // to the `emit` function. However, the buffer for IEEE802.15.4 is usually 125 bytes. + let mut buffer = [0; 4096]; + let mut total_size = 0; - let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { - (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), - #[allow(unreachable_patterns)] - _ => return Err(Error::Unaddressable), - }; + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut buffer[..]); + ieee_repr.emit(&mut ieee_packet); + total_size += ieee_repr.buffer_len(); - #[allow(unreachable_patterns)] - let (next_header, hop_limit) = match &packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp(_) => (SixlowpanNextHeader::Compressed, 64), - IpPacket::Icmpv6((_, repr)) => ( - SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), - match repr { - Icmpv6Repr::Ndisc(_) => 255, - _ => 64, - }, - ), - _ => return Err(Error::Unrecognized), - }; + let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut buffer[total_size..]); + iphc_repr.emit(&mut iphc_packet); + total_size += iphc_repr.buffer_len(); - let iphc_repr = SixlowpanIphcRepr { - src_addr, - ll_src_addr, - dst_addr, - ll_dst_addr: Some(dst_hardware_addr), - next_header, - hop_limit, - ecn: None, - dscp: None, - flow_label: None, - }; + let mut compressed_headers_len = iphc_repr.buffer_len(); - tx_len += ieee_repr.buffer_len(); - tx_len += iphc_repr.buffer_len(); + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut buffer[total_size..][..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); - #[allow(unreachable_patterns)] - match &packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udp_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(*udp_repr); - tx_len += udp_repr.header_len() + payload.len(); - } - IpPacket::Icmpv6((_, icmp)) => { - tx_len += icmp.buffer_len(); - } - _ => return Err(Error::Unrecognized), - } + compressed_headers_len += udp_repr.header_len(); + total_size += udp_repr.header_len() + payload.len(); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut buffer[total_size..][..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - // 1. Create the header of 802.15.4 - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buffer); - ieee_repr.emit(&mut ieee_packet); + total_size += tcp_repr.buffer_len(); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = Icmpv6Packet::new_unchecked( + &mut buffer[total_size..][..icmp_repr.buffer_len()], + ); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); - let mut start = ieee_repr.buffer_len(); + total_size += icmp_repr.buffer_len(); + } + _ => return Err(Error::Unrecognized), + } - // 2. Create the header for 6LoWPAN IPHC - let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut tx_buffer[start..tx_len]); - iphc_repr.emit(&mut iphc_packet); - start += iphc_repr.buffer_len(); + if total_size > 125 { + // Fragmentation is required because the data does not fit into a IEEE802.15.4 packet. + // Everything has already been emitted in the buffer, thus we only need to insert the + // fragmentation headers in the correct places. + // + // The first fragment header is right after the first IEEE802.15.4 header. + // + // Since we can only use `tx_token` once we need to store other packets into the + // out_fragments buffer. + + let out_fragments = out_fragments.unwrap(); + + // The datagram size that we need to set in the first fragment header is equal to the + // IPv6 payload length + 40. + let datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + // We generate a random tag. + let tag = self.get_sixlowpan_fragment_tag(); + + let ieee_len = ieee_repr.buffer_len(); + + // We calculate how much data we can send in the first fragment and the other + // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight + // (except for the last fragment) since the offset field in the fragment is an offset + // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. + // + // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + + let frag1 = SixlowpanFragRepr::FirstFragment { + size: datagram_size, + tag, + }; + let mut fragn = SixlowpanFragRepr::Fragment { + size: datagram_size, + tag, + offset: 0, + }; - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udp_repr, payload)) => { - // 3. Create the header for 6LoWPAN UDP - let mut udp_packet = - SixlowpanUdpNhcPacket::new_unchecked(&mut tx_buffer[start..tx_len]); - - SixlowpanUdpNhcRepr(udp_repr).emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - // 3. Create the header for ICMPv6 - let mut icmp_packet = - Icmpv6Packet::new_unchecked(&mut tx_buffer[start..tx_len]); - - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - } - _ => return Err(Error::Unrecognized), - } + let frag1_size = ((125 - ieee_len - frag1.buffer_len() - compressed_headers_len) + & 0xffff_fff8) + + compressed_headers_len; + let fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + + tx_token.consume( + self.now, + ieee_len + frag1.buffer_len() + frag1_size, + |mut tx_buf| { + // Copy the IEEE header + tx_buf[..ieee_len].copy_from_slice(&buffer[..ieee_len]); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the first fragment header + let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); + frag1.emit(&mut frag1_packet); + tx_buf = &mut tx_buf[frag1.buffer_len()..]; + + // Add the buffer part. + tx_buf[..frag1_size].copy_from_slice(&buffer[ieee_len..][..frag1_size]); Ok(()) - }) + }, + )?; + + let mut written = ieee_len + frag1_size; + + // We need to save the the rest of the packets into the `out_fragments` buffer. + while written < total_size { + out_fragments.enqueue_one_with::<'_, (), Error, _>(|(len, tx_buf)| { + let mut tx_buf = &mut tx_buf[..]; + + // Modify the sequence number of the IEEE header + ieee_repr.sequence_number = Some(self.get_sequence_number()); + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the next fragment header + let datagram_offset = ((40 + written - ieee_len) / 8) as u8; + fragn.set_offset(datagram_offset); + let mut frag_packet = + SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); + fragn.emit(&mut frag_packet); + tx_buf = &mut tx_buf[fragn.buffer_len()..]; + + // Add the buffer part + let frag_size = (total_size - written).min(fragn_size); + tx_buf[..frag_size].copy_from_slice(&buffer[written..][..frag_size]); + written += frag_size; + + // Save the lenght of this packet. + *len = ieee_len + fragn.buffer_len() + frag_size; + Ok(()) + }).unwrap().unwrap(); } - #[allow(unreachable_patterns)] - _ => Err(Error::NotSupported), + + Ok(()) + } else { + // No fragmentation is needed thus we can copy everything over that already has been + // emitted in the buffer. + tx_token.consume(self.now, total_size, |tx_buffer| { + tx_buffer.copy_from_slice(&buffer[..total_size]); + Ok(()) + }) } } @@ -2732,10 +3052,20 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; + #[cfg(feature = "proto-sixlowpan")] + let iface_builder = InterfaceBuilder::new(device, vec![]) + .hardware_addr(EthernetAddress::default().into()) + .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .out_fragments_cache(RingBuffer::new(vec![])) + .ip_addrs(ip_addrs); + + #[cfg(not(feature = "proto-sixlowpan"))] let iface_builder = InterfaceBuilder::new(device, vec![]) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); + #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); iface_builder.finalize() @@ -2985,7 +3315,7 @@ mod test { payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }, - data: data, + data, }; let expected_repr = IpPacket::Icmpv4(( Ipv4Repr { @@ -3232,8 +3562,8 @@ mod test { }; #[cfg(feature = "proto-ipv6")] let ip_repr = Ipv6Repr { - src_addr: src_addr, - dst_addr: dst_addr, + src_addr, + dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, @@ -3468,7 +3798,11 @@ mod test { } #[test] - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] + #[cfg(all( + feature = "medium-ethernet", + feature = "proto-ipv4", + not(feature = "medium-ieee802154") + ))] fn test_arp_flush_after_update_ip() { let mut iface = create_loopback_ethernet(); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index d52194726..8dc8a2355 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2059,7 +2059,7 @@ mod test { #[test] fn sixlowpan_three_fragments() { - use crate::iface::{FragmentsCache, SixlowpanAssemblerInfo}; + use crate::iface::FragmentsCache; use crate::time::Instant; use crate::wire::ieee802154::Frame as Ieee802154Frame; use crate::wire::ieee802154::Repr as Ieee802154Repr; @@ -2106,8 +2106,8 @@ mod test { .unwrap() .start( frag.datagram_size() as usize - uncompressed + compressed, - SixlowpanAssemblerInfo::new(uncompressed - compressed), Instant::now(), + -((uncompressed - compressed) as isize), ) .unwrap(); frags_cache From c11e4bb6a7a1ca78d3a45a6e28237407ed0fce9d Mon Sep 17 00:00:00 2001 From: AntoonBeres <35069455+AntoonBeres@users.noreply.github.com> Date: Sun, 1 May 2022 18:30:05 +0200 Subject: [PATCH 341/566] added sixlowpan fragmentation benchmark --- Cargo.toml | 3 + examples/benchmark.rs | 3 +- examples/sixlowpan.rs | 4 +- examples/sixlowpan_benchmark.rs | 247 ++++++++++++++++++++++++++++++++ src/iface/interface.rs | 84 ++++++----- src/wire/sixlowpan.rs | 8 +- 6 files changed, 305 insertions(+), 44 deletions(-) create mode 100644 examples/sixlowpan_benchmark.rs diff --git a/Cargo.toml b/Cargo.toml index 058eef420..ae74d4f7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,9 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac [[example]] name = "sixlowpan" required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] +[[example]] +name = "sixlowpan_benchmark" +required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] [[example]] name = "dns" diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 720894b20..e4d3e27a0 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -81,8 +81,6 @@ fn main() { _ => panic!("invalid mode"), }; - thread::spawn(move || client(mode)); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); @@ -108,6 +106,7 @@ fn main() { let tcp2_handle = iface.add_socket(tcp2_socket); let default_timeout = Some(Duration::from_millis(1000)); + thread::spawn(move || client(mode)); let mut processed = 0; while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index b31d54e0d..d7d975a15 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -49,11 +49,11 @@ use std::str; use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; +use smoltcp::socket::tcp; use smoltcp::socket::udp; +use smoltcp::storage::RingBuffer; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; -use smoltcp::socket::tcp; -use smoltcp::storage::RingBuffer; fn main() { utils::setup_logging(""); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs new file mode 100644 index 000000000..6de9df621 --- /dev/null +++ b/examples/sixlowpan_benchmark.rs @@ -0,0 +1,247 @@ +//! 6lowpan benchmark exmaple +//! +//! This example runs a simple TCP throughput benchmark using the 6lowpan implementation in smoltcp +//! It is designed to run using the Linux ieee802154/6lowpan support, +//! using mac802154_hwsim. +//! +//! mac802154_hwsim allows you to create multiple "virtual" radios and specify +//! which is in range with which. This is very useful for testing without +//! needing real hardware. By default it creates two interfaces `wpan0` and +//! `wpan1` that are in range with each other. You can customize this with +//! the `wpan-hwsim` tool. +//! +//! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1` +//! unconfigured so smoltcp can use it with a raw socket. +//! +//! +//! +//! +//! +//! # Setup +//! +//! modprobe mac802154_hwsim +//! +//! ip link set wpan0 down +//! ip link set wpan1 down +//! iwpan dev wpan0 set pan_id 0xbeef +//! iwpan dev wpan1 set pan_id 0xbeef +//! ip link add link wpan0 name lowpan0 type lowpan +//! ip link set wpan0 up +//! ip link set wpan1 up +//! ip link set lowpan0 up +//! +//! +//! # Running +//! +//! Compile with `cargo build --release --example sixlowpan_benchmark` +//! Run it with `sudo ./target/release/examples/sixlowpan_benchmark [reader|writer]`. +//! +//! # Teardown +//! +//! rmmod mac802154_hwsim +//! + +mod utils; + +use log::debug; +use std::collections::BTreeMap; +use std::os::unix::io::AsRawFd; +use std::str; + +use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; +use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; +use smoltcp::socket::tcp; +use smoltcp::storage::RingBuffer; +use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; + +//For benchmark +use smoltcp::time::{Duration, Instant}; +use std::cmp; +use std::io::{Read, Write}; +use std::net::SocketAddrV6; +use std::net::TcpStream; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; + +use std::fs; + +fn if_nametoindex(ifname: &str) -> u32 { + let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{}/ifindex", ifname)) + .expect("couldn't read interface from \"/sys/devices/virtual/net\"") + .replace("\n", ""); + contents.parse::().unwrap() +} + +const AMOUNT: usize = 100_000_000; + +enum Client { + Reader, + Writer, +} + +fn client(kind: Client) { + let port: u16 = match kind { + Client::Reader => 1234, + Client::Writer => 1235, + }; + + let scope_id = if_nametoindex("lowpan0"); + + let socket_addr = SocketAddrV6::new( + "fe80:0:0:0:180b:4242:4242:4242".parse().unwrap(), + port, + 0, + scope_id, + ); + + let mut stream = TcpStream::connect(socket_addr).expect("failed to connect TLKAGMKA"); + let mut buffer = vec![0; 1_000_000]; + + let start = Instant::now(); + + let mut processed = 0; + while processed < AMOUNT { + let length = cmp::min(buffer.len(), AMOUNT - processed); + let result = match kind { + Client::Reader => stream.read(&mut buffer[..length]), + Client::Writer => stream.write(&buffer[..length]), + }; + match result { + Ok(0) => break, + Ok(result) => { + // print!("(P:{})", result); + processed += result + } + Err(err) => panic!("cannot process: {}", err), + } + } + + let end = Instant::now(); + + let elapsed = (end - start).total_millis() as f64 / 1000.0; + + println!("throughput: {:.3} Gbps", AMOUNT as f64 / elapsed / 0.125e9); + + CLIENT_DONE.store(true, Ordering::SeqCst); +} + +static CLIENT_DONE: AtomicBool = AtomicBool::new(false); + +fn main() { + #[cfg(feature = "log")] + utils::setup_logging("info"); + + let (mut opts, mut free) = utils::create_options(); + utils::add_middleware_options(&mut opts, &mut free); + free.push("MODE"); + + let mut matches = utils::parse_options(&opts, free); + + let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap(); + + let fd = device.as_raw_fd(); + let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + + let mode = match matches.free[0].as_ref() { + "reader" => Client::Reader, + "writer" => Client::Writer, + _ => panic!("invalid mode"), + }; + + let neighbor_cache = NeighborCache::new(BTreeMap::new()); + + let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer); + + let tcp2_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); + let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); + + let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ + 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + ]); + let ip_addrs = [IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )]; + + let cache = FragmentsCache::new(vec![], BTreeMap::new()); + + let buffer: Vec<(usize, managed::ManagedSlice<'_, u8>)> = (0..12) + .into_iter() + .map(|_| (0_usize, managed::ManagedSlice::from(vec![0; 1_000_000_000]))) + .collect(); + + let out_fragments_cache = RingBuffer::new(buffer); + + let mut builder = InterfaceBuilder::new(device, vec![]) + .ip_addrs(ip_addrs) + .pan_id(Ieee802154Pan(0xbeef)); + builder = builder + .hardware_addr(ieee802154_addr.into()) + .neighbor_cache(neighbor_cache) + .sixlowpan_fragments_cache(cache) + .out_fragments_cache(out_fragments_cache); + let mut iface = builder.finalize(); + + let tcp1_handle = iface.add_socket(tcp1_socket); + let tcp2_handle = iface.add_socket(tcp2_socket); + + let default_timeout = Some(Duration::from_millis(1000)); + + thread::spawn(move || client(mode)); + let mut processed = 0; + + while !CLIENT_DONE.load(Ordering::SeqCst) { + let timestamp = Instant::now(); + match iface.poll(timestamp) { + Ok(_) => {} + Err(e) => { + debug!("poll error: {}", e); + } + } + + // tcp:1234: emit data + let socket = iface.get_socket::(tcp1_handle); + if !socket.is_open() { + socket.listen(1234).unwrap(); + } + + if socket.can_send() && processed < AMOUNT { + let length = socket + .send(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); + processed += length; + } + + // tcp:1235: sink data + let socket = iface.get_socket::(tcp2_handle); + if !socket.is_open() { + socket.listen(1235).unwrap(); + } + + if socket.can_recv() && processed < AMOUNT { + let length = socket + .recv(|buffer| { + let length = cmp::min(buffer.len(), AMOUNT - processed); + (length, length) + }) + .unwrap(); + processed += length; + } + + match iface.poll_at(timestamp) { + Some(poll_at) if timestamp < poll_at => { + phy_wait(fd, Some(poll_at - timestamp)).expect("wait error"); + } + Some(_) => (), + None => { + phy_wait(fd, default_timeout).expect("wait error"); + } + } + } +} diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 8a0fc7ceb..a1edf6738 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -979,17 +979,24 @@ where let mut neighbor_addr = None; let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); - // FIXME(thvdveld): remove unwrap - let tx_token = device.transmit().ok_or(Error::Exhausted).unwrap(); - #[cfg(feature = "proto-sixlowpan")] - { - inner.dispatch_ip(tx_token, response, Some(out_fragments)).unwrap(); - } - #[cfg(not(feature = "proto-sixlowpan"))] - { - check!(inner.dispatch_ip(tx_token, response, None)); + match device.transmit().ok_or(Error::Exhausted) { + Ok(t) => { + #[cfg(feature = "proto-sixlowpan")] + if let Err(_e) = inner.dispatch_ip(t, response, Some(out_fragments)) { + net_debug!("failed to dispatch IP: {}", _e); + } + + #[cfg(not(feature = "proto-sixlowpan"))] + if let Err(_e) = inner.dispatch_ip(t, response, None) { + net_debug!("failed to dispatch IP: {}", _e); + } + emitted_any = true; + } + Err(e) => { + net_debug!("failed to transmit IP: {}", e); + } } - emitted_any = true; + Ok(()) }; @@ -2734,6 +2741,7 @@ impl<'a> InterfaceInner<'a> { let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), + #[allow(unreachable_patterns)] _ => return Err(Error::Unaddressable), }; @@ -2905,32 +2913,36 @@ impl<'a> InterfaceInner<'a> { // We need to save the the rest of the packets into the `out_fragments` buffer. while written < total_size { - out_fragments.enqueue_one_with::<'_, (), Error, _>(|(len, tx_buf)| { - let mut tx_buf = &mut tx_buf[..]; - - // Modify the sequence number of the IEEE header - ieee_repr.sequence_number = Some(self.get_sequence_number()); - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - // Add the next fragment header - let datagram_offset = ((40 + written - ieee_len) / 8) as u8; - fragn.set_offset(datagram_offset); - let mut frag_packet = - SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); - fragn.emit(&mut frag_packet); - tx_buf = &mut tx_buf[fragn.buffer_len()..]; - - // Add the buffer part - let frag_size = (total_size - written).min(fragn_size); - tx_buf[..frag_size].copy_from_slice(&buffer[written..][..frag_size]); - written += frag_size; - - // Save the lenght of this packet. - *len = ieee_len + fragn.buffer_len() + frag_size; - Ok(()) - }).unwrap().unwrap(); + out_fragments + .enqueue_one_with::<'_, (), Error, _>(|(len, tx_buf)| { + let mut tx_buf = &mut tx_buf[..]; + + // Modify the sequence number of the IEEE header + ieee_repr.sequence_number = Some(self.get_sequence_number()); + let mut ieee_packet = + Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the next fragment header + let datagram_offset = ((40 + written - ieee_len) / 8) as u8; + fragn.set_offset(datagram_offset); + let mut frag_packet = + SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); + fragn.emit(&mut frag_packet); + tx_buf = &mut tx_buf[fragn.buffer_len()..]; + + // Add the buffer part + let frag_size = (total_size - written).min(fragn_size); + tx_buf[..frag_size].copy_from_slice(&buffer[written..][..frag_size]); + written += frag_size; + + // Save the lenght of this packet. + *len = ieee_len + fragn.buffer_len() + frag_size; + Ok(()) + }) + .unwrap() + .unwrap(); } Ok(()) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 8dc8a2355..69caadd81 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -325,7 +325,7 @@ pub mod frag { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Packet { + impl + AsMut<[u8]>> Packet { fn set_dispatch_field(&mut self, value: u8) { let raw = self.buffer.as_mut(); raw[field::DISPATCH] = (raw[field::DISPATCH] & !(0b11111 << 3)) | (value << 3); @@ -839,7 +839,7 @@ pub mod iphc { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Packet { + impl + AsMut<[u8]>> Packet { /// Set the dispatch field to `0b011`. fn set_dispatch_field(&mut self) { let data = &mut self.buffer.as_mut()[field::IPHC_FIELD]; @@ -1506,7 +1506,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> ExtHeaderPacket { + impl + AsMut<[u8]>> ExtHeaderPacket { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { let start = 2 + self.next_header_size(); @@ -1781,7 +1781,7 @@ pub mod nhc { } } - impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpNhcPacket { + impl + AsMut<[u8]>> UdpNhcPacket { /// Return a mutable pointer to the payload. pub fn payload_mut(&mut self) -> &mut [u8] { let start = 1 + self.ports_size() + 2; // XXX(thvdveld): we assume we put the checksum inlined. From 352c13a374f17bdaf2664bc88137ac016a50696f Mon Sep 17 00:00:00 2001 From: lachlansneff-parallel <105677805+lachlansneff-parallel@users.noreply.github.com> Date: Wed, 25 May 2022 15:35:38 -0700 Subject: [PATCH 342/566] Change neighbor discovery timeout from 3s to 1s. --- src/iface/socket_meta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/socket_meta.rs b/src/iface/socket_meta.rs index 64cb30308..b2fba8b82 100644 --- a/src/iface/socket_meta.rs +++ b/src/iface/socket_meta.rs @@ -45,7 +45,7 @@ impl Meta { /// in milliseconds. /// /// See also `iface::NeighborCache::SILENT_TIME`. - pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(3_000); + pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(1_000); pub(crate) fn poll_at(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt where From 47253122b262da87a43361313289a89604e388c4 Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Wed, 1 Jun 2022 11:21:49 +0200 Subject: [PATCH 343/566] dhcpv4: add waker support --- src/socket/dhcpv4.rs | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d4f8649b5..694064e34 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "async")] +use core::task::Waker; + use crate::iface::Context; use crate::time::{Duration, Instant}; use crate::wire::dhcpv4::field as dhcpv4_field; @@ -7,6 +10,9 @@ use crate::wire::{ UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, }; +#[cfg(feature = "async")] +use super::WakerRegistration; + use super::PollAt; const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10); @@ -123,6 +129,10 @@ pub struct Socket { /// Ignore NAKs. ignore_naks: bool, + + /// Waker registration + #[cfg(feature = "async")] + waker: WakerRegistration, } /// DHCP client socket. @@ -142,6 +152,8 @@ impl Socket { transaction_id: 1, max_lease_duration: None, ignore_naks: false, + #[cfg(feature = "async")] + waker: WakerRegistration::new(), } } @@ -265,13 +277,13 @@ impl Socket { 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, config, renew_at, expires_at, }); + self.config_changed(); } } (ClientState::Requesting(_), DhcpMessageType::Nak) => { @@ -286,8 +298,8 @@ impl Socket { state.renew_at = renew_at; state.expires_at = expires_at; if state.config != config { - self.config_changed = true; state.config = config; + self.config_changed(); } } } @@ -520,7 +532,7 @@ impl Socket { pub fn reset(&mut self) { net_trace!("DHCP reset"); if let ClientState::Renewing(_) = &self.state { - self.config_changed = true; + self.config_changed(); } self.state = ClientState::Discovering(DiscoverState { retry_at: Instant::from_millis(0), @@ -542,6 +554,31 @@ impl Socket { Some(Event::Deconfigured) } } + + /// This function _must_ be called when the configuration provided to the + /// interface, by this DHCP socket, changes. It will update the `config_changed` field + /// so that a subsequent call to `poll` will yield an event, and wake a possible waker. + pub(crate) fn config_changed(&mut self) { + self.config_changed = true; + #[cfg(feature = "async")] + self.waker.wake(); + } + + /// Register a waker. + /// + /// The waker is woken on state changes that might affect the return value + /// of `poll` method calls, which indicates a new state in the DHCP configuration + /// provided by this DHCP socket. + /// + /// Notes: + /// + /// - Only one waker can be registered at a time. If another waker was previously registered, + /// it is overwritten and will no longer be woken. + /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. + #[cfg(feature = "async")] + pub fn register_waker(&mut self, waker: &Waker) { + self.waker.register(waker) + } } #[cfg(test)] From f3310e63f4b014cc7f5f04ad9393e13602e7c163 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 30 May 2022 16:11:45 +0200 Subject: [PATCH 344/566] refactor 6LoWPAN fragmentation --- examples/sixlowpan.rs | 14 +- examples/sixlowpan_benchmark.rs | 10 +- src/iface/interface.rs | 526 +++++++++++++++++++++----------- src/phy/raw_socket.rs | 6 + 4 files changed, 360 insertions(+), 196 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index d7d975a15..ef33ecfd3 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -51,7 +51,6 @@ use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; -use smoltcp::storage::RingBuffer; use smoltcp::time::Instant; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -70,8 +69,8 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); @@ -88,12 +87,7 @@ fn main() { let cache = FragmentsCache::new(vec![], BTreeMap::new()); - let buffer: Vec<(usize, managed::ManagedSlice<'_, u8>)> = (0..12) - .into_iter() - .map(|_| (0_usize, managed::ManagedSlice::from(vec![0; 127]))) - .collect(); - - let out_fragments_cache = RingBuffer::new(buffer); + let mut out_packet_buffer = [0u8; 1280]; let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) @@ -102,7 +96,7 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) - .out_fragments_cache(out_fragments_cache); + .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); let mut iface = builder.finalize(); let udp_handle = iface.add_socket(udp_socket); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 6de9df621..2fa393665 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -51,7 +51,6 @@ use std::str; use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; -use smoltcp::storage::RingBuffer; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; //For benchmark @@ -168,13 +167,6 @@ fn main() { let cache = FragmentsCache::new(vec![], BTreeMap::new()); - let buffer: Vec<(usize, managed::ManagedSlice<'_, u8>)> = (0..12) - .into_iter() - .map(|_| (0_usize, managed::ManagedSlice::from(vec![0; 1_000_000_000]))) - .collect(); - - let out_fragments_cache = RingBuffer::new(buffer); - let mut builder = InterfaceBuilder::new(device, vec![]) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); @@ -182,7 +174,7 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) - .out_fragments_cache(out_fragments_cache); + .sixlowpan_out_packet_cache(vec![]); let mut iface = builder.finalize(); let tcp1_handle = iface.add_socket(tcp1_socket); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index a1edf6738..d4fd3f6a2 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -19,15 +19,62 @@ use crate::socket::dhcpv4; #[cfg(feature = "socket-dns")] use crate::socket::dns; use crate::socket::*; -use crate::storage::RingBuffer; use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; -type OutBuffer<'a> = RingBuffer<'a, (usize, ManagedSlice<'a, u8>)>; +pub(crate) struct FragmentsBuffer<'a> { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData<&'a ()>, +} + +pub(crate) struct OutPackets<'a> { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_out_packet: SixlowpanOutPacket<'a>, + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData<&'a ()>, +} #[cfg(feature = "proto-sixlowpan")] -type SixlowpanFragmentsBuffer<'a> = PacketAssemblerSet<'a, SixlowpanFragKey>; +pub(crate) struct SixlowpanOutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The datagram size that is used for the fragmentation headers. + datagram_size: u16, + /// The datagram tag that is used for the fragmentation headers. + datagram_tag: u16, + + /// The size of the FRAG_N packets. + fragn_size: usize, + + /// The link layer IEEE802.15.4 source address. + ll_dst_addr: Ieee802154Address, + /// The link layer IEEE802.15.4 source address. + ll_src_addr: Ieee802154Address, +} + +#[cfg(feature = "proto-sixlowpan")] +impl<'a> SixlowpanOutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + datagram_size: 0, + datagram_tag: 0, + sent_bytes: 0, + fragn_size: 0, + ll_dst_addr: Ieee802154Address::Absent, + ll_src_addr: Ieee802154Address::Absent, + } + } +} macro_rules! check { ($e:expr) => { @@ -54,11 +101,8 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, sockets: SocketSet<'a>, inner: InterfaceInner<'a>, - // Currently this is behind the sixlowpan feature. However, this buffer could also be used for other protocols. - #[cfg(feature = "proto-sixlowpan")] - out_fragments: OutBuffer<'a>, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments: SixlowpanFragmentsBuffer<'a>, + fragments: FragmentsBuffer<'a>, + out_packets: OutPackets<'a>, } /// The device independent part of an Ethernet network interface. @@ -113,9 +157,9 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, random_seed: u64, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments: Option>, + sixlowpan_fragments: Option>, #[cfg(feature = "proto-sixlowpan")] - out_fragments: Option>, + sixlowpan_out_buffer: Option>, } impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> @@ -178,7 +222,7 @@ let iface = InterfaceBuilder::new(device, vec![]) #[cfg(feature = "proto-sixlowpan")] sixlowpan_fragments: None, #[cfg(feature = "proto-sixlowpan")] - out_fragments: None, + sixlowpan_out_buffer: None, } } @@ -291,14 +335,20 @@ let iface = InterfaceBuilder::new(device, vec![]) } #[cfg(feature = "proto-sixlowpan")] - pub fn sixlowpan_fragments_cache(mut self, storage: SixlowpanFragmentsBuffer<'a>) -> Self { + pub fn sixlowpan_fragments_cache( + mut self, + storage: PacketAssemblerSet<'a, SixlowpanFragKey>, + ) -> Self { self.sixlowpan_fragments = Some(storage); self } #[cfg(feature = "proto-sixlowpan")] - pub fn out_fragments_cache(mut self, storage: OutBuffer<'a>) -> Self { - self.out_fragments = Some(storage); + pub fn sixlowpan_out_packet_cache(mut self, storage: T) -> Self + where + T: Into>, + { + self.sixlowpan_out_buffer = Some(storage.into()); self } @@ -385,14 +435,25 @@ let iface = InterfaceBuilder::new(device, vec![]) Interface { device: self.device, sockets: self.sockets, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments: self - .sixlowpan_fragments - .expect("Cache for incoming 6LoWPAN fragments is required"), - #[cfg(feature = "proto-sixlowpan")] - out_fragments: self - .out_fragments - .expect("Cache for outgoing fragments is required"), + fragments: FragmentsBuffer { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_fragments: self + .sixlowpan_fragments + .expect("Cache for incoming 6LoWPAN fragments is required"), + + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData, + }, + out_packets: OutPackets { + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_out_packet: SixlowpanOutPacket::new( + self.sixlowpan_out_buffer + .expect("Cache for outgoing fragments is required"), + ), + + #[cfg(not(feature = "proto-sixlowpan"))] + _lifetime: core::marker::PhantomData, + }, inner: InterfaceInner { now: Instant::from_secs(0), caps, @@ -827,6 +888,33 @@ where let mut readiness_may_have_changed = false; + #[cfg(feature = "proto-sixlowpan")] + { + let SixlowpanOutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.sixlowpan_out_packet; + + if *packet_len > *sent_bytes { + match self.device.transmit().ok_or(Error::Exhausted) { + Ok(tx_token) => { + if let Err(e) = self.inner.dispatch_ieee802154_out_packet( + tx_token, + &mut self.out_packets.sixlowpan_out_packet, + ) { + net_debug!("failed to transmit: {}", e); + } + } + Err(e) => { + net_debug!("failed to transmit: {}", e); + } + } + + return Ok(true); + } + } + loop { let processed_any = self.socket_ingress(); let emitted_any = self.socket_egress(); @@ -841,18 +929,6 @@ where } } - #[cfg(feature = "proto-sixlowpan")] - while !self.out_fragments.is_empty() { - let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - - // FIXME(thvdveld): remove the unwrap - let (len, buffer) = self.out_fragments.dequeue_one().unwrap(); - tx_token.consume(self.inner.now, *len, |tx_token| { - tx_token.copy_from_slice(&buffer[..*len]); - Ok(()) - })?; - } - Ok(readiness_may_have_changed) } @@ -907,10 +983,8 @@ where device, inner, sockets, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_fragments, - #[cfg(feature = "proto-sixlowpan")] - out_fragments, + fragments: _fragments, + out_packets: _out_packets, } = self; while let Some((rx_token, tx_token)) = device.receive() { let res = rx_token.consume(inner.now, |frame| { @@ -933,11 +1007,13 @@ where } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = - inner.process_ieee802154(sockets, &frame, sixlowpan_fragments) - { + if let Some(packet) = inner.process_ieee802154( + sockets, + &frame, + &mut _fragments.sixlowpan_fragments, + ) { if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(out_fragments)) + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) { net_debug!("Failed to send response: {}", err); } @@ -961,8 +1037,7 @@ where device, inner, sockets, - #[cfg(feature = "proto-sixlowpan")] - out_fragments, + out_packets: _out_packets, .. } = self; let _caps = device.capabilities(); @@ -982,7 +1057,7 @@ where match device.transmit().ok_or(Error::Exhausted) { Ok(t) => { #[cfg(feature = "proto-sixlowpan")] - if let Err(_e) = inner.dispatch_ip(t, response, Some(out_fragments)) { + if let Err(_e) = inner.dispatch_ip(t, response, Some(_out_packets)) { net_debug!("failed to dispatch IP: {}", _e); } @@ -1406,7 +1481,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, sixlowpan_payload: &'payload T, - fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Option> { let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); @@ -1441,7 +1516,7 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: &'output mut SixlowpanFragmentsBuffer<'a>, + fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Option> { check!(fragments.remove_when(|frag| Ok( self.now - frag.start_time().unwrap() > Duration::from_secs(60) @@ -2649,7 +2724,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, packet: IpPacket, - _out_fragments: Option<&mut OutBuffer<'_>>, + _out_packet: Option<&mut OutPackets<'_>>, ) -> Result<()> { let ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); @@ -2708,13 +2783,7 @@ impl<'a> InterfaceInner<'a> { _ => unreachable!(), }; - self.dispatch_ieee802154( - dst_hardware_addr, - &ip_repr, - tx_token, - packet, - _out_fragments, - ) + self.dispatch_ieee802154(dst_hardware_addr, &ip_repr, tx_token, packet, _out_packet) } } } @@ -2722,16 +2791,16 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] fn dispatch_ieee802154( &mut self, - ll_dst_addr: Ieee802154Address, + ll_dst_a: Ieee802154Address, ip_repr: &IpRepr, tx_token: Tx, packet: IpPacket, - out_fragments: Option<&mut OutBuffer<'_>>, + out_packet: Option<&mut OutPackets>, ) -> Result<()> { // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to // fragment it. - let ll_src_addr = self.hardware_addr.map_or_else( + let ll_src_a = self.hardware_addr.map_or_else( || Err(Error::Malformed), |addr| match addr { HardwareAddress::Ieee802154(addr) => Ok(addr), @@ -2746,7 +2815,7 @@ impl<'a> InterfaceInner<'a> { }; // Create the IEEE802.15.4 header. - let mut ieee_repr = Ieee802154Repr { + let ieee_repr = Ieee802154Repr { frame_type: Ieee802154FrameType::Data, security_enabled: false, frame_pending: false, @@ -2755,17 +2824,17 @@ impl<'a> InterfaceInner<'a> { pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, dst_pan_id: self.pan_id, - dst_addr: Some(ll_dst_addr), + dst_addr: Some(ll_dst_a), src_pan_id: self.pan_id, - src_addr: Some(ll_src_addr), + src_addr: Some(ll_src_a), }; // Create the 6LoWPAN IPHC header. let iphc_repr = SixlowpanIphcRepr { src_addr, - ll_src_addr: Some(ll_src_addr), + ll_src_addr: Some(ll_src_a), dst_addr, - ll_dst_addr: Some(ll_dst_addr), + ll_dst_addr: Some(ll_dst_a), next_header: match &packet { IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), #[cfg(feature = "socket-tcp")] @@ -2781,21 +2850,10 @@ impl<'a> InterfaceInner<'a> { flow_label: None, }; - // We will first emit every packet into this buffer. - // When we emitted everything we know if we need fragmentation or not. - // This method is not ideal, but emitting UDP frames require that the full buffer is passed - // to the `emit` function. However, the buffer for IEEE802.15.4 is usually 125 bytes. - let mut buffer = [0; 4096]; + // Now we calculate the total size of the packet. + // We need to know this, such that we know when to do the fragmentation. let mut total_size = 0; - - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut buffer[..]); - ieee_repr.emit(&mut ieee_packet); - total_size += ieee_repr.buffer_len(); - - let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut buffer[total_size..]); - iphc_repr.emit(&mut iphc_packet); total_size += iphc_repr.buffer_len(); - let mut compressed_headers_len = iphc_repr.buffer_len(); #[allow(unreachable_patterns)] @@ -2803,98 +2861,134 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( - &mut buffer[total_size..][..udp_repr.header_len() + payload.len()], - ); - udp_repr.emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - compressed_headers_len += udp_repr.header_len(); total_size += udp_repr.header_len() + payload.len(); } #[cfg(feature = "socket-tcp")] IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = - TcpPacket::new_unchecked(&mut buffer[total_size..][..tcp_repr.buffer_len()]); - tcp_repr.emit( - &mut tcp_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &self.caps.checksum, - ); - total_size += tcp_repr.buffer_len(); } #[cfg(feature = "proto-ipv6")] IpPacket::Icmpv6((_, icmp_repr)) => { - let mut icmp_packet = Icmpv6Packet::new_unchecked( - &mut buffer[total_size..][..icmp_repr.buffer_len()], - ); - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - total_size += icmp_repr.buffer_len(); } _ => return Err(Error::Unrecognized), } - if total_size > 125 { - // Fragmentation is required because the data does not fit into a IEEE802.15.4 packet. - // Everything has already been emitted in the buffer, thus we only need to insert the - // fragmentation headers in the correct places. - // - // The first fragment header is right after the first IEEE802.15.4 header. - // - // Since we can only use `tx_token` once we need to store other packets into the - // out_fragments buffer. + let ieee_len = ieee_repr.buffer_len(); + + if total_size + ieee_len > 125 { + // The packet does not fit in one Ieee802154 frame, so we need fragmentation. + // We do this by emitting everything in the `out_packet.buffer` from the interface. + // After emitting everything into that buffer, we send the first fragment heere. + // When `poll` is called again, we check if out_packet was fully sent, otherwise we + // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + + // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // the link local source and destination address... + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = &mut out_packet.unwrap().sixlowpan_out_packet; + + *ll_dst_addr = ll_dst_a; + *ll_src_addr = ll_src_a; + + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); - let out_fragments = out_fragments.unwrap(); + let b = &mut buffer[iphc_repr.buffer_len()..]; + + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut b[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } + + *packet_len = total_size; // The datagram size that we need to set in the first fragment header is equal to the // IPv6 payload length + 40. - let datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + // We generate a random tag. let tag = self.get_sixlowpan_fragment_tag(); - - let ieee_len = ieee_repr.buffer_len(); - - // We calculate how much data we can send in the first fragment and the other - // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight - // (except for the last fragment) since the offset field in the fragment is an offset - // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. - // - // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + // We save the tag for the other fragments that will be created when calling `poll` + // multiple times. + *datagram_tag = tag; let frag1 = SixlowpanFragRepr::FirstFragment { - size: datagram_size, + size: *datagram_size, tag, }; - let mut fragn = SixlowpanFragRepr::Fragment { - size: datagram_size, + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, tag, offset: 0, }; + // We calculate how much data we can send in the first fragment and the other + // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight + // (except for the last fragment) since the offset field in the fragment is an offset + // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. + // + // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 let frag1_size = ((125 - ieee_len - frag1.buffer_len() - compressed_headers_len) & 0xffff_fff8) + compressed_headers_len; - let fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + + *sent_bytes = frag1_size; tx_token.consume( self.now, ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| { - // Copy the IEEE header - tx_buf[..ieee_len].copy_from_slice(&buffer[..ieee_len]); + // Add the IEEE header. + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); tx_buf = &mut tx_buf[ieee_len..]; // Add the first fragment header @@ -2903,59 +2997,137 @@ impl<'a> InterfaceInner<'a> { tx_buf = &mut tx_buf[frag1.buffer_len()..]; // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[ieee_len..][..frag1_size]); + tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); Ok(()) }, - )?; - - let mut written = ieee_len + frag1_size; - - // We need to save the the rest of the packets into the `out_fragments` buffer. - while written < total_size { - out_fragments - .enqueue_one_with::<'_, (), Error, _>(|(len, tx_buf)| { - let mut tx_buf = &mut tx_buf[..]; - - // Modify the sequence number of the IEEE header - ieee_repr.sequence_number = Some(self.get_sequence_number()); - let mut ieee_packet = - Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - // Add the next fragment header - let datagram_offset = ((40 + written - ieee_len) / 8) as u8; - fragn.set_offset(datagram_offset); - let mut frag_packet = - SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); - fragn.emit(&mut frag_packet); - tx_buf = &mut tx_buf[fragn.buffer_len()..]; - - // Add the buffer part - let frag_size = (total_size - written).min(fragn_size); - tx_buf[..frag_size].copy_from_slice(&buffer[written..][..frag_size]); - written += frag_size; - - // Save the lenght of this packet. - *len = ieee_len + fragn.buffer_len() + frag_size; - Ok(()) - }) - .unwrap() - .unwrap(); - } - - Ok(()) + ) } else { - // No fragmentation is needed thus we can copy everything over that already has been - // emitted in the buffer. - tx_token.consume(self.now, total_size, |tx_buffer| { - tx_buffer.copy_from_slice(&buffer[..total_size]); + // We don't need fragmentation, so we emit everything to the TX token. + tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut tx_buf[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); + tx_buf = &mut tx_buf[iphc_repr.buffer_len()..]; + + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut tx_buf[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut tx_buf[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut tx_buf[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } Ok(()) }) } } + #[cfg(feature = "medium-ieee802154")] + fn dispatch_ieee802154_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut SixlowpanOutPacket, + ) -> Result<()> { + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = out_packet; + + // Create the IEEE802.15.4 header. + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(*ll_dst_addr), + src_pan_id: self.pan_id, + src_addr: Some(*ll_src_addr), + }; + + // Create the FRAG_N header. + let mut fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag: *datagram_tag, + offset: 0, + }; + + let ieee_len = ieee_repr.buffer_len(); + let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); + + tx_token.consume( + self.now, + ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, + |mut tx_buf| { + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the next fragment header + let datagram_offset = ((40 + *sent_bytes) / 8) as u8; + fragn.set_offset(datagram_offset); + let mut frag_packet = + SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); + fragn.emit(&mut frag_packet); + tx_buf = &mut tx_buf[fragn.buffer_len()..]; + + // Add the buffer part + tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); + + *sent_bytes += frag_size; + + Ok(()) + }, + ) + } + #[cfg(feature = "proto-igmp")] fn igmp_report_packet<'any>( &self, @@ -3069,7 +3241,7 @@ mod test { .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .out_fragments_cache(RingBuffer::new(vec![])) + .sixlowpan_out_packet_cache(vec![]) .ip_addrs(ip_addrs); #[cfg(not(feature = "proto-sixlowpan"))] diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index b266156aa..b760983a5 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -33,6 +33,12 @@ impl RawSocket { let mut mtu = lower.interface_mtu()?; + // FIXME(thvdveld): this is a workaround for https://github.com/smoltcp-rs/smoltcp/issues/622 + #[cfg(feature = "medium-ieee802154")] + if medium == Medium::Ieee802154 { + mtu += 2; + } + #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) From d7e8278a06c8f762fe2de9c3d686ba59b48e36f9 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Wed, 1 Jun 2022 16:17:24 -0700 Subject: [PATCH 345/566] Add send_with to udp, raw, and icmp sockets --- src/iface/socket_meta.rs | 23 +++++++++-------- src/socket/icmp.rs | 24 +++++++++++++++++ src/socket/raw.rs | 25 ++++++++++++++++++ src/socket/udp.rs | 40 +++++++++++++++++++++++++++++ src/storage/packet_buffer.rs | 50 ++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 10 deletions(-) diff --git a/src/iface/socket_meta.rs b/src/iface/socket_meta.rs index b2fba8b82..a48f08bd8 100644 --- a/src/iface/socket_meta.rs +++ b/src/iface/socket_meta.rs @@ -1,19 +1,21 @@ use super::SocketHandle; -use crate::socket::PollAt; -use crate::time::{Duration, Instant}; -use crate::wire::IpAddress; +use crate::{ + socket::PollAt, + time::{Duration, Instant}, + wire::IpAddress, +}; /// Neighbor dependency. /// -/// This enum tracks whether the socket should be polled based on the neighbor it is -/// going to send packets to. +/// This enum tracks whether the socket should be polled based on the neighbor +/// it is going to send packets to. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum NeighborState { /// Socket can be polled immediately. Active, - /// Socket should not be polled until either `silent_until` passes or `neighbor` appears - /// in the neighbor cache. + /// Socket should not be polled until either `silent_until` passes or + /// `neighbor` appears in the neighbor cache. Waiting { neighbor: IpAddress, silent_until: Instant, @@ -29,7 +31,8 @@ impl Default for NeighborState { /// Network socket metadata. /// /// This includes things that only external (to the socket, that is) code -/// is interested in, but which are more conveniently stored inside the socket itself. +/// is interested in, but which are more conveniently stored inside the socket +/// itself. #[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct Meta { @@ -41,8 +44,8 @@ pub(crate) struct Meta { } impl Meta { - /// Minimum delay between neighbor discovery requests for this particular socket, - /// in milliseconds. + /// Minimum delay between neighbor discovery requests for this particular + /// socket, in milliseconds. /// /// See also `iface::NeighborCache::SILENT_TIME`. pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(1_000); diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index f1416ccc1..a187330b8 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -310,6 +310,30 @@ impl<'a> Socket<'a> { Ok(packet_buf) } + /// Enqueue a packet to be send to a given remote address and pass the buffer + /// to the provided closure. The closure then returns the size of the data written + /// into the buffer. + /// + /// Also see [send](#method.send). + pub fn send_with(&mut self, max_size: usize, endpoint: IpAddress, f: F) -> Result<&mut [u8]> + where + F: FnOnce(&mut [u8]) -> usize, + { + if endpoint.is_unspecified() { + return Err(Error::Unaddressable); + } + + let (size, packet_buf) = + self.tx_buffer + .enqueue_with_infallible(max_size, endpoint, |data| { + let size = f(data); + (size, &mut data[..size]) + })?; + + net_trace!("icmp:{}: buffer to send {} octets", endpoint, size); + Ok(packet_buf) + } + /// Enqueue a packet to be sent to a given remote address, and fill it from a slice. /// /// See also [send](#method.send). diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 0ce357cf3..8a87cf4b5 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -188,6 +188,31 @@ impl<'a> Socket<'a> { Ok(packet_buf) } + /// Enqueue a packet to be send and pass the buffer to the provided closure. + /// The closure then returns the size of the data written into the buffer. + /// + /// Also see [send](#method.send). + pub fn send_with(&mut self, max_size: usize, f: F) -> Result<&mut [u8]> + where + F: FnOnce(&mut [u8]) -> usize, + { + let (size, packet_buf) = self + .tx_buffer + .enqueue_with_infallible(max_size, (), |data| { + let size = f(data); + (size, &mut data[..size]) + })?; + + net_trace!( + "raw:{}:{}: buffer to send {} octets", + self.ip_version, + self.ip_protocol, + size + ); + + Ok(packet_buf) + } + /// Enqueue a packet to send, and fill it from a slice. /// /// See also [send](#method.send). diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 135585322..c93b87fee 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -258,6 +258,46 @@ impl<'a> Socket<'a> { Ok(payload_buf) } + /// Enqueue a packet to be send to a given remote endpoint and pass the buffer + /// to the provided closure. The closure then returns the size of the data written + /// into the buffer. + /// + /// Also see [send](#method.send). + pub fn send_with( + &mut self, + max_size: usize, + remote_endpoint: IpEndpoint, + f: F, + ) -> Result<&mut [u8]> + where + F: FnOnce(&mut [u8]) -> usize, + { + if self.endpoint.port == 0 { + return Err(Error::Unaddressable); + } + if remote_endpoint.addr.is_unspecified() { + return Err(Error::Unaddressable); + } + if remote_endpoint.port == 0 { + return Err(Error::Unaddressable); + } + + let (size, payload_buf) = + self.tx_buffer + .enqueue_with_infallible(max_size, remote_endpoint, |data| { + let size = f(data); + (size, &mut data[..size]) + })?; + + net_trace!( + "udp:{}:{}: buffer to send {} octets", + self.endpoint, + remote_endpoint, + size + ); + Ok(payload_buf) + } + /// Enqueue a packet to be sent to a given remote endpoint, and fill it from a slice. /// /// See also [send](#method.send). diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 6be763723..b41e502a0 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -111,6 +111,56 @@ impl<'a, H> PacketBuffer<'a, H> { Ok(payload_buf) } + /// Call `f` with a packet from the buffer large enough to fit `max_size` bytes. The packet + /// is shrunk to the size returned from `f` and enqueued into the buffer. + pub fn enqueue_with_infallible<'b, R, F>( + &'b mut self, + max_size: usize, + header: H, + f: F, + ) -> Result<(usize, R)> + where + F: FnOnce(&'b mut [u8]) -> (usize, R), + { + if self.payload_ring.capacity() < max_size { + return Err(Error::Truncated); + } + + if self.metadata_ring.is_full() { + return Err(Error::Exhausted); + } + + let window = self.payload_ring.window(); + let contig_window = self.payload_ring.contiguous_window(); + + if window < max_size { + return Err(Error::Exhausted); + } else if contig_window < max_size { + if window - contig_window < max_size { + // The buffer length is larger than the current contiguous window + // and is larger than the contiguous window will be after adding + // the padding necessary to circle around to the beginning of the + // ring buffer. + return Err(Error::Exhausted); + } else { + // Add padding to the end of the ring buffer so that the + // contiguous window is at the beginning of the ring buffer. + *self.metadata_ring.enqueue_one()? = PacketMetadata::padding(contig_window); + // note(discard): function does not write to the result + // enqueued padding buffer location + let _buf_enqueued = self.payload_ring.enqueue_many(contig_window); + } + } + + let (size, r) = self + .payload_ring + .enqueue_many_with(|data| f(&mut data[..max_size])); + + *self.metadata_ring.enqueue_one()? = PacketMetadata::packet(size, header); + + Ok((size, r)) + } + fn dequeue_padding(&mut self) { let Self { ref mut metadata_ring, From 4af0b4b932a149ba63ee9aabf744177bf9ac6618 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Thu, 2 Jun 2022 09:05:36 -0700 Subject: [PATCH 346/566] Fix after rebasing --- src/socket/icmp.rs | 22 ++++++++++++++-------- src/socket/raw.rs | 5 +++-- src/socket/udp.rs | 21 +++++++++++---------- src/storage/packet_buffer.rs | 14 +++++--------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index a187330b8..c6b4199cb 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -315,20 +315,26 @@ impl<'a> Socket<'a> { /// into the buffer. /// /// Also see [send](#method.send). - pub fn send_with(&mut self, max_size: usize, endpoint: IpAddress, f: F) -> Result<&mut [u8]> + pub fn send_with( + &mut self, + max_size: usize, + endpoint: IpAddress, + f: F, + ) -> Result<&mut [u8], SendError> where F: FnOnce(&mut [u8]) -> usize, { if endpoint.is_unspecified() { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } - let (size, packet_buf) = - self.tx_buffer - .enqueue_with_infallible(max_size, endpoint, |data| { - let size = f(data); - (size, &mut data[..size]) - })?; + let (size, packet_buf) = self + .tx_buffer + .enqueue_with_infallible(max_size, endpoint, |data| { + let size = f(data); + (size, &mut data[..size]) + }) + .map_err(|_| SendError::BufferFull)?; net_trace!("icmp:{}: buffer to send {} octets", endpoint, size); Ok(packet_buf) diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 8a87cf4b5..b4f0d1788 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -192,7 +192,7 @@ impl<'a> Socket<'a> { /// The closure then returns the size of the data written into the buffer. /// /// Also see [send](#method.send). - pub fn send_with(&mut self, max_size: usize, f: F) -> Result<&mut [u8]> + pub fn send_with(&mut self, max_size: usize, f: F) -> Result<&mut [u8], SendError> where F: FnOnce(&mut [u8]) -> usize, { @@ -201,7 +201,8 @@ impl<'a> Socket<'a> { .enqueue_with_infallible(max_size, (), |data| { let size = f(data); (size, &mut data[..size]) - })?; + }) + .map_err(|_| SendError::BufferFull)?; net_trace!( "raw:{}:{}: buffer to send {} octets", diff --git a/src/socket/udp.rs b/src/socket/udp.rs index c93b87fee..1f775c232 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -268,26 +268,27 @@ impl<'a> Socket<'a> { max_size: usize, remote_endpoint: IpEndpoint, f: F, - ) -> Result<&mut [u8]> + ) -> Result<&mut [u8], SendError> where F: FnOnce(&mut [u8]) -> usize, { if self.endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } if remote_endpoint.addr.is_unspecified() { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } if remote_endpoint.port == 0 { - return Err(Error::Unaddressable); + return Err(SendError::Unaddressable); } - let (size, payload_buf) = - self.tx_buffer - .enqueue_with_infallible(max_size, remote_endpoint, |data| { - let size = f(data); - (size, &mut data[..size]) - })?; + let (size, payload_buf) = self + .tx_buffer + .enqueue_with_infallible(max_size, remote_endpoint, |data| { + let size = f(data); + (size, &mut data[..size]) + }) + .map_err(|_| SendError::BufferFull)?; net_trace!( "udp:{}:{}: buffer to send {} octets", diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index b41e502a0..b841d4dab 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -118,30 +118,26 @@ impl<'a, H> PacketBuffer<'a, H> { max_size: usize, header: H, f: F, - ) -> Result<(usize, R)> + ) -> Result<(usize, R), Full> where F: FnOnce(&'b mut [u8]) -> (usize, R), { - if self.payload_ring.capacity() < max_size { - return Err(Error::Truncated); - } - - if self.metadata_ring.is_full() { - return Err(Error::Exhausted); + if self.payload_ring.capacity() < max_size || self.metadata_ring.is_full() { + return Err(Full); } let window = self.payload_ring.window(); let contig_window = self.payload_ring.contiguous_window(); if window < max_size { - return Err(Error::Exhausted); + return Err(Full); } else if contig_window < max_size { if window - contig_window < max_size { // The buffer length is larger than the current contiguous window // and is larger than the contiguous window will be after adding // the padding necessary to circle around to the beginning of the // ring buffer. - return Err(Error::Exhausted); + return Err(Full); } else { // Add padding to the end of the ring buffer so that the // contiguous window is at the beginning of the ring buffer. From 422a60e6ae0ca08030a52224b1e5d764e3b48c50 Mon Sep 17 00:00:00 2001 From: ngc0202 Date: Sun, 8 May 2022 20:18:20 -0400 Subject: [PATCH 347/566] Finish ICMPv4 TimeExceeded support Will now Display and PrettyPrint properly. Structure is exactly the same as Destination Unreachable so this mostly mirrors that implementation. --- src/wire/icmpv4.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 864081992..3814bec63 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -138,6 +138,16 @@ enum_with_unknown! { } } +impl fmt::Display for TimeExceeded { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + TimeExceeded::TtlExpired => write!(f, "time-to-live exceeded in transit"), + TimeExceeded::FragExpired => write!(f, "fragment reassembly time exceeded"), + TimeExceeded::Unknown(id) => write!(f, "{}", id), + } + } +} + enum_with_unknown! { /// Internet protocol control message subtype for type "Parameter Problem". pub enum ParamProblem(u8) { @@ -372,6 +382,11 @@ pub enum Repr<'a> { header: Ipv4Repr, data: &'a [u8], }, + TimeExceeded { + reason: TimeExceeded, + header: Ipv4Repr, + data: &'a [u8], + }, } impl<'a> Repr<'a> { @@ -424,6 +439,30 @@ impl<'a> Repr<'a> { data: payload, }) } + + (Message::TimeExceeded, code) => { + let ip_packet = Ipv4Packet::new_checked(packet.data())?; + + let payload = &packet.data()[ip_packet.header_len() as usize..]; + // RFC 792 requires exactly eight bytes to be returned. + // We allow more, since there isn't a reason not to, but require at least eight. + if payload.len() < 8 { + return Err(Error); + } + + Ok(Repr::TimeExceeded { + reason: TimeExceeded::from(code), + header: Ipv4Repr { + src_addr: ip_packet.src_addr(), + dst_addr: ip_packet.dst_addr(), + next_header: ip_packet.next_header(), + payload_len: payload.len(), + hop_limit: ip_packet.hop_limit(), + }, + data: payload, + }) + } + _ => Err(Error), } } @@ -434,7 +473,8 @@ impl<'a> Repr<'a> { &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() } - &Repr::DstUnreachable { header, data, .. } => { + &Repr::DstUnreachable { header, data, .. } + | &Repr::TimeExceeded { header, data, .. } => { field::UNUSED.end + header.buffer_len() + data.len() } } @@ -487,6 +527,20 @@ impl<'a> Repr<'a> { let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; payload.copy_from_slice(data) } + + Repr::TimeExceeded { + reason, + header, + data, + } => { + packet.set_msg_type(Message::TimeExceeded); + packet.set_msg_code(reason.into()); + + let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut()); + header.emit(&mut ip_packet, checksum_caps); + let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; + payload.copy_from_slice(data) + } } if checksum_caps.icmpv4.tx() { @@ -510,6 +564,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { Message::DstUnreachable => { write!(f, " code={:?}", DstUnreachable::from(self.msg_code())) } + Message::TimeExceeded => { + write!(f, " code={:?}", TimeExceeded::from(self.msg_code())) + } _ => write!(f, " code={}", self.msg_code()), } } @@ -545,6 +602,9 @@ impl<'a> fmt::Display for Repr<'a> { Repr::DstUnreachable { reason, .. } => { write!(f, "ICMPv4 destination unreachable ({})", reason) } + Repr::TimeExceeded { reason, .. } => { + write!(f, "ICMPv4 time exceeded ({})", reason) + } } } } @@ -564,7 +624,7 @@ impl> PrettyPrint for Packet { write!(f, "{}{}", indent, packet)?; match packet.msg_type() { - Message::DstUnreachable => { + Message::DstUnreachable | Message::TimeExceeded => { indent.increase(f)?; super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent) } From fe5164d310772129d256cb6710384af3114cc252 Mon Sep 17 00:00:00 2001 From: ngc0202 Date: Fri, 20 May 2022 12:09:09 -0400 Subject: [PATCH 348/566] Enable TimeExceeded processing in socket, fix wrong addresses used Updates IcmpSocket::accepts to accept packets of type Icmpv4Repr::TimeExceeded and Icmpv6Repr::TimeExceeded. Fixes apparent bug where the UdpRepr contained in the DstUnreachable and TimeExceeded packets were built using the wrong source and dest IP addresses. --- src/socket/icmp.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index f1416ccc1..6612270e2 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -350,18 +350,22 @@ impl<'a> Socket<'a> { pub(crate) fn accepts(&self, cx: &mut 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 - // a UDP packet send from the local port we are bound to. + // accept Destination Unreachable or Time Exceeded messages with + // the data containing a UDP packet send from the local port we + // are bound to. #[cfg(feature = "proto-ipv4")] ( &Endpoint::Udp(endpoint), - &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }), + &IcmpRepr::Ipv4( + Icmpv4Repr::DstUnreachable { data, header, .. } + | Icmpv4Repr::TimeExceeded { data, header, .. }, + ), ) if endpoint.addr.is_none() || endpoint.addr == Some(ip_repr.dst_addr()) => { let packet = UdpPacket::new_unchecked(data); match UdpRepr::parse( &packet, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), + &header.src_addr.into(), + &header.dst_addr.into(), &cx.checksum_caps(), ) { Ok(repr) => endpoint.port == repr.src_port, @@ -371,13 +375,16 @@ impl<'a> Socket<'a> { #[cfg(feature = "proto-ipv6")] ( &Endpoint::Udp(endpoint), - &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }), + &IcmpRepr::Ipv6( + Icmpv6Repr::DstUnreachable { data, header, .. } + | Icmpv6Repr::TimeExceeded { data, header, .. }, + ), ) if endpoint.addr.is_none() || endpoint.addr == Some(ip_repr.dst_addr()) => { let packet = UdpPacket::new_unchecked(data); match UdpRepr::parse( &packet, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), + &header.src_addr.into(), + &header.dst_addr.into(), &cx.checksum_caps(), ) { Ok(repr) => endpoint.port == repr.src_port, From e9929f486f57fe65f5dfe25dc445f3fc239ab1da Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Fri, 3 Jun 2022 11:25:10 -0700 Subject: [PATCH 349/566] Simplify send_with functions --- src/socket/icmp.rs | 11 ++++------- src/socket/raw.rs | 11 ++++------- src/socket/udp.rs | 11 ++++------- src/storage/packet_buffer.rs | 12 ++++++------ 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index c6b4199cb..301fe64ff 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -320,7 +320,7 @@ impl<'a> Socket<'a> { max_size: usize, endpoint: IpAddress, f: F, - ) -> Result<&mut [u8], SendError> + ) -> Result where F: FnOnce(&mut [u8]) -> usize, { @@ -328,16 +328,13 @@ impl<'a> Socket<'a> { return Err(SendError::Unaddressable); } - let (size, packet_buf) = self + let size = self .tx_buffer - .enqueue_with_infallible(max_size, endpoint, |data| { - let size = f(data); - (size, &mut data[..size]) - }) + .enqueue_with_infallible(max_size, endpoint, f) .map_err(|_| SendError::BufferFull)?; net_trace!("icmp:{}: buffer to send {} octets", endpoint, size); - Ok(packet_buf) + Ok(size) } /// Enqueue a packet to be sent to a given remote address, and fill it from a slice. diff --git a/src/socket/raw.rs b/src/socket/raw.rs index b4f0d1788..08c3ddfd1 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -192,16 +192,13 @@ impl<'a> Socket<'a> { /// The closure then returns the size of the data written into the buffer. /// /// Also see [send](#method.send). - pub fn send_with(&mut self, max_size: usize, f: F) -> Result<&mut [u8], SendError> + pub fn send_with(&mut self, max_size: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> usize, { - let (size, packet_buf) = self + let size = self .tx_buffer - .enqueue_with_infallible(max_size, (), |data| { - let size = f(data); - (size, &mut data[..size]) - }) + .enqueue_with_infallible(max_size, (), f) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -211,7 +208,7 @@ impl<'a> Socket<'a> { size ); - Ok(packet_buf) + Ok(size) } /// Enqueue a packet to send, and fill it from a slice. diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 1f775c232..20e19d3f7 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -268,7 +268,7 @@ impl<'a> Socket<'a> { max_size: usize, remote_endpoint: IpEndpoint, f: F, - ) -> Result<&mut [u8], SendError> + ) -> Result where F: FnOnce(&mut [u8]) -> usize, { @@ -282,12 +282,9 @@ impl<'a> Socket<'a> { return Err(SendError::Unaddressable); } - let (size, payload_buf) = self + let size = self .tx_buffer - .enqueue_with_infallible(max_size, remote_endpoint, |data| { - let size = f(data); - (size, &mut data[..size]) - }) + .enqueue_with_infallible(max_size, remote_endpoint, f) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -296,7 +293,7 @@ impl<'a> Socket<'a> { remote_endpoint, size ); - Ok(payload_buf) + Ok(size) } /// Enqueue a packet to be sent to a given remote endpoint, and fill it from a slice. diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index b841d4dab..287ab78ee 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -113,14 +113,14 @@ impl<'a, H> PacketBuffer<'a, H> { /// Call `f` with a packet from the buffer large enough to fit `max_size` bytes. The packet /// is shrunk to the size returned from `f` and enqueued into the buffer. - pub fn enqueue_with_infallible<'b, R, F>( + pub fn enqueue_with_infallible<'b, F>( &'b mut self, max_size: usize, header: H, f: F, - ) -> Result<(usize, R), Full> + ) -> Result where - F: FnOnce(&'b mut [u8]) -> (usize, R), + F: FnOnce(&'b mut [u8]) -> usize, { if self.payload_ring.capacity() < max_size || self.metadata_ring.is_full() { return Err(Full); @@ -148,13 +148,13 @@ impl<'a, H> PacketBuffer<'a, H> { } } - let (size, r) = self + let (size, _) = self .payload_ring - .enqueue_many_with(|data| f(&mut data[..max_size])); + .enqueue_many_with(|data| (f(&mut data[..max_size]), ())); *self.metadata_ring.enqueue_one()? = PacketMetadata::packet(size, header); - Ok((size, r)) + Ok(size) } fn dequeue_padding(&mut self) { From b71ffcb6ccff29cde3108a046dfcbc2c3dcdfdce Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 21 May 2022 02:40:15 +0200 Subject: [PATCH 350/566] iface: borrow the SocketSet instead of owning. --- examples/benchmark.rs | 17 +-- examples/client.rs | 19 +-- examples/dhcp_client.rs | 13 +- examples/dns.rs | 19 +-- examples/httpclient.rs | 14 +- examples/loopback.rs | 20 +-- examples/multicast.rs | 18 +-- examples/ping.rs | 12 +- examples/server.rs | 29 ++-- examples/sixlowpan.rs | 19 +-- examples/sixlowpan_benchmark.rs | 17 +-- src/iface/interface.rs | 231 ++++++++++++-------------------- src/iface/mod.rs | 2 +- src/iface/socket_set.rs | 16 ++- src/lib.rs | 7 - 15 files changed, 208 insertions(+), 245 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index e4d3e27a0..c00f4f77e 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -11,7 +11,7 @@ use std::os::unix::io::AsRawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; @@ -94,7 +94,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) @@ -102,15 +102,16 @@ fn main() { } let mut iface = builder.finalize(); - let tcp1_handle = iface.add_socket(tcp1_socket); - let tcp2_handle = iface.add_socket(tcp2_socket); + let mut sockets = SocketSet::new(vec![]); + let tcp1_handle = sockets.add(tcp1_socket); + let tcp2_handle = sockets.add(tcp2_socket); let default_timeout = Some(Duration::from_millis(1000)); thread::spawn(move || client(mode)); let mut processed = 0; while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -118,7 +119,7 @@ fn main() { } // tcp:1234: emit data - let socket = iface.get_socket::(tcp1_handle); + let socket = sockets.get::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -136,7 +137,7 @@ fn main() { } // tcp:1235: sink data - let socket = iface.get_socket::(tcp2_handle); + let socket = sockets.get::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } @@ -153,7 +154,7 @@ fn main() { } } - match iface.poll_at(timestamp) { + match iface.poll_at(timestamp, &sockets) { Some(poll_at) if timestamp < poll_at => { phy_wait(fd, Some(poll_at - timestamp)).expect("wait error"); } diff --git a/examples/client.rs b/examples/client.rs index 6fa2dc7f7..88a3b01c1 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; @@ -42,7 +42,7 @@ fn main() { routes.add_default_ipv4_route(default_v4_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -52,22 +52,25 @@ fn main() { } let mut iface = builder.finalize(); - let tcp_handle = iface.add_socket(tcp_socket); + let mut sockets = SocketSet::new(vec![]); + let tcp_handle = sockets.add(tcp_socket); - let (socket, cx) = iface.get_socket_and_context::(tcp_handle); - socket.connect(cx, (address, port), 49500).unwrap(); + let socket = sockets.get::(tcp_handle); + socket + .connect(iface.context(), (address, port), 49500) + .unwrap(); let mut tcp_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } } - let socket = iface.get_socket::(tcp_handle); + let socket = sockets.get::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { @@ -104,6 +107,6 @@ fn main() { socket.close(); } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 72c26115a..65abd3d26 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -5,7 +5,7 @@ use log::*; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::socket::dhcpv4; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; @@ -34,7 +34,7 @@ fn main() { let routes = Routes::new(&mut routes_storage[..]); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -52,15 +52,16 @@ fn main() { // IMPORTANT: This should be removed in production. dhcp_socket.set_max_lease_duration(Some(Duration::from_secs(10))); - let dhcp_handle = iface.add_socket(dhcp_socket); + let mut sockets = SocketSet::new(vec![]); + let dhcp_handle = sockets.add(dhcp_socket); loop { let timestamp = Instant::now(); - if let Err(e) = iface.poll(timestamp) { + if let Err(e) = iface.poll(timestamp, &mut sockets) { debug!("poll error: {}", e); } - let event = iface.get_socket::(dhcp_handle).poll(); + let event = sockets.get::(dhcp_handle).poll(); match event { None => {} Some(dhcpv4::Event::Configured(config)) => { @@ -90,7 +91,7 @@ fn main() { } } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/dns.rs b/examples/dns.rs index c3b8cd3a1..1456befcc 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -7,7 +7,7 @@ extern crate smoltcp; mod utils; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::Device; use smoltcp::phy::{wait as phy_wait, Medium}; use smoltcp::socket::dns::{self, GetQueryResultError}; @@ -55,7 +55,7 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -65,24 +65,25 @@ fn main() { } let mut iface = builder.finalize(); - let dns_handle = iface.add_socket(dns_socket); + let mut sockets = SocketSet::new(vec![]); + let dns_handle = sockets.add(dns_socket); - let (socket, cx) = iface.get_socket_and_context::(dns_handle); - let query = socket.start_query(cx, name).unwrap(); + let socket = sockets.get::(dns_handle); + let query = socket.start_query(iface.context(), name).unwrap(); loop { let timestamp = Instant::now(); debug!("timestamp {:?}", timestamp); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } } - match iface - .get_socket::(dns_handle) + match sockets + .get::(dns_handle) .get_query_result(query) { Ok(addrs) => { @@ -93,6 +94,6 @@ fn main() { Err(e) => panic!("query failed: {:?}", e), } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/httpclient.rs b/examples/httpclient.rs index bda9b9510..9f7f80067 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; use url::Url; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; @@ -48,7 +48,7 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -58,7 +58,8 @@ fn main() { } let mut iface = builder.finalize(); - let tcp_handle = iface.add_socket(tcp_socket); + let mut sockets = SocketSet::new(vec![]); + let tcp_handle = sockets.add(tcp_socket); enum State { Connect, @@ -69,14 +70,15 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } } - let (socket, cx) = iface.get_socket_and_context::(tcp_handle); + let socket = sockets.get::(tcp_handle); + let cx = iface.context(); state = match state { State::Connect if !socket.is_active() => { @@ -115,6 +117,6 @@ fn main() { _ => state, }; - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/loopback.rs b/examples/loopback.rs index 8fe5150a0..2e3f7084b 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -9,7 +9,7 @@ mod utils; use core::str; use log::{debug, error, info}; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{Loopback, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; @@ -86,8 +86,7 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut sockets: [_; 2] = Default::default(); - let mut iface = InterfaceBuilder::new(device, &mut sockets[..]) + let mut iface = InterfaceBuilder::new(device) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) @@ -113,21 +112,23 @@ fn main() { tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer) }; - let server_handle = iface.add_socket(server_socket); - let client_handle = iface.add_socket(client_socket); + let mut sockets: [_; 2] = Default::default(); + let mut sockets = SocketSet::new(&mut sockets[..]); + let server_handle = sockets.add(server_socket); + let client_handle = sockets.add(client_socket); let mut did_listen = false; let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(10_000) { - match iface.poll(clock.elapsed()) { + match iface.poll(clock.elapsed(), &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } } - let mut socket = iface.get_socket::(server_handle); + let mut socket = sockets.get::(server_handle); if !socket.is_active() && !socket.is_listening() { if !did_listen { debug!("listening"); @@ -145,7 +146,8 @@ fn main() { done = true; } - let (mut socket, cx) = iface.get_socket_and_context::(client_handle); + let mut socket = sockets.get::(client_handle); + let cx = iface.context(); if !socket.is_open() { if !did_connect { debug!("connecting"); @@ -162,7 +164,7 @@ fn main() { socket.close(); } - match iface.poll_delay(clock.elapsed()) { + match iface.poll_delay(clock.elapsed(), &sockets) { Some(Duration::ZERO) => debug!("resuming"), Some(delay) => { debug!("sleeping for {} ms", delay); diff --git a/examples/multicast.rs b/examples/multicast.rs index 6b94b1326..c18659b16 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -4,7 +4,7 @@ use log::debug; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::wait as phy_wait; use smoltcp::socket::{raw, udp}; use smoltcp::time::Instant; @@ -34,7 +34,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; - let mut iface = InterfaceBuilder::new(device, vec![]) + let mut iface = InterfaceBuilder::new(device) .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) @@ -47,6 +47,8 @@ fn main() { .join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now) .unwrap(); + let mut sockets = SocketSet::new(vec![]); + // Must fit at least one IGMP packet let raw_rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; 2], vec![0; 512]); // Will not send IGMP @@ -57,25 +59,25 @@ fn main() { raw_rx_buffer, raw_tx_buffer, ); - let raw_handle = iface.add_socket(raw_socket); + let raw_handle = sockets.add(raw_socket); // Must fit mDNS payload of at least one packet let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY; 4], vec![0; 1024]); // Will not send mDNS let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 0]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let udp_handle = iface.add_socket(udp_socket); + let udp_handle = sockets.add(udp_socket); loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); } } - let socket = iface.get_socket::(raw_handle); + let socket = sockets.get::(raw_handle); if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets @@ -92,7 +94,7 @@ fn main() { } } - let socket = iface.get_socket::(udp_handle); + let socket = sockets.get::(udp_handle); if !socket.is_open() { socket.bind(MDNS_PORT).unwrap() } @@ -106,6 +108,6 @@ fn main() { .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/ping.rs b/examples/ping.rs index cf240550d..411d9ca0b 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -2,6 +2,7 @@ mod utils; use byteorder::{ByteOrder, NetworkEndian}; use log::debug; +use smoltcp::iface::SocketSet; use std::cmp; use std::collections::BTreeMap; use std::collections::HashMap; @@ -127,7 +128,7 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .routes(routes); if medium == Medium::Ethernet { @@ -137,7 +138,8 @@ fn main() { } let mut iface = builder.finalize(); - let icmp_handle = iface.add_socket(icmp_socket); + let mut sockets = SocketSet::new(vec![]); + let icmp_handle = sockets.add(icmp_socket); let mut send_at = Instant::from_millis(0); let mut seq_no = 0; @@ -148,7 +150,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -156,7 +158,7 @@ fn main() { } let timestamp = Instant::now(); - let socket = iface.get_socket::(icmp_handle); + let socket = sockets.get::(icmp_handle); if !socket.is_open() { socket.bind(icmp::Endpoint::Ident(ident)).unwrap(); send_at = timestamp; @@ -255,7 +257,7 @@ fn main() { } let timestamp = Instant::now(); - match iface.poll_at(timestamp) { + match iface.poll_at(timestamp, &sockets) { Some(poll_at) if timestamp < poll_at => { let resume_at = cmp::min(poll_at, send_at); phy_wait(fd, Some(resume_at - timestamp)).expect("wait error"); diff --git a/examples/server.rs b/examples/server.rs index a5db0a499..0565eb33a 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{tcp, udp}; use smoltcp::time::{Duration, Instant}; @@ -54,7 +54,7 @@ fn main() { ]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) @@ -62,16 +62,17 @@ fn main() { } let mut iface = builder.finalize(); - let udp_handle = iface.add_socket(udp_socket); - let tcp1_handle = iface.add_socket(tcp1_socket); - let tcp2_handle = iface.add_socket(tcp2_socket); - let tcp3_handle = iface.add_socket(tcp3_socket); - let tcp4_handle = iface.add_socket(tcp4_socket); + let mut sockets = SocketSet::new(vec![]); + let udp_handle = sockets.add(udp_socket); + let tcp1_handle = sockets.add(tcp1_socket); + let tcp2_handle = sockets.add(tcp2_socket); + let tcp3_handle = sockets.add(tcp3_socket); + let tcp4_handle = sockets.add(tcp4_socket); let mut tcp_6970_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -79,7 +80,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = iface.get_socket::(udp_handle); + let socket = sockets.get::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -105,7 +106,7 @@ fn main() { } // tcp:6969: respond "hello" - let socket = iface.get_socket::(tcp1_handle); + let socket = sockets.get::(tcp1_handle); if !socket.is_open() { socket.listen(6969).unwrap(); } @@ -118,7 +119,7 @@ fn main() { } // tcp:6970: echo with reverse - let socket = iface.get_socket::(tcp2_handle); + let socket = sockets.get::(tcp2_handle); if !socket.is_open() { socket.listen(6970).unwrap() } @@ -160,7 +161,7 @@ fn main() { } // tcp:6971: sinkhole - let socket = iface.get_socket::(tcp3_handle); + let socket = sockets.get::(tcp3_handle); if !socket.is_open() { socket.listen(6971).unwrap(); socket.set_keep_alive(Some(Duration::from_millis(1000))); @@ -181,7 +182,7 @@ fn main() { } // tcp:6972: fountain - let socket = iface.get_socket::(tcp4_handle); + let socket = sockets.get::(tcp4_handle); if !socket.is_open() { socket.listen(6972).unwrap() } @@ -200,6 +201,6 @@ fn main() { .unwrap(); } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index ef33ecfd3..b60be2c5b 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -47,7 +47,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; @@ -89,7 +89,7 @@ fn main() { let mut out_packet_buffer = [0u8; 1280]; - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder @@ -99,10 +99,11 @@ fn main() { .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); let mut iface = builder.finalize(); - let udp_handle = iface.add_socket(udp_socket); - let tcp_handle = iface.add_socket(tcp_socket); + let mut sockets = SocketSet::new(vec![]); + let udp_handle = sockets.add(udp_socket); + let tcp_handle = sockets.add(tcp_socket); - let socket = iface.get_socket::(tcp_handle); + let socket = sockets.get::(tcp_handle); socket.listen(50000).unwrap(); let mut tcp_active = false; @@ -112,7 +113,7 @@ fn main() { let mut poll = true; while poll { - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(r) => poll = r, Err(e) => { debug!("poll error: {}", e); @@ -122,7 +123,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = iface.get_socket::(udp_handle); + let socket = sockets.get::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -148,7 +149,7 @@ fn main() { socket.send_slice(&buffer[..len], endpoint).unwrap(); } - let socket = iface.get_socket::(tcp_handle); + let socket = sockets.get::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { @@ -182,6 +183,6 @@ fn main() { socket.close(); } - phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error"); + phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); } } diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 2fa393665..34a71edc6 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -48,7 +48,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache}; +use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -167,7 +167,7 @@ fn main() { let cache = FragmentsCache::new(vec![], BTreeMap::new()); - let mut builder = InterfaceBuilder::new(device, vec![]) + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder @@ -177,8 +177,9 @@ fn main() { .sixlowpan_out_packet_cache(vec![]); let mut iface = builder.finalize(); - let tcp1_handle = iface.add_socket(tcp1_socket); - let tcp2_handle = iface.add_socket(tcp2_socket); + let mut sockets = SocketSet::new(vec![]); + let tcp1_handle = sockets.add(tcp1_socket); + let tcp2_handle = sockets.add(tcp2_socket); let default_timeout = Some(Duration::from_millis(1000)); @@ -187,7 +188,7 @@ fn main() { while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp) { + match iface.poll(timestamp, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); @@ -195,7 +196,7 @@ fn main() { } // tcp:1234: emit data - let socket = iface.get_socket::(tcp1_handle); + let socket = sockets.get::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -211,7 +212,7 @@ fn main() { } // tcp:1235: sink data - let socket = iface.get_socket::(tcp2_handle); + let socket = sockets.get::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } @@ -226,7 +227,7 @@ fn main() { processed += length; } - match iface.poll_at(timestamp) { + match iface.poll_at(timestamp, &sockets) { Some(poll_at) if timestamp < poll_at => { phy_wait(fd, Some(poll_at - timestamp)).expect("wait error"); } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index d4fd3f6a2..649522c18 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -8,7 +8,6 @@ use managed::{ManagedMap, ManagedSlice}; #[cfg(feature = "proto-sixlowpan")] use super::fragmentation::PacketAssemblerSet; use super::socket_set::SocketSet; -use super::{SocketHandle, SocketStorage}; use crate::iface::Routes; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::iface::{NeighborAnswer, NeighborCache}; @@ -99,7 +98,6 @@ macro_rules! check { /// a `&mut [T]`, or `Vec` if a heap is available. pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, - sockets: SocketSet<'a>, inner: InterfaceInner<'a>, fragments: FragmentsBuffer<'a>, out_packets: OutPackets<'a>, @@ -148,7 +146,6 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { #[cfg(feature = "medium-ieee802154")] pan_id: Option, ip_addrs: ManagedSlice<'a, IpCidr>, - sockets: SocketSet<'a>, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes<'a>, @@ -187,7 +184,7 @@ let neighbor_cache = // ... # NeighborCache::new(BTreeMap::new()); let ip_addrs = // ... # []; -let iface = InterfaceBuilder::new(device, vec![]) +let iface = InterfaceBuilder::new(device) .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) @@ -195,13 +192,9 @@ let iface = InterfaceBuilder::new(device, vec![]) ``` "## )] - pub fn new(device: DeviceT, sockets: SocketsT) -> Self - where - SocketsT: Into>>, - { + pub fn new(device: DeviceT) -> Self { InterfaceBuilder { device, - sockets: SocketSet::new(sockets), #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: None, @@ -434,7 +427,6 @@ let iface = InterfaceBuilder::new(device, vec![]) Interface { device: self.device, - sockets: self.sockets, fragments: FragmentsBuffer { #[cfg(feature = "proto-sixlowpan")] sixlowpan_fragments: self @@ -635,43 +627,11 @@ impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d>, { - /// Add a socket to the interface, and return its handle. - /// - /// # Panics - /// This function panics if the storage is fixed-size (not a `Vec`) and is full. - pub fn add_socket>(&mut self, socket: T) -> SocketHandle { - self.sockets.add(socket) - } - - /// Get a socket from the interface by its handle, as mutable. - /// - /// # Panics - /// This function may panic if the handle does not belong to this socket set - /// or the socket has the wrong type. - pub fn get_socket>(&mut self, handle: SocketHandle) -> &mut T { - self.sockets.get(handle) - } - - /// Get a socket by handle, and the socket context. + /// Get the socket context. /// /// The context is needed for some socket methods. - /// - /// # Panics - /// This function may panic if the handle does not belong to this socket set - /// or the socket has the wrong type. - pub fn get_socket_and_context>( - &mut self, - handle: SocketHandle, - ) -> (&mut T, &mut InterfaceInner<'a>) { - (self.sockets.get(handle), &mut self.inner) - } - - /// Remove a socket from the set, without changing its state. - /// - /// # Panics - /// This function may panic if the handle does not belong to this socket set. - pub fn remove_socket(&mut self, handle: SocketHandle) -> Socket<'a> { - self.sockets.remove(handle) + pub fn context(&mut self) -> &mut InterfaceInner<'a> { + &mut self.inner } /// Get the HardwareAddress address of the interface. @@ -732,18 +692,6 @@ where &mut self.device } - /// Get an iterator to the inner sockets. - pub fn sockets(&self) -> impl Iterator)> { - self.sockets.iter().map(|i| (i.meta.handle, &i.socket)) - } - - /// Get a mutable iterator to the inner sockets. - pub fn sockets_mut(&mut self) -> impl Iterator)> { - self.sockets - .iter_mut() - .map(|i| (i.meta.handle, &mut i.socket)) - } - /// Add an address to a list of subscribed multicast IP addresses. /// /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent` @@ -883,7 +831,7 @@ where /// packets containing any unsupported protocol, option, or form, which is /// a very common occurrence and on a production system it should not even /// be logged. - pub fn poll(&mut self, timestamp: Instant) -> Result { + pub fn poll(&mut self, timestamp: Instant, sockets: &mut SocketSet<'_>) -> Result { self.inner.now = timestamp; let mut readiness_may_have_changed = false; @@ -916,8 +864,8 @@ where } loop { - let processed_any = self.socket_ingress(); - let emitted_any = self.socket_egress(); + let processed_any = self.socket_ingress(sockets); + let emitted_any = self.socket_egress(sockets); #[cfg(feature = "proto-igmp")] self.igmp_egress()?; @@ -940,13 +888,13 @@ where /// /// [poll]: #method.poll /// [Instant]: struct.Instant.html - pub fn poll_at(&mut self, timestamp: Instant) -> Option { + pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { self.inner.now = timestamp; let inner = &mut self.inner; - self.sockets - .iter() + sockets + .items() .filter_map(move |item| { let socket_poll_at = item.socket.poll_at(inner); match item @@ -969,20 +917,19 @@ where /// /// [poll]: #method.poll /// [Duration]: struct.Duration.html - pub fn poll_delay(&mut self, timestamp: Instant) -> Option { - match self.poll_at(timestamp) { + pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { + match self.poll_at(timestamp, sockets) { Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), Some(_) => Some(Duration::from_millis(0)), _ => None, } } - fn socket_ingress(&mut self) -> bool { + fn socket_ingress(&mut self, sockets: &mut SocketSet<'_>) -> bool { let mut processed_any = false; let Self { device, inner, - sockets, fragments: _fragments, out_packets: _out_packets, } = self; @@ -1032,18 +979,17 @@ where processed_any } - fn socket_egress(&mut self) -> bool { + fn socket_egress(&mut self, sockets: &mut SocketSet<'_>) -> bool { let Self { device, inner, - sockets, out_packets: _out_packets, .. } = self; let _caps = device.capabilities(); let mut emitted_any = false; - for item in sockets.iter_mut() { + for item in sockets.items_mut() { if !item .meta .egress_permitted(inner.now, |ip_addr| inner.has_neighbor(&ip_addr)) @@ -1670,7 +1616,7 @@ impl<'a> InterfaceInner<'a> { // However, we cannot use that one because the payload passed to it is a // normal IPv6 UDP payload, which is not what we have here. for udp_socket in sockets - .iter_mut() + .items_mut() .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { if udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { @@ -1799,7 +1745,7 @@ impl<'a> InterfaceInner<'a> { // Pass every IP packet to all raw sockets we have registered. for raw_socket in sockets - .iter_mut() + .items_mut() .filter_map(|i| raw::Socket::downcast(&mut i.socket)) { if raw_socket.accepts(ip_repr) { @@ -1917,7 +1863,7 @@ impl<'a> InterfaceInner<'a> { && udp_packet.dst_port() == DHCP_CLIENT_PORT { if let Some(dhcp_socket) = sockets - .iter_mut() + .items_mut() .filter_map(|i| dhcpv4::Socket::downcast(&mut i.socket)) .next() { @@ -2096,7 +2042,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] for icmp_socket in _sockets - .iter_mut() + .items_mut() .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { @@ -2275,7 +2221,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] for icmp_socket in _sockets - .iter_mut() + .items_mut() .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) { if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { @@ -2400,7 +2346,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] for udp_socket in sockets - .iter_mut() + .items_mut() .filter_map(|i| udp::Socket::downcast(&mut i.socket)) { if udp_socket.accepts(self, &ip_repr, &udp_repr) { @@ -2411,7 +2357,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dns")] for dns_socket in sockets - .iter_mut() + .items_mut() .filter_map(|i| dns::Socket::downcast(&mut i.socket)) { if dns_socket.accepts(&ip_repr, &udp_repr) { @@ -2468,7 +2414,7 @@ impl<'a> InterfaceInner<'a> { )); for tcp_socket in sockets - .iter_mut() + .items_mut() .filter_map(|i| tcp::Socket::downcast(&mut i.socket)) { if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { @@ -3196,7 +3142,7 @@ mod test { } } - fn create_loopback<'a>() -> Interface<'a, Loopback> { + fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { #[cfg(feature = "medium-ethernet")] return create_loopback_ethernet(); #[cfg(not(feature = "medium-ethernet"))] @@ -3205,7 +3151,7 @@ mod test { #[cfg(all(feature = "medium-ip"))] #[allow(unused)] - fn create_loopback_ip<'a>() -> Interface<'a, Loopback> { + fn create_loopback_ip<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device let device = Loopback::new(Medium::Ip); let ip_addrs = [ @@ -3217,14 +3163,16 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - let iface_builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs); + let iface_builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - iface_builder.finalize() + let iface = iface_builder.finalize(); + + (iface, SocketSet::new(vec![])) } #[cfg(all(feature = "medium-ethernet"))] - fn create_loopback_ethernet<'a>() -> Interface<'a, Loopback> { + fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device let device = Loopback::new(Medium::Ethernet); let ip_addrs = [ @@ -3237,7 +3185,7 @@ mod test { ]; #[cfg(feature = "proto-sixlowpan")] - let iface_builder = InterfaceBuilder::new(device, vec![]) + let iface_builder = InterfaceBuilder::new(device) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) @@ -3245,14 +3193,16 @@ mod test { .ip_addrs(ip_addrs); #[cfg(not(feature = "proto-sixlowpan"))] - let iface_builder = InterfaceBuilder::new(device, vec![]) + let iface_builder = InterfaceBuilder::new(device) .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - iface_builder.finalize() + let iface = iface_builder.finalize(); + + (iface, SocketSet::new(vec![])) } #[cfg(feature = "proto-igmp")] @@ -3285,13 +3235,13 @@ mod test { #[should_panic(expected = "hardware_addr required option was not set")] #[cfg(all(feature = "medium-ethernet"))] fn test_builder_initialization_panic() { - InterfaceBuilder::new(Loopback::new(Medium::Ethernet), vec![]).finalize(); + InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); } #[test] #[cfg(feature = "proto-ipv4")] fn test_no_icmp_no_unicast_ipv4() { - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); // Unknown Ipv4 Protocol // @@ -3313,13 +3263,13 @@ 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 iface.sockets, &frame), None); + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); } #[test] #[cfg(feature = "proto-ipv6")] fn test_no_icmp_no_unicast_ipv6() { - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); // Unknown Ipv6 Protocol // @@ -3341,14 +3291,14 @@ 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 iface.sockets, &frame), None); + assert_eq!(iface.inner.process_ipv6(&mut sockets, &frame), None); } #[test] #[cfg(feature = "proto-ipv4")] fn test_icmp_error_no_payload() { static NO_BYTES: [u8; 0] = []; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { @@ -3391,7 +3341,7 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( - iface.inner.process_ipv4(&mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut sockets, &frame), Some(expected_repr) ); } @@ -3399,7 +3349,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv4")] fn test_local_subnet_broadcasts() { - let mut iface = create_loopback(); + let (mut iface, _) = create_loopback(); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); @@ -3456,7 +3406,7 @@ mod test { static UDP_PAYLOAD: [u8; 12] = [ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let mut udp_bytes_unicast = vec![0u8; 20]; let mut udp_bytes_broadcast = vec![0u8; 20]; @@ -3515,9 +3465,7 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( - iface - .inner - .process_udp(&mut iface.sockets, ip_repr, false, data), + iface.inner.process_udp(&mut sockets, ip_repr, false, data), Some(expected_repr) ); @@ -3543,12 +3491,9 @@ mod test { // 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 iface.sockets, - ip_repr, - false, - packet_broadcast.into_inner() - ), + iface + .inner + .process_udp(&mut sockets, ip_repr, false, packet_broadcast.into_inner()), None ); } @@ -3560,7 +3505,7 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); @@ -3570,7 +3515,7 @@ mod test { let mut udp_bytes = vec![0u8; 13]; let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); - let socket_handle = iface.add_socket(udp_socket); + let socket_handle = sockets.add(udp_socket); #[cfg(feature = "proto-ipv6")] let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -3600,7 +3545,7 @@ mod test { }); // Bind the socket to port 68 - let socket = iface.get_socket::(socket_handle); + let socket = sockets.get::(socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -3618,13 +3563,13 @@ mod test { assert_eq!( iface .inner - .process_udp(&mut iface.sockets, ip_repr, false, packet.into_inner()), + .process_udp(&mut sockets, ip_repr, false, packet.into_inner()), None ); // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer - let socket = iface.get_socket::(socket_handle); + let socket = sockets.get::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -3637,7 +3582,7 @@ mod test { fn test_handle_ipv4_broadcast() { use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let our_ipv4_addr = iface.ipv4_address().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); @@ -3689,7 +3634,7 @@ mod test { let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); assert_eq!( - iface.inner.process_ipv4(&mut iface.sockets, &frame), + iface.inner.process_ipv4(&mut sockets, &frame), Some(expected_packet) ); } @@ -3709,7 +3654,7 @@ mod test { #[cfg(feature = "proto-ipv6")] const MAX_PAYLOAD_LEN: usize = 1192; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let src_addr = Ipv4Address([192, 168, 1, 1]); @@ -3801,14 +3746,14 @@ mod test { assert_eq!( iface .inner - .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), + .process_udp(&mut sockets, ip_repr.into(), false, payload), Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) ); #[cfg(feature = "proto-ipv6")] assert_eq!( iface .inner - .process_udp(&mut iface.sockets, ip_repr.into(), false, payload), + .process_udp(&mut sockets, ip_repr.into(), false, payload), Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) ); } @@ -3816,7 +3761,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { - let mut iface = create_loopback_ethernet(); + let (mut iface, mut sockets) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3844,7 +3789,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner()), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3868,7 +3813,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { - let mut iface = create_loopback_ethernet(); + let (mut iface, mut sockets) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 86]; @@ -3919,7 +3864,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner()), Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected @@ -3940,7 +3885,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { - let mut iface = create_loopback_ethernet(); + let (mut iface, mut sockets) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3966,7 +3911,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner()), None ); @@ -3988,7 +3933,7 @@ mod test { not(feature = "medium-ieee802154") ))] fn test_arp_flush_after_update_ip() { - let mut iface = create_loopback_ethernet(); + let (mut iface, mut sockets) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -4018,7 +3963,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut iface.sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner()), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -4055,20 +4000,20 @@ mod test { fn test_icmpv4_socket() { use crate::wire::Icmpv4Packet; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); - let socket_handle = iface.add_socket(icmpv4_socket); + let socket_handle = sockets.add(icmpv4_socket); let ident = 0x1234; let seq_no = 0x5432; let echo_data = &[0xff; 16]; - let socket = iface.get_socket::(socket_handle); + let socket = sockets.get::(socket_handle); // Bind to the ID 0x1234 assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); @@ -4094,7 +4039,7 @@ mod test { // Open a socket and ensure the packet is handled due to the listening // socket. - assert!(!iface.get_socket::(socket_handle).can_recv()); + assert!(!sockets.get::(socket_handle).can_recv()); // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening let echo_reply = Icmpv4Repr::EchoReply { @@ -4108,13 +4053,11 @@ mod test { ..ipv4_repr }; assert_eq!( - iface - .inner - .process_icmpv4(&mut iface.sockets, ip_repr, icmp_data), + iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) ); - let socket = iface.get_socket::(socket_handle); + let socket = sockets.get::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -4128,7 +4071,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { - let mut iface = create_loopback(); + let (mut iface, _) = create_loopback(); let mut new_addrs = vec![ IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), @@ -4151,7 +4094,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_icmpv6_nxthdr_unknown() { - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -4206,7 +4149,7 @@ mod test { // 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 iface.sockets, &frame), + iface.inner.process_ipv6(&mut sockets, &frame), Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) ); } @@ -4248,7 +4191,7 @@ mod test { Ipv4Address::new(224, 0, 0, 56), ]; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); // Join multicast groups let timestamp = Instant::now(); @@ -4292,7 +4235,7 @@ 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(); + iface.socket_ingress(&mut sockets); // Leave multicast groups let timestamp = Instant::now(); @@ -4314,7 +4257,7 @@ mod test { fn test_raw_socket_no_reply() { use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let packets = 1; let rx_buffer = @@ -4324,7 +4267,7 @@ mod test { vec![0; 48 * packets], ); let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - iface.add_socket(raw_socket); + sockets.add(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); @@ -4371,7 +4314,7 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!(iface.inner.process_ipv4(&mut iface.sockets, &frame), None); + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); } #[test] @@ -4381,15 +4324,15 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let mut iface = create_loopback(); + let (mut iface, mut sockets) = create_loopback(); let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let udp_socket_handle = iface.add_socket(udp_socket); + let udp_socket_handle = sockets.add(udp_socket); // Bind the socket to port 68 - let socket = iface.get_socket::(udp_socket_handle); + let socket = sockets.get::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -4407,7 +4350,7 @@ mod test { raw_rx_buffer, raw_tx_buffer, ); - iface.add_socket(raw_socket); + sockets.add(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); @@ -4453,10 +4396,10 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!(iface.inner.process_ipv4(&mut iface.sockets, &frame), None); + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = iface.get_socket::(udp_socket_handle); + let socket = sockets.get::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 62be2d475..0d7c2e8b7 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -20,7 +20,7 @@ pub use self::neighbor::Cache as NeighborCache; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; -pub use socket_set::{SocketHandle, SocketStorage}; +pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "proto-sixlowpan")] pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as FragmentsCache}; diff --git a/src/iface/socket_set.rs b/src/iface/socket_set.rs index 72e274a70..92629c39a 100644 --- a/src/iface/socket_set.rs +++ b/src/iface/socket_set.rs @@ -39,7 +39,7 @@ impl fmt::Display for SocketHandle { /// /// The lifetime `'a` is used when storing a `Socket<'a>`. #[derive(Debug)] -pub(crate) struct SocketSet<'a> { +pub struct SocketSet<'a> { sockets: ManagedSlice<'a, SocketStorage<'a>>, } @@ -114,13 +114,23 @@ impl<'a> SocketSet<'a> { } } + /// Get an iterator to the inner sockets. + pub fn iter(&self) -> impl Iterator)> { + self.items().map(|i| (i.meta.handle, &i.socket)) + } + + /// Get a mutable iterator to the inner sockets. + pub fn iter_mut(&mut self) -> impl Iterator)> { + self.items_mut().map(|i| (i.meta.handle, &mut i.socket)) + } + /// Iterate every socket in this set. - pub fn iter(&self) -> impl Iterator> + '_ { + pub(crate) fn items(&self) -> impl Iterator> + '_ { self.sockets.iter().filter_map(|x| x.inner.as_ref()) } /// Iterate every socket in this set. - pub fn iter_mut(&mut self) -> impl Iterator> + '_ { + pub(crate) fn items_mut(&mut self) -> impl Iterator> + '_ { self.sockets.iter_mut().filter_map(|x| x.inner.as_mut()) } } diff --git a/src/lib.rs b/src/lib.rs index b28f434bd..f1a3653e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,5 @@ #![cfg_attr(not(any(test, feature = "std")), no_std)] #![deny(unsafe_code)] -#![cfg_attr( - all( - any(feature = "proto-ipv4", feature = "proto-ipv6"), - feature = "medium-ethernet" - ), - deny(unused) -)] //! The _smoltcp_ library is built in a layered structure, with the layers corresponding //! to the levels of API abstraction. Only the highest layers would be used by a typical From d703a66d1d08798d1c04f1f7f064b465a2c5dbf4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 May 2022 01:05:51 +0200 Subject: [PATCH 351/566] iface: borrow the device instead of owning it. --- examples/benchmark.rs | 9 +- examples/client.rs | 11 +- examples/dhcp_client.rs | 16 +-- examples/dns.rs | 11 +- examples/httpclient.rs | 11 +- examples/loopback.rs | 8 +- examples/multicast.rs | 11 +- examples/ping.rs | 11 +- examples/server.rs | 9 +- examples/sixlowpan.rs | 9 +- examples/sixlowpan_benchmark.rs | 9 +- fuzz/fuzz_targets/tcp_headers.rs | 4 +- src/iface/interface.rs | 227 +++++++++++++++---------------- 13 files changed, 171 insertions(+), 175 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index c00f4f77e..cbcefcbe2 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -74,7 +74,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let mode = match matches.free[0].as_ref() { "reader" => Client::Reader, "writer" => Client::Writer, @@ -94,13 +95,13 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); @@ -111,7 +112,7 @@ fn main() { let mut processed = 0; while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/client.rs b/examples/client.rs index 88a3b01c1..15865095a 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -24,7 +24,8 @@ fn main() { let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let port = u16::from_str(&matches.free[1]).expect("invalid port format"); @@ -42,15 +43,13 @@ fn main() { routes.add_default_ipv4_route(default_v4_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); @@ -63,7 +62,7 @@ fn main() { let mut tcp_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 65abd3d26..d3cc01f6f 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -25,7 +25,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); @@ -34,15 +35,13 @@ fn main() { let routes = Routes::new(&mut routes_storage[..]); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut dhcp_socket = dhcpv4::Socket::new(); @@ -57,7 +56,7 @@ fn main() { loop { let timestamp = Instant::now(); - if let Err(e) = iface.poll(timestamp, &mut sockets) { + if let Err(e) = iface.poll(timestamp, &mut device, &mut sockets) { debug!("poll error: {}", e); } @@ -95,10 +94,7 @@ fn main() { } } -fn set_ipv4_addr(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr) -where - DeviceT: for<'d> Device<'d>, -{ +fn set_ipv4_addr(iface: &mut Interface<'_>, cidr: Ipv4Cidr) { iface.update_ip_addrs(|addrs| { let dest = addrs.iter_mut().next().unwrap(); *dest = IpCidr::Ipv4(cidr); diff --git a/examples/dns.rs b/examples/dns.rs index 1456befcc..3d85dc63c 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -29,7 +29,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let name = &matches.free[0]; let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -55,15 +56,13 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); if medium == Medium::Ethernet { builder = builder .hardware_addr(HardwareAddress::Ethernet(ethernet_addr)) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let dns_handle = sockets.add(dns_socket); @@ -75,7 +74,7 @@ fn main() { let timestamp = Instant::now(); debug!("timestamp {:?}", timestamp); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 9f7f80067..903080522 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -24,7 +24,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let url = Url::parse(&matches.free[1]).expect("invalid url format"); @@ -48,15 +49,13 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); @@ -70,7 +69,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/loopback.rs b/examples/loopback.rs index 2e3f7084b..797c83cdb 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -71,7 +71,7 @@ fn main() { let device = Loopback::new(Medium::Ethernet); #[cfg(feature = "std")] - let device = { + let mut device = { let clock = clock.clone(); utils::setup_logging_with_clock("", move || clock.elapsed()); @@ -86,11 +86,11 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = InterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .finalize(); + .finalize(&mut device); let server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but @@ -121,7 +121,7 @@ fn main() { let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(10_000) { - match iface.poll(clock.elapsed(), &mut sockets) { + match iface.poll(clock.elapsed(), &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/multicast.rs b/examples/multicast.rs index c18659b16..e1278a124 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -26,7 +26,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); let local_addr = Ipv4Address::new(192, 168, 69, 2); @@ -34,17 +35,17 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; - let mut iface = InterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new() .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) - .finalize(); + .finalize(&mut device); let now = Instant::now(); // Join a multicast group to receive mDNS traffic iface - .join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now) + .join_multicast_group(&mut device, Ipv4Address::from_bytes(&MDNS_GROUP), now) .unwrap(); let mut sockets = SocketSet::new(vec![]); @@ -70,7 +71,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/ping.rs b/examples/ping.rs index 411d9ca0b..0704b0d69 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -87,7 +87,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let device_caps = device.capabilities(); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let count = matches @@ -128,15 +129,13 @@ fn main() { routes.add_default_ipv6_route(default_v6_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device) - .ip_addrs(ip_addrs) - .routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let icmp_handle = sockets.add(icmp_socket); @@ -150,7 +149,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/server.rs b/examples/server.rs index 0565eb33a..8a23b1fc9 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -22,7 +22,8 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = utils::parse_tuntap_options(&mut matches); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -54,13 +55,13 @@ fn main() { ]; let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache); } - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); @@ -72,7 +73,7 @@ fn main() { let mut tcp_6970_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index b60be2c5b..2dec24ee8 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -65,7 +65,8 @@ fn main() { let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap(); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let neighbor_cache = NeighborCache::new(BTreeMap::new()); @@ -89,7 +90,7 @@ fn main() { let mut out_packet_buffer = [0u8; 1280]; - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder @@ -97,7 +98,7 @@ fn main() { .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); @@ -113,7 +114,7 @@ fn main() { let mut poll = true; while poll { - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(r) => poll = r, Err(e) => { debug!("poll error: {}", e); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 34a71edc6..c107755a0 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -139,7 +139,8 @@ fn main() { let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap(); let fd = device.as_raw_fd(); - let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); + let mut device = + utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let mode = match matches.free[0].as_ref() { "reader" => Client::Reader, @@ -167,7 +168,7 @@ fn main() { let cache = FragmentsCache::new(vec![], BTreeMap::new()); - let mut builder = InterfaceBuilder::new(device) + let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder @@ -175,7 +176,7 @@ fn main() { .neighbor_cache(neighbor_cache) .sixlowpan_fragments_cache(cache) .sixlowpan_out_packet_cache(vec![]); - let mut iface = builder.finalize(); + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); @@ -188,7 +189,7 @@ fn main() { while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut sockets) { + match iface.poll(timestamp, &mut device, &mut sockets) { Ok(_) => {} Err(e) => { debug!("poll error: {}", e); diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index e7c90444d..7d4d4eaa2 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -132,11 +132,11 @@ fuzz_target!(|data: &[u8]| { let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = InterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new() .ethernet_addr(EthernetAddress::default()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .finalize(); + .finalize(&mut device); let server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 649522c18..f0d927e84 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -96,8 +96,7 @@ macro_rules! check { /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be /// a `&mut [T]`, or `Vec` if a heap is available. -pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { - device: DeviceT, +pub struct Interface<'a> { inner: InterfaceInner<'a>, fragments: FragmentsBuffer<'a>, out_packets: OutPackets<'a>, @@ -137,8 +136,7 @@ pub struct InterfaceInner<'a> { } /// A builder structure used for creating a network interface. -pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { - device: DeviceT, +pub struct InterfaceBuilder<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -159,10 +157,7 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> { sixlowpan_out_buffer: Option>, } -impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> -where - DeviceT: for<'d> Device<'d>, -{ +impl<'a> InterfaceBuilder<'a> { /// Create a builder used for creating a network interface using the /// given device and address. #[cfg_attr( @@ -176,7 +171,7 @@ use smoltcp::iface::{InterfaceBuilder, NeighborCache}; # use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; -let device = // ... +let mut device = // ... # Loopback::new(Medium::Ethernet); let hw_addr = // ... # EthernetAddress::default(); @@ -184,18 +179,16 @@ let neighbor_cache = // ... # NeighborCache::new(BTreeMap::new()); let ip_addrs = // ... # []; -let iface = InterfaceBuilder::new(device) +let iface = InterfaceBuilder::new() .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .finalize(); + .finalize(&mut device); ``` "## )] - pub fn new(device: DeviceT) -> Self { + pub fn new() -> Self { InterfaceBuilder { - device, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: None, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -293,7 +286,7 @@ let iface = InterfaceBuilder::new(device) /// [routes]. /// /// [routes]: struct.Interface.html#method.routes - pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a, DeviceT> + pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a> where T: Into>, { @@ -356,11 +349,14 @@ let iface = InterfaceBuilder::new(device) /// /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self) -> Interface<'a, DeviceT> { - let device_capabilities = self.device.capabilities(); + pub fn finalize(self, device: &mut D) -> Interface<'a> + where + D: for<'d> Device<'d>, + { + let caps = device.capabilities(); #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let (hardware_addr, neighbor_cache) = match device_capabilities.medium { + let (hardware_addr, neighbor_cache) = match caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => ( Some( @@ -397,8 +393,6 @@ let iface = InterfaceBuilder::new(device) ), }; - let caps = self.device.capabilities(); - #[cfg(feature = "medium-ieee802154")] let mut rand = Rand::new(self.random_seed); #[cfg(not(feature = "medium-ieee802154"))] @@ -426,7 +420,6 @@ let iface = InterfaceBuilder::new(device) } Interface { - device: self.device, fragments: FragmentsBuffer { #[cfg(feature = "proto-sixlowpan")] sixlowpan_fragments: self @@ -623,10 +616,7 @@ enum IgmpReportState { }, } -impl<'a, DeviceT> Interface<'a, DeviceT> -where - DeviceT: for<'d> Device<'d>, -{ +impl<'a> Interface<'a> { /// Get the socket context. /// /// The context is needed for some socket methods. @@ -641,14 +631,14 @@ where #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub fn hardware_addr(&self) -> HardwareAddress { #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] - assert!(self.device().capabilities().medium == Medium::Ethernet); + assert!(self.inner.caps.medium == Medium::Ethernet); #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] - assert!(self.device().capabilities().medium == Medium::Ieee802154); + assert!(self.inner.caps.medium == Medium::Ieee802154); #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] assert!( - self.device().capabilities().medium == Medium::Ethernet - || self.device().capabilities().medium == Medium::Ieee802154 + self.inner.caps.medium == Medium::Ethernet + || self.inner.caps.medium == Medium::Ieee802154 ); self.inner.hardware_addr.unwrap() @@ -662,45 +652,33 @@ where #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub fn set_hardware_addr(&mut self, addr: HardwareAddress) { #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] - assert!(self.device().capabilities().medium == Medium::Ethernet); + assert!(self.inner.caps.medium == Medium::Ethernet); #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] - assert!(self.device().capabilities().medium == Medium::Ieee802154); + assert!(self.inner.caps.medium == Medium::Ieee802154); #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] assert!( - self.device().capabilities().medium == Medium::Ethernet - || self.device().capabilities().medium == Medium::Ieee802154 + self.inner.caps.medium == Medium::Ethernet + || self.inner.caps.medium == Medium::Ieee802154 ); InterfaceInner::check_hardware_addr(&addr); self.inner.hardware_addr = Some(addr); } - /// Get a reference to the inner device. - pub fn device(&self) -> &DeviceT { - &self.device - } - - /// Get a mutable reference to the inner device. - /// - /// There are no invariants imposed on the device by the interface itself. Furthermore the - /// trait implementations, required for references of all lifetimes, guarantees that the - /// mutable reference can not invalidate the device as such. For some devices, such access may - /// still allow modifications with adverse effects on the usability as a `phy` device. You - /// should not use them this way. - pub fn device_mut(&mut self) -> &mut DeviceT { - &mut self.device - } - /// Add an address to a list of subscribed multicast IP addresses. /// /// 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>( + pub fn join_multicast_group>( &mut self, + device: &mut D, addr: T, timestamp: Instant, - ) -> Result { + ) -> Result + where + D: for<'d> Device<'d>, + { self.inner.now = timestamp; match addr.into() { @@ -717,7 +695,7 @@ where } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report - let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; + let tx_token = device.transmit().ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { @@ -734,11 +712,15 @@ where /// /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` /// indicates whether an immediate leave packet has been sent. - pub fn leave_multicast_group>( + pub fn leave_multicast_group>( &mut self, + device: &mut D, addr: T, timestamp: Instant, - ) -> Result { + ) -> Result + where + D: for<'d> Device<'d>, + { self.inner.now = timestamp; match addr.into() { @@ -749,7 +731,7 @@ where Ok(false) } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet - let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; + let tx_token = device.transmit().ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { @@ -831,7 +813,15 @@ where /// packets containing any unsupported protocol, option, or form, which is /// a very common occurrence and on a production system it should not even /// be logged. - pub fn poll(&mut self, timestamp: Instant, sockets: &mut SocketSet<'_>) -> Result { + pub fn poll( + &mut self, + timestamp: Instant, + device: &mut D, + sockets: &mut SocketSet<'_>, + ) -> Result + where + D: for<'d> Device<'d>, + { self.inner.now = timestamp; let mut readiness_may_have_changed = false; @@ -845,7 +835,7 @@ where } = &self.out_packets.sixlowpan_out_packet; if *packet_len > *sent_bytes { - match self.device.transmit().ok_or(Error::Exhausted) { + match device.transmit().ok_or(Error::Exhausted) { Ok(tx_token) => { if let Err(e) = self.inner.dispatch_ieee802154_out_packet( tx_token, @@ -864,11 +854,11 @@ where } loop { - let processed_any = self.socket_ingress(sockets); - let emitted_any = self.socket_egress(sockets); + let processed_any = self.socket_ingress(device, sockets); + let emitted_any = self.socket_egress(device, sockets); #[cfg(feature = "proto-igmp")] - self.igmp_egress()?; + self.igmp_egress(device)?; if processed_any || emitted_any { readiness_may_have_changed = true; @@ -925,10 +915,12 @@ where } } - fn socket_ingress(&mut self, sockets: &mut SocketSet<'_>) -> bool { + fn socket_ingress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool + where + D: for<'d> Device<'d>, + { let mut processed_any = false; let Self { - device, inner, fragments: _fragments, out_packets: _out_packets, @@ -979,9 +971,11 @@ where processed_any } - fn socket_egress(&mut self, sockets: &mut SocketSet<'_>) -> bool { + fn socket_egress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool + where + D: for<'d> Device<'d>, + { let Self { - device, inner, out_packets: _out_packets, .. @@ -1086,7 +1080,10 @@ where /// Depending on `igmp_report_state` and the therein contained /// timeouts, send IGMP membership reports. #[cfg(feature = "proto-igmp")] - fn igmp_egress(&mut self) -> Result { + fn igmp_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d>, + { match self.inner.igmp_report_state { IgmpReportState::ToSpecificQuery { version, @@ -1095,7 +1092,7 @@ where } if self.inner.now >= 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)?; + let tx_token = device.transmit().ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; } @@ -1119,7 +1116,7 @@ where Some(addr) => { 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)?; + let tx_token = device.transmit().ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; } @@ -3142,18 +3139,18 @@ mod test { } } - fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { #[cfg(feature = "medium-ethernet")] - return create_loopback_ethernet(); + return create_ethernet(); #[cfg(not(feature = "medium-ethernet"))] - return create_loopback_ip(); + return create_ip(); } #[cfg(all(feature = "medium-ip"))] #[allow(unused)] - fn create_loopback_ip<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device - let device = Loopback::new(Medium::Ip); + let mut device = Loopback::new(Medium::Ip); let ip_addrs = [ #[cfg(feature = "proto-ipv4")] IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), @@ -3163,18 +3160,18 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - let iface_builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs); + let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(); + let iface = iface_builder.finalize(&mut device); - (iface, SocketSet::new(vec![])) + (iface, SocketSet::new(vec![]), device) } #[cfg(all(feature = "medium-ethernet"))] - fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device - let device = Loopback::new(Medium::Ethernet); + let mut device = Loopback::new(Medium::Ethernet); let ip_addrs = [ #[cfg(feature = "proto-ipv4")] IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), @@ -3185,7 +3182,7 @@ mod test { ]; #[cfg(feature = "proto-sixlowpan")] - let iface_builder = InterfaceBuilder::new(device) + let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) @@ -3193,22 +3190,22 @@ mod test { .ip_addrs(ip_addrs); #[cfg(not(feature = "proto-sixlowpan"))] - let iface_builder = InterfaceBuilder::new(device) + let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) .ip_addrs(ip_addrs); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(); + let iface = iface_builder.finalize(&mut device); - (iface, SocketSet::new(vec![])) + (iface, SocketSet::new(vec![]), device) } #[cfg(feature = "proto-igmp")] - fn recv_all(iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec> { + fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); - while let Some((rx, _tx)) = iface.device.receive() { + while let Some((rx, _tx)) = device.receive() { rx.consume(timestamp, |pkt| { pkts.push(pkt.to_vec()); Ok(()) @@ -3235,13 +3232,14 @@ mod test { #[should_panic(expected = "hardware_addr required option was not set")] #[cfg(all(feature = "medium-ethernet"))] fn test_builder_initialization_panic() { - InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); + let mut device = Loopback::new(Medium::Ethernet); + InterfaceBuilder::new().finalize(&mut device); } #[test] #[cfg(feature = "proto-ipv4")] fn test_no_icmp_no_unicast_ipv4() { - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); // Unknown Ipv4 Protocol // @@ -3269,7 +3267,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_no_icmp_no_unicast_ipv6() { - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); // Unknown Ipv6 Protocol // @@ -3298,7 +3296,7 @@ mod test { #[cfg(feature = "proto-ipv4")] fn test_icmp_error_no_payload() { static NO_BYTES: [u8; 0] = []; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { @@ -3349,7 +3347,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv4")] fn test_local_subnet_broadcasts() { - let (mut iface, _) = create_loopback(); + let (mut iface, _, _device) = create(); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); @@ -3406,7 +3404,7 @@ mod test { static UDP_PAYLOAD: [u8; 12] = [ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let mut udp_bytes_unicast = vec![0u8; 20]; let mut udp_bytes_broadcast = vec![0u8; 20]; @@ -3505,7 +3503,7 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); @@ -3582,7 +3580,7 @@ mod test { fn test_handle_ipv4_broadcast() { use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let our_ipv4_addr = iface.ipv4_address().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); @@ -3654,7 +3652,7 @@ mod test { #[cfg(feature = "proto-ipv6")] const MAX_PAYLOAD_LEN: usize = 1192; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let src_addr = Ipv4Address([192, 168, 1, 1]); @@ -3761,7 +3759,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { - let (mut iface, mut sockets) = create_loopback_ethernet(); + let (mut iface, mut sockets, _device) = create_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3813,7 +3811,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { - let (mut iface, mut sockets) = create_loopback_ethernet(); + let (mut iface, mut sockets, _device) = create_ethernet(); let mut eth_bytes = vec![0u8; 86]; @@ -3885,7 +3883,7 @@ mod test { #[test] #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { - let (mut iface, mut sockets) = create_loopback_ethernet(); + let (mut iface, mut sockets, _device) = create_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -3933,7 +3931,7 @@ mod test { not(feature = "medium-ieee802154") ))] fn test_arp_flush_after_update_ip() { - let (mut iface, mut sockets) = create_loopback_ethernet(); + let (mut iface, mut sockets, _device) = create_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -4000,7 +3998,7 @@ mod test { fn test_icmpv4_socket() { use crate::wire::Icmpv4Packet; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); @@ -4071,7 +4069,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { - let (mut iface, _) = create_loopback(); + let (mut iface, _, _device) = create(); let mut new_addrs = vec![ IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), @@ -4094,7 +4092,7 @@ mod test { #[test] #[cfg(feature = "proto-ipv6")] fn test_icmpv6_nxthdr_unknown() { - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -4157,13 +4155,10 @@ mod test { #[test] #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { - fn recv_igmp( - iface: &mut Interface<'_, Loopback>, - timestamp: Instant, - ) -> Vec<(Ipv4Repr, IgmpRepr)> { - let caps = iface.device.capabilities(); + fn recv_igmp(device: &mut Loopback, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + let caps = device.capabilities(); let checksum_caps = &caps.checksum; - recv_all(iface, timestamp) + recv_all(device, timestamp) .iter() .filter_map(|frame| { let ipv4_packet = match caps.medium { @@ -4191,15 +4186,17 @@ mod test { Ipv4Address::new(224, 0, 0, 56), ]; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, mut device) = create(); // Join multicast groups let timestamp = Instant::now(); for group in &groups { - iface.join_multicast_group(*group, timestamp).unwrap(); + iface + .join_multicast_group(&mut device, *group, timestamp) + .unwrap(); } - let reports = recv_igmp(&mut iface, timestamp); + let reports = recv_igmp(&mut device, timestamp); assert_eq!(reports.len(), 2); for (i, group_addr) in groups.iter().enumerate() { assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); @@ -4223,7 +4220,7 @@ mod test { ]; { // Transmit GENERAL_QUERY_BYTES into loopback - let tx_token = iface.device.transmit().unwrap(); + let tx_token = device.transmit().unwrap(); tx_token .consume(timestamp, GENERAL_QUERY_BYTES.len(), |buffer| { buffer.copy_from_slice(GENERAL_QUERY_BYTES); @@ -4235,15 +4232,17 @@ 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 sockets); + iface.socket_ingress(&mut device, &mut sockets); // Leave multicast groups let timestamp = Instant::now(); for group in &groups { - iface.leave_multicast_group(*group, timestamp).unwrap(); + iface + .leave_multicast_group(&mut device, *group, timestamp) + .unwrap(); } - let leaves = recv_igmp(&mut iface, timestamp); + let leaves = recv_igmp(&mut device, timestamp); assert_eq!(leaves.len(), 2); for (i, group_addr) in groups.iter().cloned().enumerate() { assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); @@ -4257,7 +4256,7 @@ mod test { fn test_raw_socket_no_reply() { use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let packets = 1; let rx_buffer = @@ -4324,7 +4323,7 @@ mod test { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (mut iface, mut sockets) = create_loopback(); + let (mut iface, mut sockets, _device) = create(); let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); From 9e18ca127ee427f443eaf1b57ec42448d4337ed5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 May 2022 03:43:47 +0200 Subject: [PATCH 352/566] socket_set: add get_mut, make get immutable. --- examples/benchmark.rs | 4 ++-- examples/client.rs | 4 ++-- examples/dhcp_client.rs | 2 +- examples/dns.rs | 4 ++-- examples/httpclient.rs | 2 +- examples/loopback.rs | 4 ++-- examples/multicast.rs | 4 ++-- examples/ping.rs | 2 +- examples/server.rs | 10 +++++----- examples/sixlowpan.rs | 6 +++--- examples/sixlowpan_benchmark.rs | 4 ++-- src/iface/interface.rs | 31 ++++++++++++++++--------------- src/iface/socket_set.rs | 19 ++++++++++++++++--- src/socket/mod.rs | 13 +++++++++++-- 14 files changed, 66 insertions(+), 43 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index cbcefcbe2..f3b1912ea 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -120,7 +120,7 @@ fn main() { } // tcp:1234: emit data - let socket = sockets.get::(tcp1_handle); + let socket = sockets.get_mut::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -138,7 +138,7 @@ fn main() { } // tcp:1235: sink data - let socket = sockets.get::(tcp2_handle); + let socket = sockets.get_mut::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } diff --git a/examples/client.rs b/examples/client.rs index 15865095a..62812d4a9 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -54,7 +54,7 @@ fn main() { let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); - let socket = sockets.get::(tcp_handle); + let socket = sockets.get_mut::(tcp_handle); socket .connect(iface.context(), (address, port), 49500) .unwrap(); @@ -69,7 +69,7 @@ fn main() { } } - let socket = sockets.get::(tcp_handle); + let socket = sockets.get_mut::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index d3cc01f6f..99d2baae5 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -60,7 +60,7 @@ fn main() { debug!("poll error: {}", e); } - let event = sockets.get::(dhcp_handle).poll(); + let event = sockets.get_mut::(dhcp_handle).poll(); match event { None => {} Some(dhcpv4::Event::Configured(config)) => { diff --git a/examples/dns.rs b/examples/dns.rs index 3d85dc63c..385b2d893 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -67,7 +67,7 @@ fn main() { let mut sockets = SocketSet::new(vec![]); let dns_handle = sockets.add(dns_socket); - let socket = sockets.get::(dns_handle); + let socket = sockets.get_mut::(dns_handle); let query = socket.start_query(iface.context(), name).unwrap(); loop { @@ -82,7 +82,7 @@ fn main() { } match sockets - .get::(dns_handle) + .get_mut::(dns_handle) .get_query_result(query) { Ok(addrs) => { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 903080522..8e2e407f2 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -76,7 +76,7 @@ fn main() { } } - let socket = sockets.get::(tcp_handle); + let socket = sockets.get_mut::(tcp_handle); let cx = iface.context(); state = match state { diff --git a/examples/loopback.rs b/examples/loopback.rs index 797c83cdb..1251a25b5 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -128,7 +128,7 @@ fn main() { } } - let mut socket = sockets.get::(server_handle); + let mut socket = sockets.get_mut::(server_handle); if !socket.is_active() && !socket.is_listening() { if !did_listen { debug!("listening"); @@ -146,7 +146,7 @@ fn main() { done = true; } - let mut socket = sockets.get::(client_handle); + let mut socket = sockets.get_mut::(client_handle); let cx = iface.context(); if !socket.is_open() { if !did_connect { diff --git a/examples/multicast.rs b/examples/multicast.rs index e1278a124..6089a0c70 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -78,7 +78,7 @@ fn main() { } } - let socket = sockets.get::(raw_handle); + let socket = sockets.get_mut::(raw_handle); if socket.can_recv() { // For display purposes only - normally we wouldn't process incoming IGMP packets @@ -95,7 +95,7 @@ fn main() { } } - let socket = sockets.get::(udp_handle); + let socket = sockets.get_mut::(udp_handle); if !socket.is_open() { socket.bind(MDNS_PORT).unwrap() } diff --git a/examples/ping.rs b/examples/ping.rs index 0704b0d69..3c654dd3c 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -157,7 +157,7 @@ fn main() { } let timestamp = Instant::now(); - let socket = sockets.get::(icmp_handle); + let socket = sockets.get_mut::(icmp_handle); if !socket.is_open() { socket.bind(icmp::Endpoint::Ident(ident)).unwrap(); send_at = timestamp; diff --git a/examples/server.rs b/examples/server.rs index 8a23b1fc9..141c1a518 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -81,7 +81,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = sockets.get::(udp_handle); + let socket = sockets.get_mut::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -107,7 +107,7 @@ fn main() { } // tcp:6969: respond "hello" - let socket = sockets.get::(tcp1_handle); + let socket = sockets.get_mut::(tcp1_handle); if !socket.is_open() { socket.listen(6969).unwrap(); } @@ -120,7 +120,7 @@ fn main() { } // tcp:6970: echo with reverse - let socket = sockets.get::(tcp2_handle); + let socket = sockets.get_mut::(tcp2_handle); if !socket.is_open() { socket.listen(6970).unwrap() } @@ -162,7 +162,7 @@ fn main() { } // tcp:6971: sinkhole - let socket = sockets.get::(tcp3_handle); + let socket = sockets.get_mut::(tcp3_handle); if !socket.is_open() { socket.listen(6971).unwrap(); socket.set_keep_alive(Some(Duration::from_millis(1000))); @@ -183,7 +183,7 @@ fn main() { } // tcp:6972: fountain - let socket = sockets.get::(tcp4_handle); + let socket = sockets.get_mut::(tcp4_handle); if !socket.is_open() { socket.listen(6972).unwrap() } diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 2dec24ee8..fa000f3ef 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -104,7 +104,7 @@ fn main() { let udp_handle = sockets.add(udp_socket); let tcp_handle = sockets.add(tcp_socket); - let socket = sockets.get::(tcp_handle); + let socket = sockets.get_mut::(tcp_handle); socket.listen(50000).unwrap(); let mut tcp_active = false; @@ -124,7 +124,7 @@ fn main() { } // udp:6969: respond "hello" - let socket = sockets.get::(udp_handle); + let socket = sockets.get_mut::(udp_handle); if !socket.is_open() { socket.bind(6969).unwrap() } @@ -150,7 +150,7 @@ fn main() { socket.send_slice(&buffer[..len], endpoint).unwrap(); } - let socket = sockets.get::(tcp_handle); + let socket = sockets.get_mut::(tcp_handle); if socket.is_active() && !tcp_active { debug!("connected"); } else if !socket.is_active() && tcp_active { diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index c107755a0..9eae6cc84 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -197,7 +197,7 @@ fn main() { } // tcp:1234: emit data - let socket = sockets.get::(tcp1_handle); + let socket = sockets.get_mut::(tcp1_handle); if !socket.is_open() { socket.listen(1234).unwrap(); } @@ -213,7 +213,7 @@ fn main() { } // tcp:1235: sink data - let socket = sockets.get::(tcp2_handle); + let socket = sockets.get_mut::(tcp2_handle); if !socket.is_open() { socket.listen(1235).unwrap(); } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index f0d927e84..bd9656302 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -187,6 +187,7 @@ let iface = InterfaceBuilder::new() ``` "## )] + #[allow(clippy::new_without_default)] pub fn new() -> Self { InterfaceBuilder { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -1614,7 +1615,7 @@ impl<'a> InterfaceInner<'a> { // normal IPv6 UDP payload, which is not what we have here. for udp_socket in sockets .items_mut() - .filter_map(|i| udp::Socket::downcast(&mut i.socket)) + .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) { if udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { udp_socket.process( @@ -1743,7 +1744,7 @@ impl<'a> InterfaceInner<'a> { // Pass every IP packet to all raw sockets we have registered. for raw_socket in sockets .items_mut() - .filter_map(|i| raw::Socket::downcast(&mut i.socket)) + .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket)) { if raw_socket.accepts(ip_repr) { raw_socket.process(self, ip_repr, ip_payload); @@ -1861,7 +1862,7 @@ impl<'a> InterfaceInner<'a> { { if let Some(dhcp_socket) = sockets .items_mut() - .filter_map(|i| dhcpv4::Socket::downcast(&mut i.socket)) + .filter_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) .next() { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); @@ -2040,7 +2041,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] for icmp_socket in _sockets .items_mut() - .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) + .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) { if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { icmp_socket.process(self, &ip_repr, &icmp_repr.into()); @@ -2219,7 +2220,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] for icmp_socket in _sockets .items_mut() - .filter_map(|i| icmp::Socket::downcast(&mut i.socket)) + .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) { if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { icmp_socket.process(self, &ip_repr, &icmp_repr.into()); @@ -2344,7 +2345,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] for udp_socket in sockets .items_mut() - .filter_map(|i| udp::Socket::downcast(&mut i.socket)) + .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) { if udp_socket.accepts(self, &ip_repr, &udp_repr) { udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); @@ -2355,7 +2356,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dns")] for dns_socket in sockets .items_mut() - .filter_map(|i| dns::Socket::downcast(&mut i.socket)) + .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket)) { if dns_socket.accepts(&ip_repr, &udp_repr) { dns_socket.process(self, &ip_repr, &udp_repr, udp_payload); @@ -2412,7 +2413,7 @@ impl<'a> InterfaceInner<'a> { for tcp_socket in sockets .items_mut() - .filter_map(|i| tcp::Socket::downcast(&mut i.socket)) + .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket)) { if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { return tcp_socket @@ -3543,7 +3544,7 @@ mod test { }); // Bind the socket to port 68 - let socket = sockets.get::(socket_handle); + let socket = sockets.get_mut::(socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -3567,7 +3568,7 @@ mod test { // Make sure the payload to the UDP packet processed by process_udp is // appended to the bound sockets rx_buffer - let socket = sockets.get::(socket_handle); + let socket = sockets.get_mut::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -4011,7 +4012,7 @@ mod test { let seq_no = 0x5432; let echo_data = &[0xff; 16]; - let socket = sockets.get::(socket_handle); + let socket = sockets.get_mut::(socket_handle); // Bind to the ID 0x1234 assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); @@ -4037,7 +4038,7 @@ mod test { // Open a socket and ensure the packet is handled due to the listening // socket. - assert!(!sockets.get::(socket_handle).can_recv()); + assert!(!sockets.get_mut::(socket_handle).can_recv()); // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening let echo_reply = Icmpv4Repr::EchoReply { @@ -4055,7 +4056,7 @@ mod test { Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) ); - let socket = sockets.get::(socket_handle); + let socket = sockets.get_mut::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), @@ -4331,7 +4332,7 @@ mod test { let udp_socket_handle = sockets.add(udp_socket); // Bind the socket to port 68 - let socket = sockets.get::(udp_socket_handle); + let socket = sockets.get_mut::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); @@ -4398,7 +4399,7 @@ mod test { assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = sockets.get::(udp_socket_handle); + let socket = sockets.get_mut::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), diff --git a/src/iface/socket_set.rs b/src/iface/socket_set.rs index 92629c39a..ce52f5bb6 100644 --- a/src/iface/socket_set.rs +++ b/src/iface/socket_set.rs @@ -93,15 +93,28 @@ impl<'a> SocketSet<'a> { /// # Panics /// This function may panic if the handle does not belong to this socket set /// or the socket has the wrong type. - pub fn get>(&mut self, handle: SocketHandle) -> &mut T { - match self.sockets[handle.0].inner.as_mut() { + pub fn get>(&self, handle: SocketHandle) -> &T { + match self.sockets[handle.0].inner.as_ref() { Some(item) => { - T::downcast(&mut item.socket).expect("handle refers to a socket of a wrong type") + T::downcast(&item.socket).expect("handle refers to a socket of a wrong type") } None => panic!("handle does not refer to a valid socket"), } } + /// Get a mutable socket from the set by its handle, as mutable. + /// + /// # Panics + /// This function may panic if the handle does not belong to this socket set + /// or the socket has the wrong type. + pub fn get_mut>(&mut self, handle: SocketHandle) -> &mut T { + match self.sockets[handle.0].inner.as_mut() { + Some(item) => T::downcast_mut(&mut item.socket) + .expect("handle refers to a socket of a wrong type"), + None => panic!("handle does not refer to a valid socket"), + } + } + /// Remove a socket from the set, without changing its state. /// /// # Panics diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 7f64c48e6..ee24841c4 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -93,7 +93,8 @@ impl<'a> Socket<'a> { /// A conversion trait for network sockets. pub trait AnySocket<'a>: Sized { fn upcast(self) -> Socket<'a>; - fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; + fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self>; + fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; } macro_rules! from_socket { @@ -103,7 +104,15 @@ macro_rules! from_socket { Socket::$variant(self) } - fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> { + fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self> { + #[allow(unreachable_patterns)] + match socket { + Socket::$variant(socket) => Some(socket), + _ => None, + } + } + + fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> { #[allow(unreachable_patterns)] match socket { Socket::$variant(socket) => Some(socket), From 3065959c70fc9749a00fecc92a87a63a04bf7984 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 8 Jun 2022 14:34:12 +0200 Subject: [PATCH 353/566] Reassemble incomming IPv4 packets. --- Cargo.toml | 10 +- examples/client.rs | 27 +- examples/server.rs | 26 +- src/iface/fragmentation.rs | 205 +++++++---- src/iface/interface.rs | 690 ++++++++++++++++++++++++------------- src/iface/mod.rs | 4 +- src/wire/ipv4.rs | 42 ++- src/wire/mod.rs | 4 +- src/wire/sixlowpan.rs | 19 +- 9 files changed, 683 insertions(+), 344 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae74d4f7d..5e61b6d9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,10 +43,12 @@ verbose = [] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "proto-ipv4" = [] +"proto-ipv4-fragmentation" = ["proto-ipv4"] "proto-igmp" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] "proto-sixlowpan" = ["proto-ipv6"] +"proto-sixlowpan-fragmentation" = ["proto-sixlowpan"] "proto-dns" = [] "socket" = [] @@ -63,7 +65,8 @@ default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", "medium-ieee802154", "phy-raw_socket", "phy-tuntap_interface", - "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", "proto-dns", + "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns", + "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "async" ] @@ -114,10 +117,11 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac [[example]] name = "sixlowpan" -required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] +required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] + [[example]] name = "sixlowpan_benchmark" -required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"] +required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] [[example]] name = "dns" diff --git a/examples/client.rs b/examples/client.rs index 62812d4a9..3fe69bdbd 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -5,6 +5,12 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; +#[cfg(any( + feature = "proto-sixlowpan-fragmentation", + feature = "proto-ipv4-fragmentation" +))] +use smoltcp::iface::FragmentsCache; + use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; @@ -31,8 +37,8 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); - let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 128]); + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); @@ -44,6 +50,23 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let mut out_packet_buffer = [0u8; 1280]; + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder + .sixlowpan_fragments_cache(sixlowpan_frag_cache) + .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + } + if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) diff --git a/examples/server.rs b/examples/server.rs index 141c1a518..07b18c404 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -6,6 +6,11 @@ use std::fmt::Write; use std::os::unix::io::AsRawFd; use std::str; +#[cfg(any( + feature = "proto-sixlowpan-fragmentation", + feature = "proto-ipv4-fragmentation" +))] +use smoltcp::iface::FragmentsCache; use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{tcp, udp}; @@ -27,8 +32,8 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 128]); + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); @@ -56,6 +61,23 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let mut out_packet_buffer = [0u8; 1280]; + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder + .sixlowpan_fragments_cache(sixlowpan_frag_cache) + .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + } + if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index cebc11d5d..72135b0c1 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -3,13 +3,12 @@ use managed::{ManagedMap, ManagedSlice}; use crate::storage::Assembler; -use crate::time::Instant; +use crate::time::{Duration, Instant}; use crate::Error; use crate::Result; /// Holds different fragments of one packet, used for assembling fragmented packets. #[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PacketAssembler<'a> { buffer: ManagedSlice<'a, u8>, assembler: AssemblerState, @@ -17,14 +16,12 @@ pub struct PacketAssembler<'a> { /// Holds the state of the assembling of one packet. #[derive(Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum AssemblerState { NotInit, Assembling { assembler: Assembler, - total_size: usize, - last_updated: Instant, - started_on: Instant, + total_size: Option, + expires_at: Instant, offset_correction: isize, }, } @@ -51,32 +48,71 @@ impl<'a> PacketAssembler<'a> { /// fragments of a packet. pub(crate) fn start( &mut self, - total_size: usize, - start_time: Instant, + total_size: Option, + expires_at: Instant, offset_correction: isize, ) -> Result<()> { match &mut self.buffer { - ManagedSlice::Borrowed(b) if b.len() < total_size => { - return Err(Error::PacketAssemblerBufferTooSmall); + ManagedSlice::Borrowed(b) => { + if let Some(total_size) = total_size { + if b.len() < total_size { + return Err(Error::PacketAssemblerBufferTooSmall); + } + } } - ManagedSlice::Borrowed(_) => (), #[cfg(any(feature = "std", feature = "alloc"))] ManagedSlice::Owned(b) => { - b.resize(total_size, 0); + if let Some(total_size) = total_size { + b.resize(total_size, 0); + } } } self.assembler = AssemblerState::Assembling { - assembler: Assembler::new(total_size), + assembler: Assembler::new(if let Some(total_size) = total_size { + total_size + } else { + usize::MAX + }), total_size, - last_updated: start_time, - started_on: start_time, + expires_at, offset_correction, }; Ok(()) } + /// Set the total size of the packet assembler. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the + /// assembler with [Self::start]). + pub(crate) fn set_total_size(&mut self, size: usize) -> Result<()> { + match self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { + ref mut total_size, .. + } => { + *total_size = Some(size); + Ok(()) + } + } + } + + /// Return the instant when the assembler expires. + /// + /// # Errors + /// + /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the + /// assembler with [Self::start]). + pub(crate) fn expires_at(&self) -> Result { + match self.assembler { + AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), + AssemblerState::Assembling { expires_at, .. } => Ok(expires_at), + } + } + /// Add a fragment into the packet that is being reassembled. /// /// # Errors @@ -86,21 +122,30 @@ impl<'a> PacketAssembler<'a> { /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// place. /// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data. - pub(crate) fn add(&mut self, data: &[u8], offset: usize, now: Instant) -> Result { + pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result { match self.assembler { AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), AssemblerState::Assembling { ref mut assembler, total_size, - ref mut last_updated, offset_correction, .. } => { let offset = offset as isize + offset_correction; let offset = if offset <= 0 { 0 } else { offset as usize }; - if offset + data.len() > total_size { - return Err(Error::PacketAssemblerBufferTooSmall); + match &mut self.buffer { + ManagedSlice::Borrowed(b) => { + if offset + data.len() > b.len() { + return Err(Error::PacketAssemblerBufferTooSmall); + } + } + #[cfg(any(feature = "std", feature = "alloc"))] + ManagedSlice::Owned(b) => { + if offset + data.len() > b.len() { + b.resize(offset + data.len(), 0); + } + } } let len = data.len(); @@ -111,7 +156,6 @@ impl<'a> PacketAssembler<'a> { if overlap { net_debug!("packet was added, but there was an overlap."); } - *last_updated = now; self.is_complete() } // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? @@ -134,6 +178,8 @@ impl<'a> PacketAssembler<'a> { AssemblerState::NotInit => return Err(Error::PacketAssemblerNotInit), AssemblerState::Assembling { total_size, .. } => { if self.is_complete()? { + // NOTE: we can unwrap because `is_complete` already checks this. + let total_size = total_size.unwrap(); let a = &self.buffer[..total_size]; self.assembler = AssemblerState::NotInit; a @@ -158,13 +204,10 @@ impl<'a> PacketAssembler<'a> { assembler, total_size, .. - } => { - if let Some(front) = assembler.peek_front() { - Ok(front == *total_size) - } else { - Ok(false) - } - } + } => match (total_size, assembler.peek_front()) { + (Some(total_size), Some(front)) => Ok(front == *total_size), + _ => Ok(false), + }, } } @@ -173,40 +216,20 @@ impl<'a> PacketAssembler<'a> { self.assembler == AssemblerState::NotInit } - /// Returns the [`Instant`] when the packet assembler was started. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the packet assembler was not initialized. - pub fn start_time(&self) -> Result { - match self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { started_on, .. } => Ok(started_on), - } - } - - /// Returns the [`Instant`] when the packet assembler was last updated. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the packet assembler was not initialized. - pub fn last_update_time(&self) -> Result { - match self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { last_updated, .. } => Ok(last_updated), - } - } - /// Mark this assembler as [`AssemblerState::NotInit`]. /// This is then cleaned up by the [`PacketAssemblerSet`]. pub fn mark_discarded(&mut self) { self.assembler = AssemblerState::NotInit; } + + /// Returns `true` when the [`AssemblerState`] is discarded. + pub fn is_discarded(&self) -> bool { + matches!(self.assembler, AssemblerState::NotInit) + } } /// Set holding multiple [`PacketAssembler`]. #[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy> { packet_buffer: ManagedSlice<'a, PacketAssembler<'a>>, index_buffer: ManagedMap<'a, Key, usize>, @@ -296,7 +319,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { match &mut self.packet_buffer { ManagedSlice::Borrowed(_) => (), #[cfg(any(feature = "std", feature = "alloc"))] - ManagedSlice::Owned(b) => b.push(PacketAssembler::new(vec![])), + ManagedSlice::Owned(b) => b.push(PacketAssembler::new(alloc::vec![])), } self.packet_buffer @@ -341,7 +364,10 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { loop { let mut key = None; for (k, i) in self.index_buffer.iter() { - if self.packet_buffer[*i as usize].assembler == AssemblerState::NotInit { + if matches!( + self.packet_buffer[*i as usize].assembler, + AssemblerState::NotInit + ) { key = Some(*k); break; } @@ -355,17 +381,28 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { } } - /// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`. - pub fn remove_when( - &mut self, - f: impl Fn(&mut PacketAssembler<'_>) -> Result, - ) -> Result<()> { + /// Mark all [`PacketAssembler`]s as discarded for which `f` returns `Ok(true)`. + /// This does not remove them from the buffer. + pub fn mark_discarded_when(&mut self, f: F) -> Result<()> + where + F: Fn(&mut PacketAssembler<'_>) -> Result, + { for (_, i) in &mut self.index_buffer.iter() { let frag = &mut self.packet_buffer[*i as usize]; if f(frag)? { frag.mark_discarded(); } } + + Ok(()) + } + + /// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`. + pub fn remove_when(&mut self, f: F) -> Result<()> + where + F: Fn(&mut PacketAssembler<'_>) -> Result, + { + self.mark_discarded_when(f)?; self.remove_discarded(); Ok(()) @@ -386,7 +423,7 @@ mod tests { let mut p_assembler = PacketAssembler::new(vec![]); let data = b"Hello World!"; assert_eq!( - p_assembler.add(&data[..], data.len(), Instant::now()), + p_assembler.add(&data[..], data.len()), Err(Error::PacketAssemblerNotInit) ); @@ -403,14 +440,14 @@ mod tests { let mut p_assembler = PacketAssembler::new(&mut storage[..]); assert_eq!( - p_assembler.start(2, Instant::now(), 0), + p_assembler.start(Some(2), Instant::from_secs(0), 0), Err(Error::PacketAssemblerBufferTooSmall) ); - assert_eq!(p_assembler.start(1, Instant::now(), 0), Ok(())); + assert_eq!(p_assembler.start(Some(1), Instant::from_secs(0), 0), Ok(())); let data = b"Hello World!"; assert_eq!( - p_assembler.add(&data[..], data.len(), Instant::now()), + p_assembler.add(&data[..], data.len()), Err(Error::PacketAssemblerBufferTooSmall) ); } @@ -420,12 +457,14 @@ mod tests { let mut storage = [0u8; 5]; let mut p_assembler = PacketAssembler::new(&mut storage[..]); - p_assembler.start(5, Instant::now(), 0).unwrap(); + p_assembler + .start(Some(5), Instant::from_secs(0), 0) + .unwrap(); let data = b"Rust"; - p_assembler.add(&data[..], 0, Instant::now()).unwrap(); + p_assembler.add(&data[..], 0).unwrap(); - assert_eq!(p_assembler.add(&data[..], 1, Instant::now()), Ok(true)); + assert_eq!(p_assembler.add(&data[..], 1), Ok(true)); } #[test] @@ -435,18 +474,40 @@ mod tests { let data = b"Hello World!"; - p_assembler.start(data.len(), Instant::now(), 0).unwrap(); + p_assembler + .start(Some(data.len()), Instant::from_secs(0), 0) + .unwrap(); - p_assembler.add(b"Hello ", 0, Instant::now()).unwrap(); + p_assembler.add(b"Hello ", 0).unwrap(); assert_eq!( p_assembler.assemble(), Err(Error::PacketAssemblerIncomplete) ); + p_assembler.add(b"World!", b"Hello ".len()).unwrap(); + + assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..])); + } + + #[test] + fn packet_assembler_out_of_order_assemble() { + let mut storage = [0u8; 12]; + let mut p_assembler = PacketAssembler::new(&mut storage[..]); + + let data = b"Hello World!"; + p_assembler - .add(b"World!", b"Hello ".len(), Instant::now()) + .start(Some(data.len()), Instant::from_secs(0), 0) .unwrap(); + p_assembler.add(b"World!", b"Hello ".len()).unwrap(); + assert_eq!( + p_assembler.assemble(), + Err(Error::PacketAssemblerIncomplete) + ); + + p_assembler.add(b"Hello ", 0).unwrap(); + assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..])); } @@ -494,7 +555,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now(), 0) + .start(Some(0), Instant::from_secs(0), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -502,7 +563,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now(), 0) + .start(Some(0), Instant::from_secs(0), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); @@ -510,7 +571,7 @@ mod tests { set.reserve_with_key(&key).unwrap(); set.get_packet_assembler_mut(&key) .unwrap() - .start(0, Instant::now(), 0) + .start(Some(0), Instant::from_secs(0), 0) .unwrap(); set.get_assembled_packet(&key).unwrap(); } diff --git a/src/iface/interface.rs b/src/iface/interface.rs index bd9656302..0f703280c 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -5,7 +5,7 @@ use core::cmp; use managed::{ManagedMap, ManagedSlice}; -#[cfg(feature = "proto-sixlowpan")] +#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] use super::fragmentation::PacketAssemblerSet; use super::socket_set::SocketSet; use crate::iface::Routes; @@ -23,19 +23,27 @@ use crate::wire::*; use crate::{Error, Result}; pub(crate) struct FragmentsBuffer<'a> { - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, - #[cfg(not(feature = "proto-sixlowpan"))] + + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] _lifetime: core::marker::PhantomData<&'a ()>, } pub(crate) struct OutPackets<'a> { - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_packet: SixlowpanOutPacket<'a>, - #[cfg(not(feature = "proto-sixlowpan"))] + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] _lifetime: core::marker::PhantomData<&'a ()>, } +#[allow(unused)] #[cfg(feature = "proto-sixlowpan")] pub(crate) struct SixlowpanOutPacket<'a> { /// The buffer that holds the unfragmented 6LoWPAN packet. @@ -59,7 +67,7 @@ pub(crate) struct SixlowpanOutPacket<'a> { ll_src_addr: Ieee802154Address, } -#[cfg(feature = "proto-sixlowpan")] +#[cfg(feature = "proto-sixlowpan-fragmentation")] impl<'a> SixlowpanOutPacket<'a> { pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { Self { @@ -121,7 +129,7 @@ pub struct InterfaceInner<'a> { sequence_no: u8, #[cfg(feature = "medium-ieee802154")] pan_id: Option, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] @@ -151,9 +159,13 @@ pub struct InterfaceBuilder<'a> { #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, random_seed: u64, - #[cfg(feature = "proto-sixlowpan")] + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: Option>, + + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: Option>, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: Option>, } @@ -167,6 +179,8 @@ impl<'a> InterfaceBuilder<'a> { ``` # use std::collections::BTreeMap; +#[cfg(feature = "proto-ipv4-fragmentation")] +use smoltcp::iface::FragmentsCache; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; # use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; @@ -177,13 +191,20 @@ let hw_addr = // ... # EthernetAddress::default(); let neighbor_cache = // ... # NeighborCache::new(BTreeMap::new()); +# #[cfg(feature = "proto-ipv4-fragmentation")] +# let ipv4_frag_cache = // ... +# FragmentsCache::new(vec![], BTreeMap::new()); let ip_addrs = // ... # []; -let iface = InterfaceBuilder::new() +let builder = InterfaceBuilder::new() .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(&mut device); + .ip_addrs(ip_addrs); + +# #[cfg(feature = "proto-ipv4-fragmentation")] +let builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + +let iface = builder.finalize(&mut device); ``` "## )] @@ -206,9 +227,12 @@ let iface = InterfaceBuilder::new() ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), random_seed: 0, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: None, + + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: None, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: None, } } @@ -321,7 +345,13 @@ let iface = InterfaceBuilder::new() self } - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-ipv4-fragmentation")] + pub fn ipv4_fragments_cache(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { + self.ipv4_fragments = Some(storage); + self + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_fragments_cache( mut self, storage: PacketAssemblerSet<'a, SixlowpanFragKey>, @@ -330,7 +360,7 @@ let iface = InterfaceBuilder::new() self } - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_out_packet_cache(mut self, storage: T) -> Self where T: Into>, @@ -422,22 +452,29 @@ let iface = InterfaceBuilder::new() Interface { fragments: FragmentsBuffer { - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: self + .ipv4_fragments + .expect("Cache for incoming IPv4 fragments is required"), + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: self .sixlowpan_fragments .expect("Cache for incoming 6LoWPAN fragments is required"), - #[cfg(not(feature = "proto-sixlowpan"))] + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] _lifetime: core::marker::PhantomData, }, out_packets: OutPackets { - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_packet: SixlowpanOutPacket::new( self.sixlowpan_out_buffer - .expect("Cache for outgoing fragments is required"), + .expect("Cache for outgoing 6LoWPAN fragments is required"), ), - #[cfg(not(feature = "proto-sixlowpan"))] + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] _lifetime: core::marker::PhantomData, }, inner: InterfaceInner { @@ -459,7 +496,7 @@ let iface = InterfaceBuilder::new() sequence_no, #[cfg(feature = "medium-ieee802154")] pan_id: self.pan_id, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] tag, rand, }, @@ -760,12 +797,11 @@ impl<'a> Interface<'a> { pub fn ipv4_addr(&self) -> Option { self.ip_addrs() .iter() - .filter_map(|cidr| match cidr.address() { + .find_map(|cidr| match cidr.address() { IpAddress::Ipv4(addr) => Some(addr), #[allow(unreachable_patterns)] _ => None, }) - .next() } /// Update the IP addresses of the interface. @@ -825,35 +861,33 @@ impl<'a> Interface<'a> { { self.inner.now = timestamp; - let mut readiness_may_have_changed = false; - - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-ipv4-fragmentation")] + if let Err(e) = self + .fragments + .ipv4_fragments + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?)) { - let SixlowpanOutPacket { - packet_len, - sent_bytes, - .. - } = &self.out_packets.sixlowpan_out_packet; + return Err(e); + } - if *packet_len > *sent_bytes { - match device.transmit().ok_or(Error::Exhausted) { - Ok(tx_token) => { - if let Err(e) = self.inner.dispatch_ieee802154_out_packet( - tx_token, - &mut self.out_packets.sixlowpan_out_packet, - ) { - net_debug!("failed to transmit: {}", e); - } - } - Err(e) => { - net_debug!("failed to transmit: {}", e); - } - } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if let Err(e) = self + .fragments + .sixlowpan_fragments + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?)) + { + return Err(e); + } - return Ok(true); - } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + match self.sixlowpan_egress(device) { + Ok(true) => return Ok(true), + Err(e) => return Err(e), + _ => (), } + let mut readiness_may_have_changed = false; + loop { let processed_any = self.socket_ingress(device, sockets); let emitted_any = self.socket_egress(device, sockets); @@ -923,15 +957,16 @@ impl<'a> Interface<'a> { let mut processed_any = false; let Self { inner, - fragments: _fragments, + fragments: ref mut _fragments, out_packets: _out_packets, } = self; + while let Some((rx_token, tx_token)) = device.receive() { let res = rx_token.consume(inner.now, |frame| { match inner.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - if let Some(packet) = inner.process_ethernet(sockets, &frame) { + if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { if let Err(err) = inner.dispatch(tx_token, packet) { net_debug!("Failed to send response: {}", err); } @@ -939,7 +974,7 @@ impl<'a> Interface<'a> { } #[cfg(feature = "medium-ip")] Medium::Ip => { - if let Some(packet) = inner.process_ip(sockets, &frame) { + if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { if let Err(err) = inner.dispatch_ip(tx_token, packet, None) { net_debug!("Failed to send response: {}", err); } @@ -947,11 +982,8 @@ impl<'a> Interface<'a> { } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = inner.process_ieee802154( - sockets, - &frame, - &mut _fragments.sixlowpan_fragments, - ) { + if let Some(packet) = inner.process_ieee802154(sockets, &frame, _fragments) + { if let Err(err) = inner.dispatch_ip(tx_token, packet, Some(_out_packets)) { @@ -996,14 +1028,14 @@ impl<'a> Interface<'a> { let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); match device.transmit().ok_or(Error::Exhausted) { - Ok(t) => { - #[cfg(feature = "proto-sixlowpan")] - if let Err(_e) = inner.dispatch_ip(t, response, Some(_out_packets)) { + Ok(_t) => { + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if let Err(_e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { net_debug!("failed to dispatch IP: {}", _e); } - #[cfg(not(feature = "proto-sixlowpan"))] - if let Err(_e) = inner.dispatch_ip(t, response, None) { + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + if let Err(_e) = inner.dispatch_ip(_t, response, None) { net_debug!("failed to dispatch IP: {}", _e); } emitted_any = true; @@ -1140,6 +1172,41 @@ impl<'a> Interface<'a> { _ => Ok(false), } } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + fn sixlowpan_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d>, + { + let SixlowpanOutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.sixlowpan_out_packet; + + if *packet_len == 0 { + return Ok(false); + } + + if *packet_len >= *sent_bytes { + match device.transmit().ok_or(Error::Exhausted) { + Ok(tx_token) => { + if let Err(e) = self.inner.dispatch_ieee802154_out_packet( + tx_token, + &mut self.out_packets.sixlowpan_out_packet, + ) { + net_debug!("failed to transmit: {}", e); + } + } + Err(e) => { + net_debug!("failed to transmit: {}", e); + } + } + Ok(true) + } else { + Ok(false) + } + } } impl<'a> InterfaceInner<'a> { @@ -1256,7 +1323,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] sequence_no: 1, - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: 1, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -1301,7 +1368,7 @@ impl<'a> InterfaceInner<'a> { no } - #[cfg(feature = "proto-sixlowpan")] + #[cfg(feature = "proto-sixlowpan-fragmentation")] fn get_sixlowpan_fragment_tag(&mut self) -> u16 { let tag = self.tag; self.tag = self.tag.wrapping_add(1); @@ -1336,14 +1403,11 @@ impl<'a> InterfaceInner<'a> { /// Get the first IPv4 address of the interface. #[cfg(feature = "proto-ipv4")] pub fn ipv4_address(&self) -> Option { - self.ip_addrs - .iter() - .filter_map(|addr| match *addr { - IpCidr::Ipv4(cidr) => Some(cidr.address()), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None, - }) - .next() + self.ip_addrs.iter().find_map(|addr| match *addr { + IpCidr::Ipv4(cidr) => Some(cidr.address()), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None, + }) } /// Check whether the interface listens to given destination multicast IP address. @@ -1367,6 +1431,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, frame: &'frame T, + _fragments: &'frame mut FragmentsBuffer<'a>, ) -> Option> { let eth_frame = check!(EthernetFrame::new_checked(frame)); @@ -1384,8 +1449,14 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - self.process_ipv4(sockets, &ipv4_packet) - .map(EthernetPacket::Ip) + + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4-fragmentation")] { + self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) + .map(EthernetPacket::Ip) } else { + self.process_ipv4(sockets, &ipv4_packet, None).map(EthernetPacket::Ip) + } + } } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { @@ -1403,12 +1474,20 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, + _fragments: &'frame mut FragmentsBuffer<'a>, ) -> Option> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - self.process_ipv4(sockets, &ipv4_packet) + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4-fragmentation")] { + self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) + } else { + self.process_ipv4(sockets, &ipv4_packet, None) + + } + } } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { @@ -1425,7 +1504,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, sixlowpan_payload: &'payload T, - fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + _fragments: &'output mut FragmentsBuffer<'a>, ) -> Option> { let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); @@ -1449,7 +1528,15 @@ impl<'a> InterfaceInner<'a> { } match ieee802154_frame.payload() { - Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, fragments), + Some(payload) => { + cfg_if::cfg_if! { + if #[cfg(feature = "proto-sixlowpan-fragmentation")] { + self.process_sixlowpan(sockets, &ieee802154_repr, payload, Some(&mut _fragments.sixlowpan_fragments)) + } else { + self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) + } + } + } None => None, } } @@ -1460,15 +1547,18 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + _fragments: Option<&'output mut PacketAssemblerSet<'a, SixlowpanFragKey>>, ) -> Option> { - check!(fragments.remove_when(|frag| Ok( - self.now - frag.start_time().unwrap() > Duration::from_secs(60) - ))); - fragments.remove_discarded(); - let payload = match check!(SixlowpanPacket::dispatch(payload)) { + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + SixlowpanPacket::FragmentHeader => { + net_debug!("Fragmentation is not supported, use the `proto-sixlowpan-fragmentation` feature to add support."); + return None; + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] SixlowpanPacket::FragmentHeader => { + let fragments = _fragments.unwrap(); + // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. let frag = check!(SixlowpanFragPacket::new_checked(payload)); @@ -1528,25 +1618,21 @@ impl<'a> InterfaceInner<'a> { // We also pass the header size, since this is needed when other fragments // (other than the first one) are added. check!(check!(fragments.reserve_with_key(&key)).start( - frag.datagram_size() as usize - uncompressed_header_size - + compressed_header_size, - self.now, + Some( + frag.datagram_size() as usize - uncompressed_header_size + + compressed_header_size + ), + self.now + Duration::from_secs(60), -((uncompressed_header_size - compressed_header_size) as isize), )); } let frags = check!(fragments.get_packet_assembler_mut(&key)); - // Check if 60 seconds have passed since the start of the first fragment. - if self.now - frags.start_time().unwrap() > Duration::from_secs(60) { - frags.mark_discarded(); - return None; - } - net_trace!("6LoWPAN: received packet fragment"); // Add the fragment to the packet assembler. - match frags.add(frag.payload(), offset, self.now) { + match frags.add(frag.payload(), offset) { Ok(true) => { net_trace!("6LoWPAN: fragmented packet now complete"); check!(fragments.get_assembled_packet(&key)) @@ -1830,22 +1916,76 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv4")] - fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized>( + fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - ipv4_packet: &Ipv4Packet<&'frame T>, - ) -> Option> { + ipv4_packet: &Ipv4Packet<&'payload T>, + _fragments: Option<&'output mut PacketAssemblerSet<'a, Ipv4FragKey>>, + ) -> Option> { let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); - if !self.is_unicast_v4(ipv4_repr.src_addr) { // Discard packets with non-unicast source addresses. net_debug!("non-unicast source address"); return None; } - let ip_repr = IpRepr::Ipv4(ipv4_repr); + #[cfg(feature = "proto-ipv4-fragmentation")] + let ip_payload = { + const REASSEMBLY_TIMEOUT: u64 = 90; + + let fragments = _fragments.unwrap(); + + if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { + let key = ipv4_packet.get_key(); + + let f = match fragments.get_packet_assembler_mut(&key) { + Ok(f) => f, + Err(_) => { + check!(check!(fragments.reserve_with_key(&key)).start( + None, + self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), + 0, + )); + check!(fragments.get_packet_assembler_mut(&key)) + } + }; + + if !ipv4_packet.more_frags() { + // This is the last fragment, so we know the total size + check!(f.set_total_size( + ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize + + ipv4_packet.frag_offset() as usize, + )); + } + + match f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { + Ok(true) => { + // NOTE: according to the standard, the total length needs to be + // recomputed, as well as the checksum. However, we don't really use + // the IPv4 header after the packet is reassembled. + check!(fragments.get_assembled_packet(&key)) + } + Ok(false) => { + return None; + } + Err(Error::PacketAssemblerOverlap) => { + return None; + } + Err(e) => { + net_debug!("fragmentation error: {}", e); + return None; + } + } + } else { + ipv4_packet.payload() + } + }; + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] let ip_payload = ipv4_packet.payload(); + let ip_repr = IpRepr::Ipv4(ipv4_repr); + #[cfg(feature = "socket-raw")] let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload); #[cfg(not(feature = "socket-raw"))] @@ -1862,8 +2002,7 @@ impl<'a> InterfaceInner<'a> { { if let Some(dhcp_socket) = sockets .items_mut() - .filter_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) - .next() + .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_repr = check!(UdpRepr::parse( @@ -2732,14 +2871,14 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "medium-ieee802154")] + #[cfg(all(feature = "medium-ieee802154", feature = "proto-sixlowpan"))] fn dispatch_ieee802154( &mut self, ll_dst_a: Ieee802154Address, ip_repr: &IpRepr, tx_token: Tx, packet: IpPacket, - out_packet: Option<&mut OutPackets>, + _out_packet: Option<&mut OutPackets>, ) -> Result<()> { // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to @@ -2798,14 +2937,14 @@ impl<'a> InterfaceInner<'a> { // We need to know this, such that we know when to do the fragmentation. let mut total_size = 0; total_size += iphc_repr.buffer_len(); - let mut compressed_headers_len = iphc_repr.buffer_len(); + let mut _compressed_headers_len = iphc_repr.buffer_len(); #[allow(unreachable_patterns)] match packet { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - compressed_headers_len += udp_repr.header_len(); + _compressed_headers_len += udp_repr.header_len(); total_size += udp_repr.header_len() + payload.len(); } #[cfg(feature = "socket-tcp")] @@ -2822,130 +2961,139 @@ impl<'a> InterfaceInner<'a> { let ieee_len = ieee_repr.buffer_len(); if total_size + ieee_len > 125 { - // The packet does not fit in one Ieee802154 frame, so we need fragmentation. - // We do this by emitting everything in the `out_packet.buffer` from the interface. - // After emitting everything into that buffer, we send the first fragment heere. - // When `poll` is called again, we check if out_packet was fully sent, otherwise we - // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. - - // `dispatch_ieee802154_out_packet` requires some information about the total packet size, - // the link local source and destination address... - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - .. - } = &mut out_packet.unwrap().sixlowpan_out_packet; + cfg_if::cfg_if! { + if #[cfg(feature = "proto-sixlowpan-fragmentation")] { + // The packet does not fit in one Ieee802154 frame, so we need fragmentation. + // We do this by emitting everything in the `out_packet.buffer` from the interface. + // After emitting everything into that buffer, we send the first fragment heere. + // When `poll` is called again, we check if out_packet was fully sent, otherwise we + // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + + // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // the link local source and destination address... + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = &mut _out_packet.unwrap().sixlowpan_out_packet; - *ll_dst_addr = ll_dst_a; - *ll_src_addr = ll_src_a; + *ll_dst_addr = ll_dst_a; + *ll_src_addr = ll_src_a; - let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); - iphc_repr.emit(&mut iphc_packet); + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); - let b = &mut buffer[iphc_repr.buffer_len()..]; + let b = &mut buffer[iphc_repr.buffer_len()..]; - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( - &mut b[..udp_repr.header_len() + payload.len()], - ); - udp_repr.emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - } - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); - tcp_repr.emit( - &mut tcp_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &self.caps.checksum, - ); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - let mut icmp_packet = - Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - } - _ => return Err(Error::Unrecognized), - } + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut b[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } - *packet_len = total_size; + *packet_len = total_size; - // The datagram size that we need to set in the first fragment header is equal to the - // IPv6 payload length + 40. - *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + // The datagram size that we need to set in the first fragment header is equal to the + // IPv6 payload length + 40. + *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; - // We generate a random tag. - let tag = self.get_sixlowpan_fragment_tag(); - // We save the tag for the other fragments that will be created when calling `poll` - // multiple times. - *datagram_tag = tag; + // We generate a random tag. + let tag = self.get_sixlowpan_fragment_tag(); + // We save the tag for the other fragments that will be created when calling `poll` + // multiple times. + *datagram_tag = tag; - let frag1 = SixlowpanFragRepr::FirstFragment { - size: *datagram_size, - tag, - }; - let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, - tag, - offset: 0, - }; + let frag1 = SixlowpanFragRepr::FirstFragment { + size: *datagram_size, + tag, + }; + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag, + offset: 0, + }; - // We calculate how much data we can send in the first fragment and the other - // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight - // (except for the last fragment) since the offset field in the fragment is an offset - // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. - // - // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 - let frag1_size = ((125 - ieee_len - frag1.buffer_len() - compressed_headers_len) - & 0xffff_fff8) - + compressed_headers_len; - *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; - - *sent_bytes = frag1_size; - - tx_token.consume( - self.now, - ieee_len + frag1.buffer_len() + frag1_size, - |mut tx_buf| { - // Add the IEEE header. - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - // Add the first fragment header - let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); - frag1.emit(&mut frag1_packet); - tx_buf = &mut tx_buf[frag1.buffer_len()..]; - - // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + // We calculate how much data we can send in the first fragment and the other + // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight + // (except for the last fragment) since the offset field in the fragment is an offset + // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. + // + // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + let frag1_size = ((125 - ieee_len - frag1.buffer_len() - _compressed_headers_len) + & 0xffff_fff8) + + _compressed_headers_len; + *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + *sent_bytes = frag1_size; + + tx_token.consume( + self.now, + ieee_len + frag1.buffer_len() + frag1_size, + |mut tx_buf| { + // Add the IEEE header. + let mut ieee_packet = + Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the first fragment header + let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); + frag1.emit(&mut frag1_packet); + tx_buf = &mut tx_buf[frag1.buffer_len()..]; + + // Add the buffer part. + tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + + Ok(()) + }, + ) + } else { + net_debug!("Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support."); Ok(()) - }, - ) + } + } } else { // We don't need fragmentation, so we emit everything to the TX token. tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { @@ -3003,7 +3151,10 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "medium-ieee802154")] + #[cfg(all( + feature = "medium-ieee802154", + feature = "proto-sixlowpan-fragmentation" + ))] fn dispatch_ieee802154_out_packet( &mut self, tx_token: Tx, @@ -3162,6 +3313,11 @@ mod test { ]; let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let iface_builder = + iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); + #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); let iface = iface_builder.finalize(&mut device); @@ -3182,19 +3338,19 @@ mod test { IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), ]; - #[cfg(feature = "proto-sixlowpan")] let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .sixlowpan_out_packet_cache(vec![]) .ip_addrs(ip_addrs); - #[cfg(not(feature = "proto-sixlowpan"))] - let iface_builder = InterfaceBuilder::new() - .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .ip_addrs(ip_addrs); + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let iface_builder = iface_builder + .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .sixlowpan_out_packet_cache(vec![]); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let iface_builder = + iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -3262,7 +3418,18 @@ 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 sockets, &frame), None); + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); } #[test] @@ -3339,8 +3506,20 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame), + iface.inner.process_ipv4(&mut sockets, &frame, None), + Some(expected_repr) + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), Some(expected_repr) ); } @@ -3632,8 +3811,19 @@ mod test { }; let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!( + iface.inner.process_ipv4(&mut sockets, &frame, None), + Some(expected_packet) + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame), + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), Some(expected_packet) ); } @@ -3682,8 +3872,8 @@ mod test { ); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let ip_repr = Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, + src_addr, + dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, @@ -3788,7 +3978,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -3863,7 +4053,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected @@ -3910,7 +4100,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), None ); @@ -3962,7 +4152,7 @@ mod test { assert_eq!( iface .inner - .process_ethernet(&mut sockets, frame.into_inner()), + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -4289,8 +4479,8 @@ mod test { &ChecksumCapabilities::default(), ); let ipv4_repr = Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, + src_addr, + dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + PAYLOAD_LEN, @@ -4314,7 +4504,17 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); } #[test] @@ -4370,8 +4570,8 @@ mod test { &ChecksumCapabilities::default(), ); let ipv4_repr = Ipv4Repr { - src_addr: src_addr, - dst_addr: dst_addr, + src_addr, + dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), @@ -4396,7 +4596,17 @@ mod test { Ipv4Packet::new_unchecked(&bytes) }; - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame), None); + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP let socket = sockets.get_mut::(udp_socket_handle); diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 0d7c2e8b7..bf8ec9644 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -4,7 +4,7 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram provides lookup and caching of hardware addresses, and handles management packets. */ -#[cfg(feature = "proto-sixlowpan")] +#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] mod fragmentation; mod interface; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -22,7 +22,7 @@ pub use self::neighbor::Neighbor; pub use self::route::{Route, Routes}; pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; -#[cfg(feature = "proto-sixlowpan")] +#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as FragmentsCache}; pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 243d948c4..672d0cec3 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -21,6 +21,13 @@ pub use super::IpProtocol as Protocol; // accept a packet of the following size. pub const MIN_MTU: usize = 576; +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +pub struct Key { + id: u16, + src_addr: Address, + dst_addr: Address, +} + /// A four-octet IPv4 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct Address(pub [u8; 4]); @@ -443,6 +450,15 @@ impl> Packet { let data = self.buffer.as_ref(); checksum::data(&data[..self.header_len() as usize]) == !0 } + + /// Returns the key for identifying the packet. + pub fn get_key(&self) -> Key { + Key { + id: self.ident(), + src_addr: self.src_addr(), + dst_addr: self.dst_addr(), + } + } } impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { @@ -617,15 +633,14 @@ impl Repr { if checksum_caps.ipv4.rx() && !packet.verify_checksum() { return Err(Error); } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] // We do not support fragmentation. if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error); } - // Since the packet is not fragmented, it must include the entire payload. + let payload_len = packet.total_len() as usize - packet.header_len() as usize; - if packet.payload().len() < payload_len { - return Err(Error); - } // All DSCP values are acceptable, since they are of no concern to receiving endpoint. // All ECN values are acceptable, since ECN requires opt-in from both endpoints. @@ -634,7 +649,7 @@ impl Repr { src_addr: packet.src_addr(), dst_addr: packet.dst_addr(), next_header: packet.next_header(), - payload_len: payload_len, + payload_len, hop_limit: packet.hop_limit(), }) } @@ -749,9 +764,20 @@ impl> PrettyPrint for Packet { Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) { Err(_) => return Ok(()), Ok(ip_repr) => { - write!(f, "{}{}", indent, ip_repr)?; - format_checksum(f, ip_packet.verify_checksum())?; - (ip_repr, ip_packet.payload()) + if ip_packet.more_frags() || ip_packet.frag_offset() != 0 { + write!( + f, + "{}IPv4 Fragment more_frags={} offset={}", + indent, + ip_packet.more_frags(), + ip_packet.frag_offset() + )?; + return Ok(()); + } else { + write!(f, "{}{}", indent, ip_repr)?; + format_checksum(f, ip_packet.verify_checksum())?; + (ip_repr, ip_packet.payload()) + } } }, }; diff --git a/src/wire/mod.rs b/src/wire/mod.rs index ab3b9b167..6b097620f 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -169,8 +169,8 @@ pub use self::ip::{ #[cfg(feature = "proto-ipv4")] pub use self::ipv4::{ - Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr, - HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, + Address as Ipv4Address, Cidr as Ipv4Cidr, Key as Ipv4FragKey, Packet as Ipv4Packet, + Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, }; #[cfg(feature = "proto-ipv6")] diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 69caadd81..79822262b 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -359,6 +359,7 @@ pub mod frag { } impl Repr { + #[cfg(feature = "proto-sixlowpan-fragmentation")] pub(crate) fn set_offset(&mut self, value: u8) { match self { Repr::FirstFragment { .. } => (), @@ -2105,15 +2106,15 @@ mod test { .reserve_with_key(&key) .unwrap() .start( - frag.datagram_size() as usize - uncompressed + compressed, - Instant::now(), + Some(frag.datagram_size() as usize - uncompressed + compressed), + Instant::now() + crate::time::Duration::from_secs(60), -((uncompressed - compressed) as isize), ) .unwrap(); frags_cache .get_packet_assembler_mut(&key) .unwrap() - .add(frag.payload(), 0, Instant::now()) + .add(frag.payload(), 0) .unwrap(); let frame2: &[u8] = &[ @@ -2149,11 +2150,7 @@ mod test { frags_cache .get_packet_assembler_mut(&key) .unwrap() - .add( - frag.payload(), - frag.datagram_offset() as usize * 8, - Instant::now(), - ) + .add(frag.payload(), frag.datagram_offset() as usize * 8) .unwrap(); let frame3: &[u8] = &[ @@ -2188,11 +2185,7 @@ mod test { frags_cache .get_packet_assembler_mut(&key) .unwrap() - .add( - frag.payload(), - frag.datagram_offset() as usize * 8, - Instant::now(), - ) + .add(frag.payload(), frag.datagram_offset() as usize * 8) .unwrap(); let assembled_packet = frags_cache.get_assembled_packet(&key).unwrap(); From c459961ab1312496349b37a20431b837b55f42fe Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 13 Jun 2022 16:19:02 +0200 Subject: [PATCH 354/566] Fix 6LoWPAN address compression --- src/wire/sixlowpan.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 79822262b..a69fac9dd 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -910,7 +910,7 @@ pub mod iphc { ) -> usize { self.set_cid_field(0); self.set_sac_field(0); - self.set_sam_field(0b11); + self.set_sam_field(0b00); let src = src_addr.as_bytes(); if src_addr == ipv6::Address::UNSPECIFIED { self.set_sac_field(1); @@ -958,6 +958,7 @@ pub mod iphc { } } else { // We cannot elide anything. + self.set_sam_field(0b00); self.set_field(idx, src); idx += 16; } From 7727e874045e7b7869dc3ebb0fbf336b2f55d009 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 15 Jun 2022 10:15:19 +0200 Subject: [PATCH 355/566] Fix doc of all routers, all nodes addresses --- src/wire/ipv6.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index a54fbbd29..563e0be42 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -26,17 +26,17 @@ impl Address { /// [unspecified address]: https://tools.ietf.org/html/rfc4291#section-2.5.2 pub const UNSPECIFIED: Address = Address([0x00; 16]); - /// The link-local [all routers multicast address]. + /// The link-local [all nodes multicast address]. /// - /// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 + /// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 pub const LINK_LOCAL_ALL_NODES: Address = Address([ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ]); - /// The link-local [all nodes multicast address]. + /// The link-local [all routers multicast address]. /// - /// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 + /// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, From b5498bce5e3af4e19977d7482ad0a9b45fb84124 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 16 Jun 2022 15:37:56 +0200 Subject: [PATCH 356/566] Update `poll_at` for sixlowpan outgoing packets When there is still data to be transmitted, `poll_at` should return `Some(Instant::from_millis(0))`. --- src/iface/interface.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 0f703280c..7419599dc 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -43,6 +43,14 @@ pub(crate) struct OutPackets<'a> { _lifetime: core::marker::PhantomData<&'a ()>, } +impl<'a> OutPackets<'a> { + #[cfg(feature = "proto-sixlowpan-fragmentation")] + /// Returns `true` when all the data of the outgoing buffers are transmitted. + fn all_transmitted(&self) -> bool { + self.sixlowpan_out_packet.finished() || self.sixlowpan_out_packet.is_empty() + } +} + #[allow(unused)] #[cfg(feature = "proto-sixlowpan")] pub(crate) struct SixlowpanOutPacket<'a> { @@ -81,6 +89,29 @@ impl<'a> SixlowpanOutPacket<'a> { ll_src_addr: Ieee802154Address::Absent, } } + + /// Return `true` when everything is transmitted. + #[inline] + fn finished(&self) -> bool { + self.packet_len == self.sent_bytes + } + + /// Returns `true` when there is nothing to transmit. + #[inline] + fn is_empty(&self) -> bool { + self.packet_len == 0 + } + + // Reset the buffer. + fn reset(&mut self) { + self.packet_len = 0; + self.datagram_size = 0; + self.datagram_tag = 0; + self.sent_bytes = 0; + self.fragn_size = 0; + self.ll_dst_addr = Ieee802154Address::Absent; + self.ll_src_addr = Ieee802154Address::Absent; + } } macro_rules! check { @@ -916,6 +947,11 @@ impl<'a> Interface<'a> { pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { self.inner.now = timestamp; + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if !self.out_packets.all_transmitted() { + return Some(Instant::from_millis(0)); + } + let inner = &mut self.inner; sockets @@ -1197,6 +1233,11 @@ impl<'a> Interface<'a> { ) { net_debug!("failed to transmit: {}", e); } + + // Reset the buffer when we transmitted everything. + if self.out_packets.sixlowpan_out_packet.finished() { + self.out_packets.sixlowpan_out_packet.reset(); + } } Err(e) => { net_debug!("failed to transmit: {}", e); From f04a44d6bc25650e5c9052003b2f2376f85717f9 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 16 Jun 2022 15:39:16 +0200 Subject: [PATCH 357/566] Update 6lowpan example --- examples/sixlowpan.rs | 25 ++++++++++++++++++------- src/iface/fragmentation.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index fa000f3ef..0e58aae8e 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -86,18 +86,29 @@ fn main() { 64, )]; - let cache = FragmentsCache::new(vec![], BTreeMap::new()); - - let mut out_packet_buffer = [0u8; 1280]; - let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) - .neighbor_cache(neighbor_cache) - .sixlowpan_fragments_cache(cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .neighbor_cache(neighbor_cache); + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let mut out_packet_buffer = [0u8; 1280]; + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + builder = builder + .sixlowpan_fragments_cache(sixlowpan_frag_cache) + .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + } + let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 72135b0c1..228d7e528 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -1,5 +1,7 @@ #![allow(unused)] +use core::fmt; + use managed::{ManagedMap, ManagedSlice}; use crate::storage::Assembler; @@ -26,6 +28,23 @@ enum AssemblerState { }, } +impl fmt::Display for AssemblerState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AssemblerState::NotInit => write!(f, "Not init")?, + AssemblerState::Assembling { + assembler, + total_size, + expires_at, + offset_correction, + } => { + write!(f, "{} expires at {}", assembler, expires_at)?; + } + } + Ok(()) + } +} + impl<'a> PacketAssembler<'a> { /// Create a new empty buffer for fragments. pub fn new(storage: S) -> Self @@ -151,8 +170,16 @@ impl<'a> PacketAssembler<'a> { let len = data.len(); self.buffer[offset..][..len].copy_from_slice(data); + net_debug!( + "frag assembler: receiving {} octests at offset {}", + len, + offset + ); + match assembler.add(offset, data.len()) { Ok(overlap) => { + net_debug!("assembler: {}", self.assembler); + if overlap { net_debug!("packet was added, but there was an overlap."); } From 75bacb1e46b5e5a0fa0c758ebbc7bbc963fb5bf3 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 16 Jun 2022 15:40:51 +0200 Subject: [PATCH 358/566] Remove redundant setting of 6LoWPAN header field --- src/wire/sixlowpan.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index a69fac9dd..a231433b6 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -910,7 +910,6 @@ pub mod iphc { ) -> usize { self.set_cid_field(0); self.set_sac_field(0); - self.set_sam_field(0b00); let src = src_addr.as_bytes(); if src_addr == ipv6::Address::UNSPECIFIED { self.set_sac_field(1); From cdc842dcf5deae0b2c8dda979d10374f677dd458 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 15 Jun 2022 10:40:40 +0200 Subject: [PATCH 359/566] Fix checkusm check for UDP From the RFC: > If the computed checksum is zero, it is transmitted as all ones (the > equivalent in one's complement arithmetic). An all zero transmitted > checksum value means that the transmitter generated no checksum (for > debugging or for higher level protocols that don't care). Fixes #460 . --- src/wire/udp.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/wire/udp.rs b/src/wire/udp.rs index ba88d8705..2f759f0d9 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -115,6 +115,14 @@ impl> Packet { return true; } + // From the RFC: + // > An all zero transmitted checksum value means that the transmitter + // > generated no checksum (for debugging or for higher level protocols + // > that don't care). + if self.checksum() == 0 { + return true; + } + let data = self.buffer.as_ref(); checksum::combine(&[ checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32), @@ -373,6 +381,18 @@ mod test { assert_eq!(packet.checksum(), 0xffff); } + #[test] + #[cfg(feature = "proto-ipv4")] + fn test_no_checksum() { + let mut bytes = vec![0; 8]; + let mut packet = Packet::new_unchecked(&mut bytes); + packet.set_src_port(1); + packet.set_dst_port(31881); + packet.set_len(8); + packet.set_checksum(0); + assert!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into())); + } + #[cfg(feature = "proto-ipv4")] fn packet_repr() -> Repr { Repr { From 4767aadcb43e24df8390ddad142201336c87fc76 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:57:56 +0200 Subject: [PATCH 360/566] Update sixlowpan fuzzer --- fuzz/Cargo.toml | 10 +- fuzz/fuzz_targets/sixlowpan_iphc_header.rs | 42 ---- fuzz/fuzz_targets/sixlowpan_packet.rs | 240 +++++++++++++++++++++ fuzz/fuzz_targets/sixlowpan_udp_header.rs | 41 ---- 4 files changed, 242 insertions(+), 91 deletions(-) delete mode 100644 fuzz/fuzz_targets/sixlowpan_iphc_header.rs create mode 100644 fuzz/fuzz_targets/sixlowpan_packet.rs delete mode 100644 fuzz/fuzz_targets/sixlowpan_udp_header.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 98bd4d0f8..b3357eba8 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -43,13 +43,7 @@ test = false doc = false [[bin]] -name = "sixlowpan_udp_header" -path = "fuzz_targets/sixlowpan_udp_header.rs" -test = false -doc = false - -[[bin]] -name = "sixlowpan_iphc_header" -path = "fuzz_targets/sixlowpan_iphc_header.rs" +name = "sixlowpan_packet" +path = "fuzz_targets/sixlowpan_packet.rs" test = false doc = false diff --git a/fuzz/fuzz_targets/sixlowpan_iphc_header.rs b/fuzz/fuzz_targets/sixlowpan_iphc_header.rs deleted file mode 100644 index 3ed012b0c..000000000 --- a/fuzz/fuzz_targets/sixlowpan_iphc_header.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![no_main] -use libfuzzer_sys::fuzz_target; -use smoltcp::wire::{Ieee802154Address, SixlowpanIphcPacket, SixlowpanIphcRepr}; - -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] -pub enum AddressFuzzer { - Absent, - Short([u8; 2]), - Extended([u8; 8]), -} - -impl From for Ieee802154Address { - fn from(val: AddressFuzzer) -> Self { - match val { - AddressFuzzer::Absent => Ieee802154Address::Absent, - AddressFuzzer::Short(b) => Ieee802154Address::Short(b), - AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b), - } - } -} - -#[derive(Debug, arbitrary::Arbitrary)] -struct SixlowpanIphcPacketFuzzer<'a> { - data: &'a [u8], - ll_src_addr: Option, - ll_dst_addr: Option, -} - -fuzz_target!(|fuzz: SixlowpanIphcPacketFuzzer| { - if let Ok(ref frame) = SixlowpanIphcPacket::new_checked(fuzz.data) { - if let Ok(repr) = SixlowpanIphcRepr::parse( - frame, - fuzz.ll_src_addr.map(Into::into), - fuzz.ll_dst_addr.map(Into::into), - ) { - let mut buffer = vec![0; repr.buffer_len()]; - - let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); - repr.emit(&mut frame); - } - }; -}); diff --git a/fuzz/fuzz_targets/sixlowpan_packet.rs b/fuzz/fuzz_targets/sixlowpan_packet.rs new file mode 100644 index 000000000..d9ba99bb8 --- /dev/null +++ b/fuzz/fuzz_targets/sixlowpan_packet.rs @@ -0,0 +1,240 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::{phy::ChecksumCapabilities, wire::*}; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] +pub enum AddressFuzzer { + Absent, + Short([u8; 2]), + Extended([u8; 8]), +} + +impl From for Ieee802154Address { + fn from(val: AddressFuzzer) -> Self { + match val { + AddressFuzzer::Absent => Ieee802154Address::Absent, + AddressFuzzer::Short(b) => Ieee802154Address::Short(b), + AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b), + } + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct SixlowpanPacketFuzzer<'a> { + data: &'a [u8], + ll_src_addr: Option, + ll_dst_addr: Option, +} + +fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { + match SixlowpanPacket::dispatch(fuzz.data) { + Ok(SixlowpanPacket::FragmentHeader) => { + if let Ok(frame) = SixlowpanFragPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanFragRepr::parse(&frame) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut frame = SixlowpanFragPacket::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + } + } + Ok(SixlowpanPacket::IphcHeader) => { + if let Ok(frame) = SixlowpanIphcPacket::new_checked(fuzz.data) { + if let Ok(iphc_repr) = SixlowpanIphcRepr::parse( + &frame, + fuzz.ll_src_addr.map(Into::into), + fuzz.ll_dst_addr.map(Into::into), + ) { + let mut buffer = vec![0; iphc_repr.buffer_len()]; + let mut iphc_frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); + iphc_repr.emit(&mut iphc_frame); + + let payload = frame.payload(); + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + if let Ok(p) = SixlowpanNhcPacket::dispatch(payload) { + match p { + SixlowpanNhcPacket::ExtHeader => { + if let Ok(frame) = + SixlowpanExtHeaderPacket::new_checked(payload) + { + if let Ok(repr) = SixlowpanExtHeaderRepr::parse(&frame) + { + let mut buffer = vec![0; repr.buffer_len()]; + let mut ext_header_frame = + SixlowpanExtHeaderPacket::new_unchecked( + &mut buffer[..], + ); + repr.emit(&mut ext_header_frame); + } + } + } + SixlowpanNhcPacket::UdpHeader => { + if let Ok(frame) = + SixlowpanUdpNhcPacket::new_checked(payload) + { + if let Ok(repr) = SixlowpanUdpNhcRepr::parse( + &frame, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + ) { + let mut buffer = vec![ + 0; + repr.header_len() + + frame.payload().len() + ]; + let mut udp_packet = + SixlowpanUdpNhcPacket::new_unchecked( + &mut buffer[..], + ); + repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + frame.payload().len(), + |b| b.copy_from_slice(frame.payload()), + ); + } + } + } + } + } + } + SixlowpanNextHeader::Uncompressed(proto) => match proto { + IpProtocol::HopByHop => { + if let Ok(frame) = Ipv6HopByHopHeader::new_checked(payload) { + if let Ok(repr) = Ipv6HopByHopRepr::parse(&frame) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut hop_by_hop_frame = + Ipv6HopByHopHeader::new_unchecked(&mut buffer[..]); + repr.emit(&mut hop_by_hop_frame); + } + } + } + IpProtocol::Icmp => { + if let Ok(frame) = Icmpv4Packet::new_checked(payload) { + if let Ok(repr) = + Icmpv4Repr::parse(&frame, &ChecksumCapabilities::default()) + { + let mut buffer = vec![0; repr.buffer_len()]; + let mut icmpv4_packet = + Icmpv4Packet::new_unchecked(&mut buffer[..]); + repr.emit( + &mut icmpv4_packet, + &ChecksumCapabilities::default(), + ); + } + } + } + IpProtocol::Igmp => { + if let Ok(frame) = IgmpPacket::new_checked(payload) { + if let Ok(repr) = IgmpRepr::parse(&frame) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut frame = IgmpPacket::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + } + } + IpProtocol::Tcp => { + if let Ok(frame) = TcpPacket::new_checked(payload) { + if let Ok(repr) = TcpRepr::parse( + &frame, + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + &ChecksumCapabilities::default(), + ) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut frame = TcpPacket::new_unchecked(&mut buffer[..]); + repr.emit( + &mut frame, + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + &ChecksumCapabilities::default(), + ); + } + } + } + IpProtocol::Udp => { + if let Ok(frame) = UdpPacket::new_checked(payload) { + if let Ok(repr) = UdpRepr::parse( + &frame, + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + &ChecksumCapabilities::default(), + ) { + let mut buffer = + vec![0; repr.header_len() + frame.payload().len()]; + let mut packet = UdpPacket::new_unchecked(&mut buffer[..]); + repr.emit( + &mut packet, + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + frame.payload().len(), + |b| b.copy_from_slice(frame.payload()), + &ChecksumCapabilities::default(), + ); + } + } + } + IpProtocol::Ipv6Route => { + if let Ok(frame) = Ipv6RoutingHeader::new_checked(payload) { + if let Ok(repr) = Ipv6RoutingRepr::parse(&frame) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut packet = Ipv6RoutingHeader::new(&mut buffer[..]); + repr.emit(&mut packet); + } + } + } + IpProtocol::Ipv6Frag => { + if let Ok(frame) = Ipv6FragmentHeader::new_checked(payload) { + if let Ok(repr) = Ipv6FragmentRepr::parse(&frame) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut frame = + Ipv6FragmentHeader::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + } + } + IpProtocol::Icmpv6 => { + if let Ok(packet) = Icmpv6Packet::new_checked(payload) { + if let Ok(repr) = Icmpv6Repr::parse( + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + &packet, + &ChecksumCapabilities::default(), + ) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut packet = + Icmpv6Packet::new_unchecked(&mut buffer[..]); + repr.emit( + &iphc_repr.src_addr.into_address(), + &iphc_repr.dst_addr.into_address(), + &mut packet, + &ChecksumCapabilities::default(), + ); + } + } + } + IpProtocol::Ipv6NoNxt => (), + IpProtocol::Ipv6Opts => { + if let Ok(packet) = Ipv6Option::new_checked(payload) { + if let Ok(repr) = Ipv6OptionRepr::parse(&packet) { + let mut buffer = vec![0; repr.buffer_len()]; + let mut packet = Ipv6Option::new_unchecked(&mut buffer[..]); + repr.emit(&mut packet); + } + } + } + IpProtocol::Unknown(_) => (), + }, + }; + + let mut buffer = vec![0; iphc_repr.buffer_len()]; + + let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); + iphc_repr.emit(&mut frame); + } + }; + } + Err(_) => (), + } +}); diff --git a/fuzz/fuzz_targets/sixlowpan_udp_header.rs b/fuzz/fuzz_targets/sixlowpan_udp_header.rs deleted file mode 100644 index de876a5e9..000000000 --- a/fuzz/fuzz_targets/sixlowpan_udp_header.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![no_main] -use libfuzzer_sys::fuzz_target; -use smoltcp::wire::{Ipv6Address, SixlowpanUdpNhcPacket, SixlowpanUdpNhcRepr}; - -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] -pub struct AddressFuzzer(pub [u8; 16]); - -impl From for Ipv6Address { - fn from(val: AddressFuzzer) -> Self { - Ipv6Address(val.0) - } -} - -#[derive(Debug, arbitrary::Arbitrary)] -struct SixlowpanUdpPacketFuzzer<'a> { - data: &'a [u8], - src_addr: AddressFuzzer, - dst_addr: AddressFuzzer, -} - -fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| { - if let Ok(ref frame) = SixlowpanUdpNhcPacket::new_checked(fuzz.data) { - if let Ok(repr) = SixlowpanUdpNhcRepr::parse( - frame, - &fuzz.src_addr.into(), - &fuzz.dst_addr.into(), - ) { - let payload = frame.payload(); - let mut buffer = vec![0; repr.header_len() + payload.len()]; - - let mut frame = SixlowpanUdpNhcPacket::new_unchecked(&mut buffer[..]); - repr.emit( - &mut frame, - &fuzz.src_addr.into(), - &fuzz.dst_addr.into(), - payload.len(), - |b| b.copy_from_slice(payload), - ); - } - }; -}); From 14d52219347f642bdd3a6545e148ec5cf8acd738 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:58:31 +0200 Subject: [PATCH 361/566] Fix flow label for 6LoWPAN --- src/wire/sixlowpan.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index a69fac9dd..897e801de 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -616,17 +616,15 @@ pub mod iphc { } /// Return the flow label field (when it is inlined). - pub fn flow_label_field(&self) -> Option { + pub fn flow_label_field(&self) -> Option { match self.tf_field() { 0b00 => { let start = self.ip_fields_start() as usize; - let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]); - Some(raw & 0xfffff) + Some(NetworkEndian::read_u16(&self.buffer.as_ref()[start..][2..4])) } 0b01 => { let start = self.ip_fields_start() as usize; - let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]) >> 8; - Some(raw & 0xfffff) + Some(NetworkEndian::read_u16(&self.buffer.as_ref()[start..][1..3])) } 0b10 | 0b11 => None, _ => unreachable!(), @@ -1075,7 +1073,7 @@ pub mod iphc { // TODO(thvdveld): refactor the following fields into something else pub ecn: Option, pub dscp: Option, - pub flow_label: Option, + pub flow_label: Option, } impl Repr { From b0e4050659f63e7537b6b774f56286b362a416fc Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:59:01 +0200 Subject: [PATCH 362/566] Fix address resolving for 6LoWPAN --- src/wire/sixlowpan.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 897e801de..ef5595a5e 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -69,15 +69,17 @@ impl<'a> UnresolvedAddress<'a> { } AddressMode::FullyElided => { bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]); - match ll_address.unwrap() { - LlAddress::Short(ll) => { + match ll_address { + Some(LlAddress::Short(ll)) => { bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); bytes[14..].copy_from_slice(&ll); } - LlAddress::Extended(_) => { - bytes[8..].copy_from_slice(&ll_address.unwrap().as_eui_64().unwrap()); - } - LlAddress::Absent => return Err(Error), + Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() { + Some(addr) => bytes[8..].copy_from_slice(&addr), + None => return Err(Error), + }, + Some(LlAddress::Absent) => return Err(Error), + None => return Err(Error), } Ok(ipv6::Address::from_bytes(&bytes[..])) } @@ -134,6 +136,10 @@ impl SixlowpanPacket { pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result { let raw = buffer.as_ref(); + if raw.is_empty() { + return Err(Error); + } + if raw[0] >> 3 == DISPATCH_FIRST_FRAGMENT_HEADER || raw[0] >> 3 == DISPATCH_FRAGMENT_HEADER { Ok(Self::FragmentHeader) From c02559121c04efb457226c702f21803cd6521092 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 14:15:52 +0200 Subject: [PATCH 363/566] Avoid multiplication overflow --- src/wire/ipv6hopbyhop.rs | 4 ++-- src/wire/ipv6routing.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 09d2ca5b4..0fd9e7a76 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -43,8 +43,8 @@ mod field { // Length of the header is in 8-octet units, not including the first 8 octets. The first two // octets are the next header type and the header length. pub fn OPTIONS(length_field: u8) -> Field { - let bytes = length_field * 8 + 8; - 2..bytes as usize + let bytes = length_field as usize * 8 + 8; + 2..bytes } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 56a16fa03..2b28ed36c 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -92,8 +92,8 @@ mod field { // Length of the header is in 8-octet units, not including the first 8 octets. The first four // octets are the next header type, the header length, routing type and segments left. pub fn DATA(length_field: u8) -> Field { - let bytes = length_field * 8 + 8; - 4..bytes as usize + let bytes = length_field as usize * 8 + 8; + 4..bytes } // The Type 2 Routing Header has the following format: From 3057ee5b30825396d19689721723b17ff36a0631 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 14:17:51 +0200 Subject: [PATCH 364/566] Check for correct header length field for `IPv6Routing::Type2` An IPv6Routing::Type2 header could contain a header length that does not match the real length of such a header. Since the header length is used for calculating offsets in the getter/setter/payload functions, we need to check if this is correctly set. --- src/wire/ipv6routing.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 2b28ed36c..d4ca2b968 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -173,6 +173,13 @@ impl> Header { return Err(Error); } + // The header lenght field could be wrong and thus we need to check this as well: + if matches!(self.routing_type(), Type::Type2) + && field::DATA(self.header_len()).end != field::HOME_ADDRESS.end + { + return Err(Error); + } + Ok(()) } From 14d5d03e557e4e9c5ee3e77fcf5ca7f54003b559 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 14:21:07 +0200 Subject: [PATCH 365/566] NDiscOption header with data length field 0 is invalid --- src/wire/ndiscoption.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 658b96a8c..f46095bec 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -477,11 +477,18 @@ impl<'a> Repr<'a> { Err(Error) } } - Type::Unknown(id) => Ok(Repr::Unknown { - type_: id, - length: opt.data_len(), - data: opt.data(), - }), + Type::Unknown(id) => { + // A length of 0 is invalid. + if opt.data_len() != 0 { + Ok(Repr::Unknown { + type_: id, + length: opt.data_len(), + data: opt.data(), + }) + } else { + Err(Error) + } + } } } From 7f73afa6416f9a32e0038547729e8e55579f1dfb Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 14:31:58 +0200 Subject: [PATCH 366/566] Data length field of 0 is invalid for ndisc option --- src/wire/ndiscoption.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index f46095bec..ce5899ef6 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -158,6 +158,12 @@ impl> NdiscOption { pub fn new_checked(buffer: T) -> Result> { let opt = Self::new_unchecked(buffer); opt.check_len()?; + + // A data length field of 0 is invalid. + if opt.data_len() == 0 { + return Err(Error); + } + Ok(opt) } From 054ebd8f1bbb2aadf83335d306ef8dd660ca534f Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 14:51:13 +0200 Subject: [PATCH 367/566] Fix offset calculation for NdiscOption --- src/wire/ndisc.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 789167345..b8e6dce21 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -314,8 +314,9 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(&packet.payload()[offset..])?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => { - lladdr = Some(opt.link_layer_addr()); - offset += 8; + let addr = opt.link_layer_addr(); + offset += NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len(); + lladdr = Some(addr); } NdiscOptionType::RedirectedHeader => { if opt.data_len() < 6 { @@ -351,7 +352,9 @@ impl<'a> Repr<'a> { pub fn buffer_len(&self) -> usize { match self { &Repr::RouterSolicit { lladdr } => match lladdr { - Some(_) => field::UNUSED.end + 8, + Some(addr) => { + field::UNUSED.end + { NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len() } + } None => field::UNUSED.end, }, &Repr::RouterAdvert { @@ -361,8 +364,8 @@ impl<'a> Repr<'a> { .. } => { let mut offset = 0; - if lladdr.is_some() { - offset += 8; + if let Some(lladdr) = lladdr { + offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len(); } if mtu.is_some() { offset += 8; @@ -385,8 +388,8 @@ impl<'a> Repr<'a> { .. } => { let mut offset = 0; - if lladdr.is_some() { - offset += 8; + if let Some(lladdr) = lladdr { + offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len(); } if let Some(NdiscRedirectedHeader { header, data }) = redirected_hdr { offset += 8 + header.buffer_len() + data.len(); @@ -439,7 +442,7 @@ impl<'a> Repr<'a> { let mut opt_pkt = NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt); - offset += 8; + offset += NdiscOptionRepr::Mtu(mtu).buffer_len(); } if let Some(prefix_info) = prefix_info { let mut opt_pkt = @@ -493,7 +496,7 @@ impl<'a> Repr<'a> { Some(lladdr) => { let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); - 8 + NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len() } None => 0, }; From eb3846da17b4ce37448f7c88159f3eb68bdb689d Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:42:36 +0200 Subject: [PATCH 368/566] Fix Compressed Extension Header functions --- src/wire/sixlowpan.rs | 45 +++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index ef5595a5e..91033b99f 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -626,11 +626,15 @@ pub mod iphc { match self.tf_field() { 0b00 => { let start = self.ip_fields_start() as usize; - Some(NetworkEndian::read_u16(&self.buffer.as_ref()[start..][2..4])) + Some(NetworkEndian::read_u16( + &self.buffer.as_ref()[start..][2..4], + )) } 0b01 => { let start = self.ip_fields_start() as usize; - Some(NetworkEndian::read_u16(&self.buffer.as_ref()[start..][1..3])) + Some(NetworkEndian::read_u16( + &self.buffer.as_ref()[start..][1..3], + )) } 0b10 | 0b11 => None, _ => unreachable!(), @@ -1375,6 +1379,9 @@ pub mod nhc { /// dispatch is recognized. pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result { let raw = buffer.as_ref(); + if raw.is_empty() { + return Err(Error); + } if raw[0] >> 4 == DISPATCH_EXT_HEADER { // We have a compressed IPv6 Extension Header. @@ -1434,6 +1441,11 @@ pub mod nhc { pub fn new_checked(buffer: T) -> Result { let packet = Self::new_unchecked(buffer); packet.check_len()?; + + if packet.eid_field() > 7 { + return Err(Error); + } + Ok(packet) } @@ -1441,10 +1453,18 @@ pub mod nhc { /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); + if buffer.is_empty() { - Err(Error) - } else { + return Err(Error); + } + + let mut len = 1; + len += self.next_header_size(); + + if len <= buffer.len() { Ok(()) + } else { + Err(Error) } } @@ -1471,14 +1491,6 @@ pub mod nhc { } } - /// Return the length field. - pub fn length_field(&self) -> u8 { - let start = 1 + self.next_header_size(); - - let data = self.buffer.as_ref(); - data[start] - } - /// Parse the next header field. pub fn next_header(&self) -> NextHeader { if self.nh_field() == 1 { @@ -1507,7 +1519,7 @@ pub mod nhc { impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> { /// Return a pointer to the payload. pub fn payload(&self) -> &'a [u8] { - let start = 2 + self.next_header_size(); + let start = 1 + self.next_header_size(); &self.buffer.as_ref()[start..] } } @@ -1536,8 +1548,8 @@ pub mod nhc { ExtHeaderId::FragmentHeader => 2, ExtHeaderId::DestinationOptionsHeader => 3, ExtHeaderId::MobilityHeader => 4, + ExtHeaderId::Reserved => 5, ExtHeaderId::Header => 7, - _ => unreachable!(), }; self.set_eid_field(id); @@ -1963,11 +1975,11 @@ pub mod nhc { let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]; let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap(); + assert_eq!(packet.next_header_size(), 0); assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER); - assert_eq!(packet.length_field(), 6); assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader); - assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); + assert_eq!(packet.payload(), [0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); } #[test] @@ -1985,7 +1997,6 @@ pub mod nhc { assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER); assert_eq!(packet.next_header(), NextHeader::Compressed); - assert_eq!(packet.length_field(), 6); assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader); } From 7ee06df264affab79a9c7208d2293c98ca1c0150 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:44:15 +0200 Subject: [PATCH 369/566] Fix Redirected Header for Ndisc Options --- src/wire/ndisc.rs | 42 ++++++++++++++++++++++------------------- src/wire/ndiscoption.rs | 26 ++++++++++++------------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index b8e6dce21..843845f8c 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -319,19 +319,21 @@ impl<'a> Repr<'a> { lladdr = Some(addr); } NdiscOptionType::RedirectedHeader => { - if opt.data_len() < 6 { + let opt_data = opt.data(); + + if opt.data_len() < 6 || opt_data.len() < offset + 8 { return Err(Error); - } else { - let ip_packet = - Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]); - let ip_repr = Ipv6Repr::parse(&ip_packet)?; - let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..]; - redirected_hdr = Some(NdiscRedirectedHeader { - header: ip_repr, - data, - }); - offset += 8 + ip_repr.buffer_len() + data.len(); - } + }; + + let ip_packet = Ipv6Packet::new_checked(&opt_data[offset + 8..])?; + let ip_repr = Ipv6Repr::parse(&ip_packet)?; + let data = ip_packet.payload(); + let redirected = NdiscRedirectedHeader { + header: ip_repr, + data, + }; + offset += NdiscOptionRepr::RedirectedHeader(redirected).buffer_len(); + redirected_hdr = Some(redirected); } _ => { return Err(Error); @@ -367,11 +369,11 @@ impl<'a> Repr<'a> { if let Some(lladdr) = lladdr { offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len(); } - if mtu.is_some() { - offset += 8; + if let Some(mtu) = mtu { + offset += NdiscOptionRepr::Mtu(mtu).buffer_len(); } - if prefix_info.is_some() { - offset += 32; + if let Some(prefix_info) = prefix_info { + offset += NdiscOptionRepr::PrefixInformation(prefix_info).buffer_len(); } field::RETRANS_TM.end + offset } @@ -387,14 +389,16 @@ impl<'a> Repr<'a> { redirected_hdr, .. } => { - let mut offset = 0; + let mut offset = field::DEST_ADDR.end; if let Some(lladdr) = lladdr { offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len(); } if let Some(NdiscRedirectedHeader { header, data }) = redirected_hdr { - offset += 8 + header.buffer_len() + data.len(); + offset += + NdiscOptionRepr::RedirectedHeader(NdiscRedirectedHeader { header, data }) + .buffer_len(); } - field::DEST_ADDR.end + offset + offset } } } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index ce5899ef6..63f9a2e15 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -128,9 +128,7 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Reserved bits. - pub const IP_RESERVED: Field = 4..8; - // Redirected header IP header + data. - pub const IP_DATA: usize = 8; + pub const REDIRECTED_RESERVED: Field = 2..8; pub const REDIR_MIN_SZ: usize = 48; // MTU Option fields @@ -368,7 +366,7 @@ impl + AsMut<[u8]>> NdiscOption { #[inline] pub fn clear_redirected_reserved(&mut self) { let data = self.buffer.as_mut(); - NetworkEndian::write_u32(&mut data[field::IP_RESERVED], 0); + data[field::REDIRECTED_RESERVED].fill_with(|| 0); } } @@ -468,11 +466,13 @@ impl<'a> Repr<'a> { if opt.data_len() < 6 { Err(Error) } else { - let ip_packet = Ipv6Packet::new_unchecked(&opt.data()[field::IP_DATA..]); + let ip_packet = + Ipv6Packet::new_unchecked(&opt.data()[field::REDIRECTED_RESERVED.len()..]); let ip_repr = Ipv6Repr::parse(&ip_packet)?; Ok(Repr::RedirectedHeader(RedirectedHeader { header: ip_repr, - data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..], + data: &opt.data() + [field::REDIRECTED_RESERVED.len() + ip_repr.buffer_len()..], })) } } @@ -508,7 +508,7 @@ impl<'a> Repr<'a> { } &Repr::PrefixInformation(_) => field::PREFIX.end, &Repr::RedirectedHeader(RedirectedHeader { header, data }) => { - field::IP_DATA + header.buffer_len() + data.len() + (8 + header.buffer_len() + data.len() + 7) / 8 * 8 } &Repr::Mtu(_) => field::MTU.end, &Repr::Unknown { length, .. } => field::DATA(length).end, @@ -550,15 +550,15 @@ impl<'a> Repr<'a> { opt.set_prefix(prefix); } Repr::RedirectedHeader(RedirectedHeader { header, data }) => { - let data_len = data.len() / 8; + // TODO(thvdveld): I think we need to check if the data we are sending is not + // exceeding the MTU. opt.clear_redirected_reserved(); opt.set_option_type(Type::RedirectedHeader); - opt.set_data_len((header.buffer_len() + 1 + data_len) as u8); - let mut ip_packet = - Ipv6Packet::new_unchecked(&mut opt.data_mut()[field::IP_DATA..]); + opt.set_data_len((((8 + header.buffer_len() + data.len()) + 7) / 8) as u8); + let mut packet = &mut opt.data_mut()[field::REDIRECTED_RESERVED.end - 2..]; + let mut ip_packet = Ipv6Packet::new_unchecked(&mut packet); header.emit(&mut ip_packet); - let payload = &mut ip_packet.into_inner()[header.buffer_len()..]; - payload.copy_from_slice(&data[..data_len]); + ip_packet.payload_mut().copy_from_slice(data); } Repr::Mtu(mtu) => { opt.set_option_type(Type::Mtu); From 9ddb63a6e532529f3f8bf35f2f6818a875bfa345 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Jul 2022 18:54:52 +0200 Subject: [PATCH 370/566] Fix `buffer_len` for MLD --- src/wire/mld.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wire/mld.rs b/src/wire/mld.rs index d163e722a..2224a4a5d 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -340,8 +340,8 @@ impl<'a> Repr<'a> { /// Return the length of a packet that will be emitted from this high-level representation. pub fn buffer_len(&self) -> usize { match self { - Repr::Query { .. } => field::QUERY_NUM_SRCS.end, - Repr::Report { .. } => field::NR_MCAST_RCRDS.end, + Repr::Query { data, .. } => field::QUERY_NUM_SRCS.end + data.len(), + Repr::Report { data, .. } => field::NR_MCAST_RCRDS.end + data.len(), } } From 8090469c2a1456b87a3e277e5b2f2e34e4f7555b Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 26 Jul 2022 00:18:15 +0200 Subject: [PATCH 371/566] fix return value of `nagle_enable` The old implementation returns the wrong value (ACK delay duration) and doesn't describe if the Nagle algorithm is enabled or not. --- src/socket/tcp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index fd498a14c..50a341d11 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -541,8 +541,8 @@ impl<'a> Socket<'a> { /// Return whether Nagle's Algorithm is enabled. /// /// See also the [set_nagle_enabled](#method.set_nagle_enabled) method. - pub fn nagle_enabled(&self) -> Option { - self.ack_delay + pub fn nagle_enabled(&self) -> bool { + self.nagle } /// Return the current window field value, including scaling according to RFC 1323. From c797194af29ddbae1d7d2cc649edb06f8e97ace8 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 2 Aug 2022 12:56:57 +0200 Subject: [PATCH 372/566] Fixes 6LoWPAN fragmentation The calculation for the fragment sizes was wrong. This commit seems to fix the calculation. This replaces #641. --- src/iface/interface.rs | 33 +++++++++++++++++++-------------- src/wire/sixlowpan.rs | 8 -------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 7419599dc..320dba29f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -65,6 +65,7 @@ pub(crate) struct SixlowpanOutPacket<'a> { datagram_size: u16, /// The datagram tag that is used for the fragmentation headers. datagram_tag: u16, + datagram_offset: usize, /// The size of the FRAG_N packets. fragn_size: usize, @@ -83,6 +84,7 @@ impl<'a> SixlowpanOutPacket<'a> { packet_len: 0, datagram_size: 0, datagram_tag: 0, + datagram_offset: 0, sent_bytes: 0, fragn_size: 0, ll_dst_addr: Ieee802154Address::Absent, @@ -1224,7 +1226,7 @@ impl<'a> Interface<'a> { return Ok(false); } - if *packet_len >= *sent_bytes { + if *packet_len > *sent_bytes { match device.transmit().ok_or(Error::Exhausted) { Ok(tx_token) => { if let Err(e) = self.inner.dispatch_ieee802154_out_packet( @@ -2979,6 +2981,7 @@ impl<'a> InterfaceInner<'a> { let mut total_size = 0; total_size += iphc_repr.buffer_len(); let mut _compressed_headers_len = iphc_repr.buffer_len(); + let mut _uncompressed_headers_len = ip_repr.buffer_len(); #[allow(unreachable_patterns)] match packet { @@ -2986,6 +2989,7 @@ impl<'a> InterfaceInner<'a> { IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); _compressed_headers_len += udp_repr.header_len(); + _uncompressed_headers_len += udpv6_repr.header_len(); total_size += udp_repr.header_len() + payload.len(); } #[cfg(feature = "socket-tcp")] @@ -3021,6 +3025,7 @@ impl<'a> InterfaceInner<'a> { fragn_size, ll_dst_addr, ll_src_addr, + datagram_offset, .. } = &mut _out_packet.unwrap().sixlowpan_out_packet; @@ -3051,8 +3056,7 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "socket-tcp")] IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = - TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); + let mut tcp_packet = TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); tcp_repr.emit( &mut tcp_packet, &iphc_repr.src_addr.into(), @@ -3102,20 +3106,22 @@ impl<'a> InterfaceInner<'a> { // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. // // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 - let frag1_size = ((125 - ieee_len - frag1.buffer_len() - _compressed_headers_len) - & 0xffff_fff8) - + _compressed_headers_len; - *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; + + let header_diff = _uncompressed_headers_len - _compressed_headers_len; + let frag1_size = + (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); + + *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; *sent_bytes = frag1_size; + *datagram_offset = frag1_size + header_diff; tx_token.consume( self.now, ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| { // Add the IEEE header. - let mut ieee_packet = - Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); ieee_repr.emit(&mut ieee_packet); tx_buf = &mut tx_buf[ieee_len..]; @@ -3206,6 +3212,7 @@ impl<'a> InterfaceInner<'a> { packet_len, datagram_size, datagram_tag, + datagram_offset, sent_bytes, fragn_size, ll_dst_addr, @@ -3229,10 +3236,10 @@ impl<'a> InterfaceInner<'a> { }; // Create the FRAG_N header. - let mut fragn = SixlowpanFragRepr::Fragment { + let fragn = SixlowpanFragRepr::Fragment { size: *datagram_size, tag: *datagram_tag, - offset: 0, + offset: (*datagram_offset / 8) as u8, }; let ieee_len = ieee_repr.buffer_len(); @@ -3246,9 +3253,6 @@ impl<'a> InterfaceInner<'a> { ieee_repr.emit(&mut ieee_packet); tx_buf = &mut tx_buf[ieee_len..]; - // Add the next fragment header - let datagram_offset = ((40 + *sent_bytes) / 8) as u8; - fragn.set_offset(datagram_offset); let mut frag_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); fragn.emit(&mut frag_packet); @@ -3258,6 +3262,7 @@ impl<'a> InterfaceInner<'a> { tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); *sent_bytes += frag_size; + *datagram_offset += frag_size; Ok(()) }, diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index e90ccfb78..170e2c612 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -365,14 +365,6 @@ pub mod frag { } impl Repr { - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub(crate) fn set_offset(&mut self, value: u8) { - match self { - Repr::FirstFragment { .. } => (), - Repr::Fragment { offset, .. } => *offset = value, - } - } - /// Parse a 6LoWPAN Fragment header. pub fn parse>(packet: &Packet) -> Result { let size = packet.datagram_size(); From d5b4cbfbeebee330023fb320eacafc9488aa81a3 Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Tue, 2 Aug 2022 14:30:17 +0200 Subject: [PATCH 373/566] AssemblerState now derives defmt::Format, allowing `net_debug` to print it --- src/iface/fragmentation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 228d7e528..a17591576 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -17,6 +17,7 @@ pub struct PacketAssembler<'a> { } /// Holds the state of the assembling of one packet. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, PartialEq)] enum AssemblerState { NotInit, From 4b76a38157ef74acc2890b6485d4ff7ff36db464 Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Tue, 2 Aug 2022 16:45:27 +0200 Subject: [PATCH 374/566] Bump Minimum Supported Rust Version from 1.56 to 1.60 --- .github/workflows/clippy.yml | 2 +- .github/workflows/test.yml | 4 ++-- CHANGELOG.md | 2 ++ README.md | 2 +- src/lib.rs | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 7aa762352..b23f9e379 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -17,7 +17,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.53.0 + toolchain: 1.60.0 override: true components: clippy - uses: actions-rs/clippy-check@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6440a13f..f467d89c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.56.0 + - 1.60.0 - nightly features: @@ -68,7 +68,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.56.0 + - 1.60.0 - nightly features: diff --git a/CHANGELOG.md b/CHANGELOG.md index c16795be7..6e7cbbf7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove IpVersion::Unspecified - Remove IpAddress::Unspecified - When sending packets with a raw socket, the source IP address is sent unmodified (it was previously replaced with the interface's address if it was unspecified). +- Fix enable `defmt/alloc` if `alloc` or `std` is enabled. +- Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.60 ## [0.8.1] - 2022-05-12 diff --git a/README.md b/README.md index fc920b074..9c07fb291 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.56 and later. +and compiles on stable Rust 1.60 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. diff --git a/src/lib.rs b/src/lib.rs index f1a3653e1..c70efdbad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,7 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.56 and up with any valid set of features. +//! This crate is guaranteed to compile on stable Rust 1.60 and up with any valid set of features. //! It *might* compile on older versions but that may change in any new patch release. //! //! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which From 9fa70d2781db7fec5836b80ad9b748726ef270a7 Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Tue, 2 Aug 2022 16:16:20 +0200 Subject: [PATCH 375/566] Enable `defmt/alloc` when `alloc` or `std` is enabled to fix Format derives. --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e61b6d9d..62650571b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,8 +32,8 @@ rand = "0.8" url = "2.0" [features] -std = ["managed/std"] -alloc = ["managed/alloc"] +std = ["managed/std", "defmt?/alloc"] +alloc = ["managed/alloc", "defmt?/alloc"] verbose = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] From 1ec362b934a3e2c14505dde78e4b583e85d9cfe2 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Thu, 4 Aug 2022 10:05:30 -0400 Subject: [PATCH 376/566] Clarify how to back a PacketAssembler --- src/iface/fragmentation.rs | 4 ++++ src/iface/interface.rs | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 228d7e528..3feb512d9 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -10,6 +10,10 @@ use crate::Error; use crate::Result; /// Holds different fragments of one packet, used for assembling fragmented packets. +/// +/// The buffer used for the `PacketAssembler` should either be dynamically sized (ex: Vec) +/// or should be statically allocated based upon the MTU of the type of packet being +/// assembled (ex: 1280 for a IPv6 frame). #[derive(Debug)] pub struct PacketAssembler<'a> { buffer: ManagedSlice<'a, u8>, diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 320dba29f..29b397cce 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1660,7 +1660,16 @@ impl<'a> InterfaceInner<'a> { // This information is the total size of the packet when it is fully assmbled. // We also pass the header size, since this is needed when other fragments // (other than the first one) are added. - check!(check!(fragments.reserve_with_key(&key)).start( + let frag_slot = match fragments.reserve_with_key(&key) { + Ok(frag) => frag, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(frag_slot.start( Some( frag.datagram_size() as usize - uncompressed_header_size + compressed_header_size From 9995ce8070502cfd56cf74bab05421b029f0038a Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Thu, 4 Aug 2022 10:10:42 -0400 Subject: [PATCH 377/566] Decreases how long the packet fragmentation cache holds a packet from 60s -> 5s --- src/iface/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 320dba29f..955a482dd 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1665,7 +1665,7 @@ impl<'a> InterfaceInner<'a> { frag.datagram_size() as usize - uncompressed_header_size + compressed_header_size ), - self.now + Duration::from_secs(60), + self.now + Duration::from_secs(5), -((uncompressed_header_size - compressed_header_size) as isize), )); } From f6f334cdc8b4c220b648293005acbd6d0dddb022 Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Tue, 2 Aug 2022 16:54:44 +0200 Subject: [PATCH 378/566] Set `DEFMT_LOG` in `check` workflow to ensure that all print statements are "actually" compiled --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f467d89c5..381faa0c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,6 +62,10 @@ jobs: check: runs-on: ubuntu-20.04 continue-on-error: ${{ matrix.rust == 'nightly' }} + env: + # Set DEFMT_LOG to trace so that all net_{error, .., trace} messages + # are actually compiled and verified + DEFMT_LOG: "trace" strategy: matrix: # Test on stable, MSRV, and nightly. @@ -75,6 +79,7 @@ jobs: # These feature sets cannot run tests, so we only check they build. - medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async + - defmt alloc medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async steps: - uses: actions/checkout@v2 From 68ebd6eb8cd2223a00909d6cc9e51270e754ede4 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Fri, 5 Aug 2022 08:15:39 -0400 Subject: [PATCH 379/566] Make the packet fragmentation cache timeout user configurable --- src/iface/interface.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 955a482dd..944f13b35 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -27,7 +27,8 @@ pub(crate) struct FragmentsBuffer<'a> { ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, - + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: Duration, #[cfg(not(any( feature = "proto-ipv4-fragmentation", feature = "proto-sixlowpan-fragmentation" @@ -199,6 +200,8 @@ pub struct InterfaceBuilder<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: Option>, #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: Duration, + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: Option>, } @@ -266,6 +269,8 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: None, #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: Duration::from_secs(60), + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: None, } } @@ -393,6 +398,12 @@ let iface = builder.finalize(&mut device); self } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn sixlowpan_fragments_cache_timeout(mut self, sec: u64) -> Self { + self.sixlowpan_fragments_cache_timeout = Duration::from_secs(sec); + self + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_out_packet_cache(mut self, storage: T) -> Self where @@ -493,6 +504,8 @@ let iface = builder.finalize(&mut device); sixlowpan_fragments: self .sixlowpan_fragments .expect("Cache for incoming 6LoWPAN fragments is required"), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: self.sixlowpan_fragments_cache_timeout, #[cfg(not(any( feature = "proto-ipv4-fragmentation", @@ -1574,7 +1587,7 @@ impl<'a> InterfaceInner<'a> { Some(payload) => { cfg_if::cfg_if! { if #[cfg(feature = "proto-sixlowpan-fragmentation")] { - self.process_sixlowpan(sockets, &ieee802154_repr, payload, Some(&mut _fragments.sixlowpan_fragments)) + self.process_sixlowpan(sockets, &ieee802154_repr, payload, Some((&mut _fragments.sixlowpan_fragments, _fragments.sixlowpan_fragments_cache_timeout))) } else { self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) } @@ -1590,7 +1603,10 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - _fragments: Option<&'output mut PacketAssemblerSet<'a, SixlowpanFragKey>>, + _fragments: Option<( + &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + Duration, + )>, ) -> Option> { let payload = match check!(SixlowpanPacket::dispatch(payload)) { #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] @@ -1600,7 +1616,7 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-sixlowpan-fragmentation")] SixlowpanPacket::FragmentHeader => { - let fragments = _fragments.unwrap(); + let (fragments, timeout) = _fragments.unwrap(); // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. @@ -1665,7 +1681,7 @@ impl<'a> InterfaceInner<'a> { frag.datagram_size() as usize - uncompressed_header_size + compressed_header_size ), - self.now + Duration::from_secs(5), + self.now + timeout, -((uncompressed_header_size - compressed_header_size) as isize), )); } From 6e2d075149f444fe66adb4a2e18c89371696b785 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Fri, 5 Aug 2022 08:30:24 -0400 Subject: [PATCH 380/566] convert u64 -> Duration --- src/iface/interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 944f13b35..62f369018 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -399,8 +399,8 @@ let iface = builder.finalize(&mut device); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_fragments_cache_timeout(mut self, sec: u64) -> Self { - self.sixlowpan_fragments_cache_timeout = Duration::from_secs(sec); + pub fn sixlowpan_fragments_cache_timeout(mut self, timeout: Duration) -> Self { + self.sixlowpan_fragments_cache_timeout = timeout; self } From af39d86faa96c282c842868747d88232f78f25a2 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Fri, 5 Aug 2022 09:47:37 -0400 Subject: [PATCH 381/566] add warning about greater than 60s RFC violation --- src/iface/interface.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 62f369018..580523499 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -400,6 +400,9 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_fragments_cache_timeout(mut self, timeout: Duration) -> Self { + if timeout > Duration::from_secs(60) { + net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); + } self.sixlowpan_fragments_cache_timeout = timeout; self } From ecb4cbc54508bb496f934d5a3b0df9e063749952 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Aug 2022 23:54:28 +0200 Subject: [PATCH 382/566] Clippy fixes. --- examples/sixlowpan_benchmark.rs | 2 +- examples/utils.rs | 16 +++++++--------- src/iface/interface.rs | 4 ++-- src/socket/icmp.rs | 24 ++++++++++++------------ src/wire/arp.rs | 6 +++--- src/wire/dhcpv4.rs | 6 +++--- src/wire/dns.rs | 14 +++++++------- src/wire/icmpv4.rs | 6 +++--- src/wire/icmpv6.rs | 10 +++++----- src/wire/igmp.rs | 6 +++--- src/wire/ipv4.rs | 6 +++--- src/wire/ipv6.rs | 6 +++--- src/wire/ipv6fragment.rs | 4 ++-- src/wire/ipv6hopbyhop.rs | 2 +- src/wire/ipv6option.rs | 2 +- src/wire/ipv6routing.rs | 2 +- src/wire/mld.rs | 10 +++++----- src/wire/ndisc.rs | 4 ++-- src/wire/ndiscoption.rs | 6 +++--- src/wire/tcp.rs | 8 ++++---- src/wire/udp.rs | 6 +++--- 21 files changed, 74 insertions(+), 76 deletions(-) diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 9eae6cc84..7d4b22fcc 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -67,7 +67,7 @@ use std::fs; fn if_nametoindex(ifname: &str) -> u32 { let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{}/ifindex", ifname)) .expect("couldn't read interface from \"/sys/devices/virtual/net\"") - .replace("\n", ""); + .replace('\n', ""); contents.parse::().unwrap() } diff --git a/examples/utils.rs b/examples/utils.rs index dcee7b4a6..8473c7303 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -41,7 +41,7 @@ where buf, "\x1b[37m{} {}\x1b[0m", timestamp, - message.replace("\n", "\n ") + message.replace('\n', "\n ") ) } else { writeln!( @@ -95,7 +95,7 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { } } -pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) { +pub fn add_tuntap_options(opts: &mut Options, _free: &mut [&str]) { opts.optopt("", "tun", "TUN interface to use", "tun0"); opts.optopt("", "tap", "TAP interface to use", "tap0"); } @@ -111,7 +111,7 @@ pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { } } -pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { +pub fn add_middleware_options(opts: &mut Options, _free: &mut [&str]) { opts.optopt("", "pcap", "Write a packet capture file", "FILE"); opts.optopt( "", @@ -186,12 +186,10 @@ where .map(|s| u64::from_str(&s).unwrap()) .unwrap_or(0); - let pcap_writer: Box; - if let Some(pcap_filename) = matches.opt_str("pcap") { - pcap_writer = Box::new(File::create(pcap_filename).expect("cannot open file")) - } else { - pcap_writer = Box::new(io::sink()) - } + let pcap_writer: Box = match matches.opt_str("pcap") { + Some(pcap_filename) => Box::new(File::create(pcap_filename).expect("cannot open file")), + None => Box::new(io::sink()), + }; let seed = SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 320dba29f..257598da8 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -4254,14 +4254,14 @@ mod test { // Ensure the ident we bound to and the ident of the packet are the same. let mut bytes = [0xff; 24]; - let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); + let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); let echo_repr = Icmpv4Repr::EchoRequest { ident, seq_no, data: echo_data, }; echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); - let icmp_data = &packet.into_inner()[..]; + let icmp_data = &*packet.into_inner(); let ipv4_repr = Ipv4Repr { src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 9897af951..060902ba3 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -683,7 +683,7 @@ mod test_ipv4 { ECHOV4_REPR.emit(&mut packet, &checksum); assert_eq!( - socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), + socket.send_slice(&*packet.into_inner(), REMOTE_IPV4.into()), Ok(()) ); assert_eq!( @@ -728,7 +728,7 @@ mod test_ipv4 { s.set_hop_limit(Some(0x2a)); assert_eq!( - s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), + s.send_slice(&*packet.into_inner(), REMOTE_IPV4.into()), Ok(()) ); assert_eq!( @@ -761,9 +761,9 @@ mod test_ipv4 { let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; - let mut packet = Icmpv4Packet::new_unchecked(&mut bytes); + let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); ECHOV4_REPR.emit(&mut packet, &checksum); - let data = &packet.into_inner()[..]; + let data = &*packet.into_inner(); assert!(socket.accepts(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into())); socket.process(&mut cx, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()); @@ -816,7 +816,7 @@ mod test_ipv4 { &checksum, ); - let data = &packet.into_inner()[..]; + let data = &*packet.into_inner(); let icmp_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::PortUnreachable, @@ -850,7 +850,7 @@ mod test_ipv4 { icmp_repr.emit(&mut packet, &checksum); assert_eq!( socket.recv(), - Ok((&packet.into_inner()[..], REMOTE_IPV4.into())) + Ok((&*packet.into_inner(), REMOTE_IPV4.into())) ); assert!(!socket.can_recv()); } @@ -931,7 +931,7 @@ mod test_ipv6 { ); assert_eq!( - socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), + socket.send_slice(&*packet.into_inner(), REMOTE_IPV6.into()), Ok(()) ); assert_eq!( @@ -981,7 +981,7 @@ mod test_ipv6 { s.set_hop_limit(Some(0x2a)); assert_eq!( - s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), + s.send_slice(&*packet.into_inner(), REMOTE_IPV6.into()), Ok(()) ); assert_eq!( @@ -1014,14 +1014,14 @@ mod test_ipv6 { let checksum = ChecksumCapabilities::default(); let mut bytes = [0xff; 24]; - let mut packet = Icmpv6Packet::new_unchecked(&mut bytes); + let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]); ECHOV6_REPR.emit( &LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum, ); - let data = &packet.into_inner()[..]; + let data = &*packet.into_inner(); assert!(socket.accepts(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into())); socket.process(&mut cx, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()); @@ -1079,7 +1079,7 @@ mod test_ipv6 { &checksum, ); - let data = &packet.into_inner()[..]; + let data = &*packet.into_inner(); let icmp_repr = Icmpv6Repr::DstUnreachable { reason: Icmpv6DstUnreachable::PortUnreachable, @@ -1118,7 +1118,7 @@ mod test_ipv6 { ); assert_eq!( socket.recv(), - Ok((&packet.into_inner()[..], REMOTE_IPV6.into())) + Ok((&*packet.into_inner(), REMOTE_IPV6.into())) ); assert!(!socket.can_recv()); } diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 05e059dcf..773c3a9d3 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -21,7 +21,7 @@ enum_with_unknown! { } /// A read/write wrapper around an Address Resolution Protocol packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -429,7 +429,7 @@ mod test { packet.set_source_protocol_addr(&[0x21, 0x22, 0x23, 0x24]); packet.set_target_hardware_addr(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]); packet.set_target_protocol_addr(&[0x41, 0x42, 0x43, 0x44]); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } fn packet_repr() -> Repr { @@ -458,6 +458,6 @@ mod test { let mut bytes = vec![0xa5; 28]; let mut packet = Packet::new_unchecked(&mut bytes); packet_repr().emit(&mut packet); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 8df841dc3..05760521a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -77,7 +77,7 @@ impl<'a> DhcpOption<'a> { // See https://tools.ietf.org/html/rfc2132 for all possible DHCP options. let (skip_len, option); - match *buffer.get(0).ok_or(Error)? { + match *buffer.first().ok_or(Error)? { field::OPT_END => { skip_len = 1; option = DhcpOption::EndOfList; @@ -217,7 +217,7 @@ impl<'a> DhcpOption<'a> { } /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -1173,7 +1173,7 @@ mod test { let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet).unwrap(); - let packet = &packet.into_inner()[..]; + let packet = &*packet.into_inner(); let packet_len = packet.len(); assert_eq!(packet, &DISCOVER_BYTES[..packet_len]); for byte in &DISCOVER_BYTES[packet_len..] { diff --git a/src/wire/dns.rs b/src/wire/dns.rs index 048ef40f4..d29e1d87e 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -76,7 +76,7 @@ mod field { const CLASS_IN: u16 = 1; /// A read/write wrapper around a DNS packet buffer. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct Packet> { buffer: T, } @@ -262,7 +262,7 @@ fn parse_name_part<'a>( mut f: impl FnMut(&'a [u8]), ) -> Result<(&'a [u8], Option)> { loop { - let x = *bytes.get(0).ok_or(Error)?; + let x = *bytes.first().ok_or(Error)?; bytes = &bytes[1..]; match x { 0x00 => return Ok((bytes, None)), @@ -273,7 +273,7 @@ fn parse_name_part<'a>( f(label); } x if x & 0xC0 == 0xC0 => { - let y = *bytes.get(0).ok_or(Error)?; + let y = *bytes.first().ok_or(Error)?; bytes = &bytes[1..]; let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); @@ -284,7 +284,7 @@ fn parse_name_part<'a>( } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Question<'a> { pub name: &'a [u8], @@ -324,7 +324,7 @@ impl<'a> Question<'a> { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Record<'a> { pub name: &'a [u8], @@ -355,7 +355,7 @@ impl<'a> RecordData<'a> { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RecordData<'a> { #[cfg(feature = "proto-ipv4")] @@ -401,7 +401,7 @@ impl<'a> Record<'a> { /// High-level DNS packet representation. /// /// Currently only supports query packets. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub transaction_id: u16, diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 3814bec63..f1546294b 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -161,7 +161,7 @@ enum_with_unknown! { } /// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -665,7 +665,7 @@ mod test { packet.set_echo_seq_no(0xabcd); packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]); packet.fill_checksum(); - assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); } fn echo_packet_repr() -> Repr<'static> { @@ -689,7 +689,7 @@ mod test { let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &ChecksumCapabilities::default()); - assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 1df3bdb7e..cfce7656f 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -183,7 +183,7 @@ impl fmt::Display for TimeExceeded { } /// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { pub(super) buffer: T, @@ -828,7 +828,7 @@ mod test { .payload_mut() .copy_from_slice(&ECHO_PACKET_PAYLOAD[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); - assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); } #[test] @@ -855,7 +855,7 @@ mod test { &mut packet, &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); } #[test] @@ -881,7 +881,7 @@ mod test { .payload_mut() .copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); - assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]); } #[test] @@ -908,6 +908,6 @@ mod test { &mut packet, &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]); } } diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 3bdd65526..56f362d21 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -330,7 +330,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { } } -impl<'a> fmt::Display for Repr { +impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Repr::MembershipQuery { @@ -413,7 +413,7 @@ mod test { packet.set_max_resp_code(0); packet.set_group_address(Ipv4Address::from_bytes(&[224, 0, 6, 150])); packet.fill_checksum(); - assert_eq!(&packet.into_inner()[..], &LEAVE_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &LEAVE_PACKET_BYTES[..]); } #[test] @@ -424,7 +424,7 @@ mod test { packet.set_max_resp_code(0); packet.set_group_address(Ipv4Address::from_bytes(&[225, 0, 0, 37])); packet.fill_checksum(); - assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 672d0cec3..0b0d0e22a 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -272,7 +272,7 @@ impl defmt::Format for Cidr { } /// A read/write wrapper around an Internet Protocol version 4 packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -838,7 +838,7 @@ mod test { packet.set_dst_addr(Address([0x21, 0x22, 0x23, 0x24])); packet.fill_checksum(); packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } #[test] @@ -918,7 +918,7 @@ mod test { let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &ChecksumCapabilities::default()); packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES); - assert_eq!(&packet.into_inner()[..], &REPR_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &REPR_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 563e0be42..037977290 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -349,7 +349,7 @@ impl fmt::Display for Cidr { } /// A read/write wrapper around an Internet Protocol version 6 packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -1103,7 +1103,7 @@ mod test { let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len(); expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]); assert_eq!(packet.check_len(), Ok(())); - assert_eq!(&packet.into_inner()[..], &expected_bytes[..]); + assert_eq!(&*packet.into_inner(), &expected_bytes[..]); } #[test] @@ -1175,7 +1175,7 @@ mod test { let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet); packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES); - assert_eq!(&packet.into_inner()[..], &REPR_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &REPR_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index e19e6cc26..a36a3635b 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -6,7 +6,7 @@ use byteorder::{ByteOrder, NetworkEndian}; pub use super::IpProtocol as Protocol; /// A read/write wrapper around an IPv6 Fragment Header. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T, @@ -202,7 +202,7 @@ impl Repr { } } -impl<'a> fmt::Display for Repr { +impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 0fd9e7a76..9bf98ff45 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -5,7 +5,7 @@ pub use super::IpProtocol as Protocol; use crate::wire::ipv6option::Ipv6OptionsIterator; /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T, diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 25bbc7dd5..fdeb8a1b2 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -57,7 +57,7 @@ impl From for FailureType { } /// A read/write wrapper around an IPv6 Extension Header Option. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Ipv6Option> { buffer: T, diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index d4ca2b968..9decf997b 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -50,7 +50,7 @@ impl fmt::Display for Type { } /// A read/write wrapper around an IPv6 Routing Header buffer. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Header> { buffer: T, diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 2224a4a5d..16326f1e1 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -165,7 +165,7 @@ impl + AsMut<[u8]>> Packet { } /// A read/write wrapper around an MLDv2 Listener Report Message Address Record. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AddressRecord> { buffer: T, @@ -478,7 +478,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), ); - assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]); } #[test] @@ -521,7 +521,7 @@ mod test { &Ipv6Address::LINK_LOCAL_ALL_NODES.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), ); - assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]); } #[test] @@ -559,7 +559,7 @@ mod test { &mut packet, &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]); } #[test] @@ -573,6 +573,6 @@ mod test { &mut packet, &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]); } } diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 843845f8c..5c7cf4d29 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -569,7 +569,7 @@ mod test { .payload_mut() .copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2); - assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]); } #[test] @@ -597,6 +597,6 @@ mod test { &mut packet, &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); + assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]); } } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 63f9a2e15..6ced627ea 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -48,7 +48,7 @@ bitflags! { /// A read/write wrapper around an [NDISC Option]. /// /// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6 -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct NdiscOption> { buffer: T, @@ -617,7 +617,7 @@ impl> PrettyPrint for NdiscOption { indent: &mut PrettyIndent, ) -> fmt::Result { match NdiscOption::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), + Err(err) => write!(f, "{}({})", indent, err), Ok(ndisc) => match Repr::parse(&ndisc) { Err(_) => Ok(()), Ok(repr) => { @@ -667,7 +667,7 @@ mod test { opt.set_valid_lifetime(Duration::from_secs(900)); opt.set_preferred_lifetime(Duration::from_secs(1000)); opt.set_prefix(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)); - assert_eq!(&PREFIX_OPT_BYTES[..], &opt.into_inner()[..]); + assert_eq!(&PREFIX_OPT_BYTES[..], &*opt.into_inner()); } #[test] diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index 401dcc50d..b814abc70 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -67,7 +67,7 @@ impl cmp::PartialOrd for SeqNumber { } /// A read/write wrapper around a Transmission Control Protocol packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -606,7 +606,7 @@ pub enum TcpOption<'a> { impl<'a> TcpOption<'a> { pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], TcpOption<'a>)> { let (length, option); - match *buffer.get(0).ok_or(Error)? { + match *buffer.first().ok_or(Error)? { field::OPT_END => { length = 1; option = TcpOption::EndOfList; @@ -1148,7 +1148,7 @@ mod test { packet.options_mut().copy_from_slice(&OPTION_BYTES[..]); packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]); packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into()); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } #[test] @@ -1215,7 +1215,7 @@ mod test { &DST_ADDR.into(), &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &SYN_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 2f759f0d9..3505d84a0 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -7,7 +7,7 @@ use crate::wire::ip::checksum; use crate::wire::{IpAddress, IpProtocol}; /// A read/write wrapper around an User Datagram Protocol packet buffer. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -358,7 +358,7 @@ mod test { packet.set_checksum(0xffff); packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]); packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into()); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } #[test] @@ -429,7 +429,7 @@ mod test { |payload| payload.copy_from_slice(&PAYLOAD_BYTES), &ChecksumCapabilities::default(), ); - assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]); + assert_eq!(&*packet.into_inner(), &PACKET_BYTES[..]); } #[test] From f3e2a8adffbc5656832206b34bf9155296bb1882 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 6 Aug 2022 00:10:34 +0200 Subject: [PATCH 383/566] Cleanup CI. The GHA ubuntu images already have rustup, so actions-rs/toolchain is not required anymore. --- .github/workflows/clippy.yml | 8 ++------ .github/workflows/fuzz.yml | 14 ++++---------- .github/workflows/rustfmt.yaml | 5 ----- .github/workflows/test.yml | 27 ++++++++++----------------- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index b23f9e379..ba4c6f5e3 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -7,6 +7,8 @@ name: Clippy check jobs: clippy: runs-on: ubuntu-latest + env: + RUSTUP_TOOLCHAIN: stable steps: - uses: actions/checkout@v2 if: github.event_name == 'pull_request_target' @@ -14,12 +16,6 @@ jobs: ref: refs/pull/${{ github.event.number }}/head - uses: actions/checkout@v2 if: github.event_name != 'pull_request_target' - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: 1.60.0 - override: true - components: clippy - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 281c6df3c..60113b48a 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -7,18 +7,12 @@ name: Fuzz jobs: fuzz: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 + env: + RUSTUP_TOOLCHAIN: nightly steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - name: Install cargo-fuzz - # Fix for cargo-fuzz on latest nightly: https://github.com/rust-fuzz/cargo-fuzz/issues/276 - # Switch back to installing from crates.io when it's released. - #run: cargo install cargo-fuzz - run: cargo install --git https://github.com/rust-fuzz/cargo-fuzz --rev b4df3e58f767b5cad8d1aa6753961003f56f3609 + run: cargo install cargo-fuzz - name: Fuzz run: cargo fuzz run packet_parser -- -max_len=1536 -max_total_time=30 diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml index 3d065a20e..edf3ae96c 100644 --- a/.github/workflows/rustfmt.yaml +++ b/.github/workflows/rustfmt.yaml @@ -9,10 +9,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - components: rustfmt - name: Check fmt run: cargo fmt -- --check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 381faa0c6..72b3fdbab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,13 +7,13 @@ name: Test jobs: tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [test, check] steps: - name: Done run: exit 0 test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 continue-on-error: ${{ matrix.rust == 'nightly' }} strategy: matrix: @@ -49,23 +49,16 @@ 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 + env: + RUSTUP_TOOLCHAIN: "${{ matrix.rust }}" steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true - name: Run Tests run: cargo test --no-default-features --features "${{ matrix.features }}" check: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 continue-on-error: ${{ matrix.rust == 'nightly' }} - env: - # Set DEFMT_LOG to trace so that all net_{error, .., trace} messages - # are actually compiled and verified - DEFMT_LOG: "trace" strategy: matrix: # Test on stable, MSRV, and nightly. @@ -81,12 +74,12 @@ jobs: - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async - defmt alloc medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async + env: + # Set DEFMT_LOG to trace so that all net_{error, .., trace} messages + # are actually compiled and verified + DEFMT_LOG: "trace" + RUSTUP_TOOLCHAIN: "${{ matrix.rust }}" steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - override: true - name: Check run: cargo check --no-default-features --features "${{ matrix.features }}" From da59503d7f0d268e439603fc334ea1716c69419a Mon Sep 17 00:00:00 2001 From: lachlansneff-parallel <105677805+lachlansneff-parallel@users.noreply.github.com> Date: Sat, 6 Aug 2022 04:27:05 -0700 Subject: [PATCH 384/566] Add additional DHCP configurability (try two) (#650) * Add support for sending and receiving DHCP options This adds the ability to set outgoing DHCP options with an optional buffer, as well as the option to buffer all incoming DHCP packets and expose them to the user. Users can then read the relevant information out of the packet without requiring that support for individual options and fields be compiled into every usage of smoltcp. Co-authored-by: Luis-Hebendanz * Run rustfmt on changes Co-authored-by: Luis-Hebendanz --- src/socket/dhcpv4.rs | 180 +++++++++++++++++++++------- src/socket/mod.rs | 4 +- src/wire/dhcpv4.rs | 273 +++++++++++++++++++++++++++++++------------ src/wire/mod.rs | 6 +- 4 files changed, 337 insertions(+), 126 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 694064e34..5717f676f 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -3,7 +3,7 @@ use core::task::Waker; use crate::iface::Context; use crate::time::{Duration, Instant}; -use crate::wire::dhcpv4::field as dhcpv4_field; +use crate::wire::dhcpv4::{field as dhcpv4_field, DhcpOptionsBuffer}; use crate::wire::HardwareAddress; use crate::wire::{ DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr, @@ -15,18 +15,9 @@ use super::WakerRegistration; use super::PollAt; -const DISCOVER_TIMEOUT: Duration = Duration::from_secs(10); - -// timeout doubles every 2 tries. -// total time 5 + 5 + 10 + 10 + 20 = 50s -const REQUEST_TIMEOUT: Duration = Duration::from_secs(5); -const REQUEST_RETRIES: u16 = 5; - -const MIN_RENEW_TIMEOUT: Duration = Duration::from_secs(60); - const DEFAULT_LEASE_DURATION: Duration = Duration::from_secs(120); -const PARAMETER_REQUEST_LIST: &[u8] = &[ +const DEFAULT_PARAMETER_REQUEST_LIST: &[u8] = &[ dhcpv4_field::OPT_SUBNET_MASK, dhcpv4_field::OPT_ROUTER, dhcpv4_field::OPT_DOMAIN_NAME_SERVER, @@ -35,7 +26,10 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[ /// IPv4 configuration data provided by the DHCP server. #[derive(Debug, Eq, PartialEq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Config { +pub struct Config<'a> { + /// Information on how to reach the DHCP server that responded with DHCP + /// configuration. + pub server: ServerInfo, /// IP address pub address: Ipv4Cidr, /// Router address, also known as default gateway. Does not necessarily @@ -43,17 +37,19 @@ pub struct Config { pub router: Option, /// DNS servers pub dns_servers: [Option; DHCP_MAX_DNS_SERVER_COUNT], + /// Received DHCP packet + pub packet: Option>, } /// Information on how to reach a DHCP server. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct ServerInfo { +pub struct ServerInfo { /// IP address to use as destination in outgoing packets - address: Ipv4Address, + pub address: Ipv4Address, /// Server identifier to use in outgoing packets. Usually equal to server_address, /// but may differ in some situations (eg DHCP relays) - identifier: Ipv4Address, + pub identifier: Ipv4Address, } #[derive(Debug)] @@ -79,10 +75,8 @@ struct RequestState { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct RenewState { - /// Server that gave us the lease - server: ServerInfo, /// Active network config - config: Config, + config: Config<'static>, /// Renew timer. When reached, we will start attempting /// to renew this lease with the DHCP server. @@ -104,18 +98,40 @@ enum ClientState { Renewing(RenewState), } +/// Timeout and retry configuration. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct RetryConfig { + pub discover_timeout: Duration, + /// The REQUEST timeout doubles every 2 tries. + pub initial_request_timeout: Duration, + pub request_retries: u16, + pub min_renew_timeout: Duration, +} + +impl Default for RetryConfig { + fn default() -> Self { + Self { + discover_timeout: Duration::from_secs(10), + initial_request_timeout: Duration::from_secs(5), + request_retries: 5, + min_renew_timeout: Duration::from_secs(60), + } + } +} + /// Return value for the `Dhcpv4Socket::poll` function #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Event { +pub enum Event<'a> { /// Configuration has been lost (for example, the lease has expired) Deconfigured, /// Configuration has been newly acquired, or modified. - Configured(Config), + Configured(Config<'a>), } #[derive(Debug)] -pub struct Socket { +pub struct Socket<'a> { /// State of the DHCP client. state: ClientState, /// Set to true on config/state change, cleared back to false by the `config` function. @@ -127,9 +143,20 @@ pub struct Socket { /// Useful to react faster to IP configuration changes and to test whether renews work correctly. max_lease_duration: Option, + retry_config: RetryConfig, + /// Ignore NAKs. ignore_naks: bool, + /// A buffer contains options additional to be added to outgoing DHCP + /// packets. + outgoing_options: Option>, + /// A buffer containing all requested parameters. + parameter_request_list: Option<&'a [u8]>, + + /// Incoming DHCP packets are copied into this buffer, overwriting the previous. + receive_packet_buffer: Option<&'a mut [u8]>, + /// Waker registration #[cfg(feature = "async")] waker: WakerRegistration, @@ -140,7 +167,7 @@ pub struct Socket { /// The socket acquires an IP address configuration through DHCP autonomously. /// You must query the configuration with `.poll()` after every call to `Interface::poll()`, /// and apply the configuration to the `Interface`. -impl Socket { +impl<'a> Socket<'a> { /// Create a DHCPv4 socket #[allow(clippy::new_without_default)] pub fn new() -> Self { @@ -151,12 +178,39 @@ impl Socket { config_changed: true, transaction_id: 1, max_lease_duration: None, + retry_config: RetryConfig::default(), ignore_naks: false, + outgoing_options: None, + parameter_request_list: None, + receive_packet_buffer: None, #[cfg(feature = "async")] waker: WakerRegistration::new(), } } + /// Set the retry/timeouts configuration. + pub fn set_retry_config(&mut self, config: RetryConfig) { + self.retry_config = config; + } + + /// Set the outgoing options buffer. + pub fn set_outgoing_options_buffer(&mut self, options_buffer: DhcpOptionsBuffer<&'a [u8]>) { + self.outgoing_options = Some(options_buffer); + } + + /// Set the buffer into which incoming DHCP packets are copied into. + pub fn set_receive_packet_buffer(&mut self, buffer: &'a mut [u8]) { + self.receive_packet_buffer = Some(buffer); + } + + /// Set the parameter request list. + /// + /// This should contain at least `OPT_SUBNET_MASK` (`1`), `OPT_ROUTER` + /// (`3`), and `OPT_DOMAIN_NAME_SERVER` (`6`). + pub fn set_parameter_request_list(&mut self, parameter_request_list: &'a [u8]) { + self.parameter_request_list = Some(parameter_request_list); + } + /// Get the configured max lease duration. /// /// See also [`Self::set_max_lease_duration()`] @@ -256,6 +310,13 @@ impl Socket { dhcp_repr ); + // Copy over the payload into the receive packet buffer. + if let Some(buffer) = self.receive_packet_buffer.as_mut() { + if let Some(buffer) = buffer.get_mut(..payload.len()) { + buffer.copy_from_slice(payload); + } + } + match (&mut self.state, dhcp_repr.message_type) { (ClientState::Discovering(_state), DhcpMessageType::Offer) => { if !dhcp_repr.your_ip.is_unicast() { @@ -275,10 +336,9 @@ impl Socket { } (ClientState::Requesting(state), DhcpMessageType::Ack) => { if let Some((config, renew_at, expires_at)) = - Self::parse_ack(cx.now(), &dhcp_repr, self.max_lease_duration) + Self::parse_ack(cx.now(), &dhcp_repr, self.max_lease_duration, state.server) { self.state = ClientState::Renewing(RenewState { - server: state.server, config, renew_at, expires_at, @@ -292,9 +352,12 @@ impl Socket { } } (ClientState::Renewing(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = - Self::parse_ack(cx.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.config.server, + ) { state.renew_at = renew_at; state.expires_at = expires_at; if state.config != config { @@ -321,7 +384,8 @@ impl Socket { now: Instant, dhcp_repr: &DhcpRepr, max_lease_duration: Option, - ) -> Option<(Config, Instant, Instant)> { + server: ServerInfo, + ) -> Option<(Config<'static>, Instant, Instant)> { let subnet_mask = match dhcp_repr.subnet_mask { Some(subnet_mask) => subnet_mask, None => { @@ -365,9 +429,11 @@ impl Socket { } } let config = Config { + server, address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), router: dhcp_repr.router, - dns_servers: dns_servers, + dns_servers, + packet: None, }; // RFC 2131 indicates clients should renew a lease halfway through its expiration. @@ -410,6 +476,7 @@ impl Socket { let mut dhcp_repr = DhcpRepr { message_type: DhcpMessageType::Discover, transaction_id: next_transaction_id, + secs: 0, client_hardware_address: ethernet_addr, client_ip: Ipv4Address::UNSPECIFIED, your_ip: Ipv4Address::UNSPECIFIED, @@ -421,10 +488,14 @@ impl Socket { requested_ip: None, client_identifier: Some(ethernet_addr), server_identifier: None, - parameter_request_list: Some(PARAMETER_REQUEST_LIST), + parameter_request_list: Some( + self.parameter_request_list + .unwrap_or(DEFAULT_PARAMETER_REQUEST_LIST), + ), max_size: Some((cx.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16), lease_duration: None, dns_servers: None, + additional_options: self.outgoing_options, }; let udp_repr = UdpRepr { @@ -456,7 +527,7 @@ impl Socket { emit(cx, (ipv4_repr, udp_repr, dhcp_repr))?; // Update state AFTER the packet has been successfully sent. - state.retry_at = cx.now() + DISCOVER_TIMEOUT; + state.retry_at = cx.now() + self.retry_config.discover_timeout; self.transaction_id = next_transaction_id; Ok(()) } @@ -465,7 +536,7 @@ impl Socket { return Ok(()); } - if state.retry >= REQUEST_RETRIES { + if state.retry >= self.retry_config.request_retries { net_debug!("DHCP request retries exceeded, restarting discovery"); self.reset(); return Ok(()); @@ -484,7 +555,8 @@ impl Socket { emit(cx, (ipv4_repr, udp_repr, dhcp_repr))?; // Exponential backoff: Double every 2 retries. - state.retry_at = cx.now() + (REQUEST_TIMEOUT << (state.retry as u32 / 2)); + state.retry_at = cx.now() + + (self.retry_config.initial_request_timeout << (state.retry as u32 / 2)); state.retry += 1; self.transaction_id = next_transaction_id; @@ -503,7 +575,7 @@ impl Socket { } ipv4_repr.src_addr = state.config.address.address(); - ipv4_repr.dst_addr = state.server.address; + ipv4_repr.dst_addr = state.config.server.address; dhcp_repr.message_type = DhcpMessageType::Request; dhcp_repr.client_ip = state.config.address.address(); @@ -516,8 +588,11 @@ impl Socket { // 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 = - cx.now() + MIN_RENEW_TIMEOUT.max((state.expires_at - cx.now()) / 2); + state.renew_at = cx.now() + + self + .retry_config + .min_renew_timeout + .max((state.expires_at - cx.now()) / 2); self.transaction_id = next_transaction_id; Ok(()) @@ -548,7 +623,16 @@ impl Socket { None } else if let ClientState::Renewing(state) = &self.state { self.config_changed = false; - Some(Event::Configured(state.config)) + Some(Event::Configured(Config { + server: state.config.server, + address: state.config.address, + router: state.config.router, + dns_servers: state.config.dns_servers, + packet: self + .receive_packet_buffer + .as_deref() + .map(DhcpPacket::new_unchecked), + })) } else { self.config_changed = false; Some(Event::Deconfigured) @@ -594,12 +678,12 @@ mod test { // Helper functions struct TestSocket { - socket: Socket, + socket: Socket<'static>, cx: Context<'static>, } impl Deref for TestSocket { - type Target = Socket; + type Target = Socket<'static>; fn deref(&self) -> &Self::Target { &self.socket } @@ -741,6 +825,7 @@ mod test { const DHCP_DEFAULT: DhcpRepr = DhcpRepr { message_type: DhcpMessageType::Unknown(99), transaction_id: TXID, + secs: 0, client_hardware_address: MY_MAC, client_ip: Ipv4Address::UNSPECIFIED, your_ip: Ipv4Address::UNSPECIFIED, @@ -756,6 +841,7 @@ mod test { dns_servers: None, max_size: None, lease_duration: None, + additional_options: None, }; const DHCP_DISCOVER: DhcpRepr = DhcpRepr { @@ -840,13 +926,14 @@ mod test { let mut s = socket(); s.state = ClientState::Renewing(RenewState { config: Config { + server: ServerInfo { + address: SERVER_IP, + identifier: SERVER_IP, + }, address: Ipv4Cidr::new(MY_IP, 24), dns_servers: DNS_IPS, router: Some(SERVER_IP), - }, - server: ServerInfo { - address: SERVER_IP, - identifier: SERVER_IP, + packet: None, }, renew_at: Instant::from_secs(500), expires_at: Instant::from_secs(1000), @@ -870,9 +957,14 @@ mod test { assert_eq!( s.poll(), Some(Event::Configured(Config { + server: ServerInfo { + address: SERVER_IP, + identifier: SERVER_IP, + }, address: Ipv4Cidr::new(MY_IP, 24), dns_servers: DNS_IPS, router: Some(SERVER_IP), + packet: None, })) ); diff --git a/src/socket/mod.rs b/src/socket/mod.rs index ee24841c4..82de926a4 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -66,7 +66,7 @@ pub enum Socket<'a> { #[cfg(feature = "socket-tcp")] Tcp(tcp::Socket<'a>), #[cfg(feature = "socket-dhcpv4")] - Dhcpv4(dhcpv4::Socket), + Dhcpv4(dhcpv4::Socket<'a>), #[cfg(feature = "socket-dns")] Dns(dns::Socket<'a>), } @@ -132,6 +132,6 @@ from_socket!(udp::Socket<'a>, Udp); #[cfg(feature = "socket-tcp")] from_socket!(tcp::Socket<'a>, Tcp); #[cfg(feature = "socket-dhcpv4")] -from_socket!(dhcpv4::Socket, Dhcpv4); +from_socket!(dhcpv4::Socket<'a>, Dhcpv4); #[cfg(feature = "socket-dns")] from_socket!(dns::Socket<'a>, Dns); diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 05760521a..49404029d 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -2,6 +2,7 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; +use core::iter; use super::{Error, Result}; use crate::wire::arp::Hardware; @@ -55,6 +56,84 @@ impl MessageType { } } +/// A buffer for DHCP options. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DhcpOptionsBuffer { + /// The underlying buffer, directly from the DHCP packet representation. + buffer: T, + len: usize, +} + +impl> DhcpOptionsBuffer { + pub fn new(buffer: T) -> Self { + Self { buffer, len: 0 } + } + + pub fn buffer_len(&self) -> usize { + self.len + } + + pub fn map(self, f: F) -> DhcpOptionsBuffer + where + F: FnOnce(T) -> U, + { + DhcpOptionsBuffer { + buffer: f(self.buffer), + len: self.len, + } + } + + pub fn map_ref<'a, F, U>(&'a self, f: F) -> DhcpOptionsBuffer + where + F: FnOnce(&'a T) -> U, + { + DhcpOptionsBuffer { + buffer: f(&self.buffer), + len: self.len, + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> DhcpOptionsBuffer<&'a T> { + /// Parse a [`DhcpOptionsBuffer`] into an iterator of [`DhcpOption`]. + /// + /// This will stop when it reaches [`DhcpOption::EndOfList`]. + pub fn parse(&self) -> impl Iterator> { + let mut buf = &self.buffer.as_ref()[..self.len]; + iter::from_fn(move || match DhcpOption::parse(buf) { + Ok((_, DhcpOption::EndOfList)) | Err(_) => None, + Ok((new_buf, option)) => { + buf = new_buf; + Some(option) + } + }) + } +} + +impl + AsRef<[u8]>> DhcpOptionsBuffer { + /// Emit an iterator of [`DhcpOption`] into a [`DhcpOptionsBuffer`]. + pub fn emit<'a, I>(&mut self, options: I) -> Result<()> + where + I: IntoIterator>, + { + let mut buf = self.buffer.as_mut().get_mut(self.len..).unwrap_or(&mut []); + for option in options.into_iter() { + let option_size = option.buffer_len(); + let buf_len = buf.len(); + if option_size > buf_len { + return Err(Error); + } + buf = option.emit(buf); + if buf.len() != buf_len { + self.len += option_size; + } + } + + Ok(()) + } +} + /// A representation of a single DHCP option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -217,7 +296,7 @@ impl<'a> DhcpOption<'a> { } /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, @@ -468,9 +547,37 @@ impl> Packet { impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { /// Return a pointer to the options. #[inline] - pub fn options(&self) -> Result<&'a [u8]> { + pub fn options(&self) -> Result> { let data = self.buffer.as_ref(); - data.get(field::OPTIONS).ok_or(Error) + data.get(field::OPTIONS) + .ok_or(Error) + .map(|buffer| DhcpOptionsBuffer { + buffer, + len: buffer.len(), + }) + } + + pub fn get_sname(&self) -> Result<&'a str> { + let data = self.buffer.as_ref(); + let data = data.get(field::SNAME).ok_or(Error)?; + let len = data.iter().position(|&x| x == 0).ok_or(Error)?; + if len == 0 { + return Err(Error); + } + + let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?; + Ok(data) + } + + pub fn get_boot_file(&self) -> Result<&'a str> { + let data = self.buffer.as_ref(); + let data = data.get(field::FILE).ok_or(Error)?; + let len = data.iter().position(|&x| x == 0).ok_or(Error)?; + if len == 0 { + return Err(Error); + } + let data = core::str::from_utf8(&data[..len]).map_err(|_| Error)?; + Ok(data) } } @@ -591,9 +698,11 @@ impl + AsMut<[u8]>> Packet { impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// Return a pointer to the options. #[inline] - pub fn options_mut(&mut self) -> Result<&mut [u8]> { + pub fn options_mut(&mut self) -> Result> { let data = self.buffer.as_mut(); - data.get_mut(field::OPTIONS).ok_or(Error) + data.get_mut(field::OPTIONS) + .ok_or(Error) + .map(DhcpOptionsBuffer::new) } } @@ -651,6 +760,11 @@ pub struct Repr<'a> { /// used by the client and server to associate messages and responses between a client and a /// server. pub transaction_id: u32, + /// seconds elapsed since client began address acquisition or renewal + /// process the DHCPREQUEST message MUST use the same value in the DHCP + /// message header's 'secs' field and be sent to the same IP broadcast + /// address as the original DHCPDISCOVER message. + pub secs: u16, /// This field is also known as `chaddr` in the RFC and for networks where the access layer is /// ethernet, it is the client MAC address. pub client_hardware_address: EthernetAddress, @@ -704,6 +818,10 @@ pub struct Repr<'a> { pub max_size: Option, /// The DHCP IP lease duration, specified in seconds. pub lease_duration: Option, + /// When returned from [`Repr::parse`], this field will be `None`. + /// However, when calling [`Repr::emit`], this field should contain only + /// additional DHCP options not known to smoltcp. + pub additional_options: Option>, } impl<'a> Repr<'a> { @@ -740,6 +858,9 @@ impl<'a> Repr<'a> { if let Some(list) = self.parameter_request_list { len += list.len() + 2; } + if let Some(additional_options) = self.additional_options { + len += additional_options.buffer_len(); + } len } @@ -755,6 +876,7 @@ impl<'a> Repr<'a> { let your_ip = packet.your_ip(); let server_ip = packet.server_ip(); let relay_agent_ip = packet.relay_agent_ip(); + let secs = packet.secs(); // only ethernet is supported right now match packet.hardware_type() { @@ -781,9 +903,7 @@ impl<'a> Repr<'a> { let mut max_size = None; let mut lease_duration = None; - let mut options = packet.options()?; - while !options.is_empty() { - let (next_options, option) = DhcpOption::parse(options)?; + for option in packet.options()?.parse() { match option { DhcpOption::EndOfList => break, DhcpOption::Pad => {} @@ -835,12 +955,12 @@ impl<'a> Repr<'a> { } DhcpOption::Other { .. } => {} } - options = next_options; } let broadcast = packet.flags().contains(Flags::BROADCAST); Ok(Repr { + secs, transaction_id, client_hardware_address, client_ip, @@ -858,6 +978,7 @@ impl<'a> Repr<'a> { max_size, lease_duration, message_type: message_type?, + additional_options: None, }) } @@ -874,7 +995,7 @@ impl<'a> Repr<'a> { packet.set_transaction_id(self.transaction_id); packet.set_client_hardware_address(self.client_hardware_address); packet.set_hops(0); - packet.set_secs(0); // TODO + packet.set_secs(self.secs); packet.set_magic_number(0x63825363); packet.set_client_ip(self.client_ip); packet.set_your_ip(self.your_ip); @@ -889,28 +1010,24 @@ impl<'a> Repr<'a> { { let mut options = packet.options_mut()?; - options = DhcpOption::MessageType(self.message_type).emit(options); - if let Some(eth_addr) = self.client_identifier { - options = DhcpOption::ClientIdentifier(eth_addr).emit(options); - } - if let Some(ip) = self.server_identifier { - options = DhcpOption::ServerIdentifier(ip).emit(options); - } - if let Some(ip) = self.router { - options = DhcpOption::Router(ip).emit(options); - } - if let Some(ip) = self.subnet_mask { - options = DhcpOption::SubnetMask(ip).emit(options); - } - if let Some(ip) = self.requested_ip { - options = DhcpOption::RequestedIp(ip).emit(options); - } - if let Some(size) = self.max_size { - options = DhcpOption::MaximumDhcpMessageSize(size).emit(options); - } - if let Some(duration) = self.lease_duration { - options = DhcpOption::IpLeaseTime(duration).emit(options); - } + options.emit( + iter::IntoIterator::into_iter([ + Some(DhcpOption::MessageType(self.message_type)), + self.client_identifier.map(DhcpOption::ClientIdentifier), + self.server_identifier.map(DhcpOption::ServerIdentifier), + self.router.map(DhcpOption::Router), + self.subnet_mask.map(DhcpOption::SubnetMask), + self.requested_ip.map(DhcpOption::RequestedIp), + self.max_size.map(DhcpOption::MaximumDhcpMessageSize), + self.lease_duration.map(DhcpOption::IpLeaseTime), + self.parameter_request_list.map(|list| DhcpOption::Other { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: list, + }), + ]) + .flatten(), + )?; + if let Some(dns_servers) = self.dns_servers { const IP_SIZE: usize = core::mem::size_of::(); let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE]; @@ -924,20 +1041,17 @@ impl<'a> Repr<'a> { }) .count() * IP_SIZE; - let option = DhcpOption::Other { + options.emit([DhcpOption::Other { kind: field::OPT_DOMAIN_NAME_SERVER, data: &servers[..data_len], - }; - options = option.emit(options); + }])?; } - if let Some(list) = self.parameter_request_list { - options = DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, - data: list, - } - .emit(options); + + if let Some(additional_options) = self.additional_options { + options.emit(additional_options.parse())?; } - DhcpOption::EndOfList.emit(options); + + options.emit([DhcpOption::EndOfList])?; } Ok(()) @@ -1042,37 +1156,31 @@ mod test { assert_eq!(packet.relay_agent_ip(), IP_NULL); assert_eq!(packet.client_hardware_address(), CLIENT_MAC); let options = packet.options().unwrap(); - assert_eq!(options.len(), 3 + 9 + 6 + 4 + 6 + 1 + 7); + assert_eq!(options.buffer_len(), 3 + 9 + 6 + 4 + 6 + 1 + 7); - let (options, message_type) = DhcpOption::parse(options).unwrap(); - assert_eq!(message_type, DhcpOption::MessageType(MessageType::Discover)); - assert_eq!(options.len(), 9 + 6 + 4 + 6 + 1 + 7); + let mut options = options.parse(); - let (options, client_id) = DhcpOption::parse(options).unwrap(); - assert_eq!(client_id, DhcpOption::ClientIdentifier(CLIENT_MAC)); - assert_eq!(options.len(), 6 + 4 + 6 + 1 + 7); - - let (options, client_id) = DhcpOption::parse(options).unwrap(); - assert_eq!(client_id, DhcpOption::RequestedIp(IP_NULL)); - assert_eq!(options.len(), 4 + 6 + 1 + 7); - - let (options, msg_size) = DhcpOption::parse(options).unwrap(); - assert_eq!(msg_size, DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)); - assert_eq!(options.len(), 6 + 1 + 7); - - let (options, client_id) = DhcpOption::parse(options).unwrap(); assert_eq!( - client_id, - DhcpOption::Other { + options.next(), + Some(DhcpOption::MessageType(MessageType::Discover)) + ); + assert_eq!( + options.next(), + Some(DhcpOption::ClientIdentifier(CLIENT_MAC)) + ); + assert_eq!(options.next(), Some(DhcpOption::RequestedIp(IP_NULL))); + assert_eq!( + options.next(), + Some(DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)) + ); + assert_eq!( + options.next(), + Some(DhcpOption::Other { kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42] - } + }) ); - assert_eq!(options.len(), 1 + 7); - - let (options, client_id) = DhcpOption::parse(options).unwrap(); - assert_eq!(client_id, DhcpOption::EndOfList); - assert_eq!(options.len(), 7); // padding + assert_eq!(options.next(), None); } #[test] @@ -1096,16 +1204,23 @@ mod test { { let mut options = packet.options_mut().unwrap(); - options = DhcpOption::MessageType(MessageType::Discover).emit(options); - options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(options); - options = DhcpOption::RequestedIp(IP_NULL).emit(options); - options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options); - let option = DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, - data: &[1, 3, 6, 42], - }; - options = option.emit(options); - DhcpOption::EndOfList.emit(options); + options + .emit([DhcpOption::MessageType(MessageType::Discover)]) + .unwrap(); + options + .emit([DhcpOption::ClientIdentifier(CLIENT_MAC)]) + .unwrap(); + options.emit([DhcpOption::RequestedIp(IP_NULL)]).unwrap(); + options + .emit([DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)]) + .unwrap(); + options + .emit([DhcpOption::Other { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: &[1, 3, 6, 42], + }]) + .unwrap(); + options.emit([DhcpOption::EndOfList]).unwrap(); } let packet = &mut packet.into_inner()[..]; @@ -1127,6 +1242,7 @@ mod test { router: Some(IP_NULL), subnet_mask: Some(IP_NULL), relay_agent_ip: IP_NULL, + secs: 0, broadcast: false, requested_ip: None, client_identifier: Some(CLIENT_MAC), @@ -1135,6 +1251,7 @@ mod test { dns_servers: None, max_size: None, lease_duration: Some(0xffff_ffff), // Infinite lease + additional_options: None, } } @@ -1150,6 +1267,7 @@ mod test { subnet_mask: None, relay_agent_ip: IP_NULL, broadcast: false, + secs: 0, max_size: Some(DHCP_SIZE), lease_duration: None, requested_ip: Some(IP_NULL), @@ -1157,6 +1275,7 @@ mod test { server_identifier: None, parameter_request_list: Some(&[1, 3, 6, 42]), dns_servers: None, + additional_options: None, } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 6b097620f..740dcfa2d 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -243,9 +243,9 @@ pub use self::tcp::{ #[cfg(feature = "proto-dhcpv4")] pub use self::dhcpv4::{ - MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr, - CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, - SERVER_PORT as DHCP_SERVER_PORT, + DhcpOption, DhcpOptionsBuffer, MessageType as DhcpMessageType, Packet as DhcpPacket, + Repr as DhcpRepr, CLIENT_PORT as DHCP_CLIENT_PORT, + MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT, }; /// Parsing a packet failed. From ffab9dbba9e74f71bb2ccda127b0110e1aeb879e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 6 Aug 2022 15:33:11 +0200 Subject: [PATCH 385/566] wire/dhcp: make some packet funcs infallible. --- src/wire/dhcpv4.rs | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 49404029d..750f4caab 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -542,24 +542,19 @@ impl> Packet { let field = &self.buffer.as_ref()[field::FLAGS]; Flags::from_bits_truncate(NetworkEndian::read_u16(field)) } -} -impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { /// Return a pointer to the options. #[inline] - pub fn options(&self) -> Result> { - let data = self.buffer.as_ref(); - data.get(field::OPTIONS) - .ok_or(Error) - .map(|buffer| DhcpOptionsBuffer { - buffer, - len: buffer.len(), - }) + pub fn options(&self) -> DhcpOptionsBuffer<&[u8]> { + let buffer = &self.buffer.as_ref()[field::OPTIONS]; + DhcpOptionsBuffer { + buffer, + len: buffer.len(), + } } - pub fn get_sname(&self) -> Result<&'a str> { - let data = self.buffer.as_ref(); - let data = data.get(field::SNAME).ok_or(Error)?; + pub fn get_sname(&self) -> Result<&str> { + let data = &self.buffer.as_ref()[field::SNAME]; let len = data.iter().position(|&x| x == 0).ok_or(Error)?; if len == 0 { return Err(Error); @@ -569,9 +564,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> { Ok(data) } - pub fn get_boot_file(&self) -> Result<&'a str> { - let data = self.buffer.as_ref(); - let data = data.get(field::FILE).ok_or(Error)?; + pub fn get_boot_file(&self) -> Result<&str> { + let data = &self.buffer.as_ref()[field::FILE]; let len = data.iter().position(|&x| x == 0).ok_or(Error)?; if len == 0 { return Err(Error); @@ -698,11 +692,8 @@ impl + AsMut<[u8]>> Packet { impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// Return a pointer to the options. #[inline] - pub fn options_mut(&mut self) -> Result> { - let data = self.buffer.as_mut(); - data.get_mut(field::OPTIONS) - .ok_or(Error) - .map(DhcpOptionsBuffer::new) + pub fn options_mut(&mut self) -> DhcpOptionsBuffer<&mut [u8]> { + DhcpOptionsBuffer::new(&mut self.buffer.as_mut()[field::OPTIONS]) } } @@ -866,7 +857,7 @@ impl<'a> Repr<'a> { } /// Parse a DHCP packet and return a high-level representation. - pub fn parse(packet: &Packet<&'a T>) -> Result + pub fn parse(packet: &'a Packet<&'a T>) -> Result where T: AsRef<[u8]> + ?Sized, { @@ -903,7 +894,7 @@ impl<'a> Repr<'a> { let mut max_size = None; let mut lease_duration = None; - for option in packet.options()?.parse() { + for option in packet.options().parse() { match option { DhcpOption::EndOfList => break, DhcpOption::Pad => {} @@ -1009,7 +1000,7 @@ impl<'a> Repr<'a> { packet.set_flags(flags); { - let mut options = packet.options_mut()?; + let mut options = packet.options_mut(); options.emit( iter::IntoIterator::into_iter([ Some(DhcpOption::MessageType(self.message_type)), @@ -1155,7 +1146,7 @@ mod test { assert_eq!(packet.server_ip(), IP_NULL); assert_eq!(packet.relay_agent_ip(), IP_NULL); assert_eq!(packet.client_hardware_address(), CLIENT_MAC); - let options = packet.options().unwrap(); + let options = packet.options(); assert_eq!(options.buffer_len(), 3 + 9 + 6 + 4 + 6 + 1 + 7); let mut options = options.parse(); @@ -1203,7 +1194,7 @@ mod test { packet.set_client_hardware_address(CLIENT_MAC); { - let mut options = packet.options_mut().unwrap(); + let mut options = packet.options_mut(); options .emit([DhcpOption::MessageType(MessageType::Discover)]) .unwrap(); From 47ce69bb44d8f25f51f7873eaad5eb5ee1463c28 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 Aug 2022 12:25:17 +0200 Subject: [PATCH 386/566] wire/dhcp: remove option enum, letting higher layers specify kind+data. This saves ~1.5kb of code size. Options like that generate a bit of code size bloat due to having many code paths that the compiler isn't able to optimize out. Also they don't scale well, adding every single option will make the bloat worse. --- src/socket/dhcpv4.rs | 16 +- src/wire/dhcpv4.rs | 535 ++++++++++++++++++------------------------- 2 files changed, 229 insertions(+), 322 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 5717f676f..ba50b0400 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -3,12 +3,12 @@ use core::task::Waker; use crate::iface::Context; use crate::time::{Duration, Instant}; -use crate::wire::dhcpv4::{field as dhcpv4_field, DhcpOptionsBuffer}; -use crate::wire::HardwareAddress; +use crate::wire::dhcpv4::field as dhcpv4_field; use crate::wire::{ DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr, UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, }; +use crate::wire::{DhcpOption, HardwareAddress}; #[cfg(feature = "async")] use super::WakerRegistration; @@ -150,7 +150,7 @@ pub struct Socket<'a> { /// A buffer contains options additional to be added to outgoing DHCP /// packets. - outgoing_options: Option>, + outgoing_options: &'a [DhcpOption<'a>], /// A buffer containing all requested parameters. parameter_request_list: Option<&'a [u8]>, @@ -180,7 +180,7 @@ impl<'a> Socket<'a> { max_lease_duration: None, retry_config: RetryConfig::default(), ignore_naks: false, - outgoing_options: None, + outgoing_options: &[], parameter_request_list: None, receive_packet_buffer: None, #[cfg(feature = "async")] @@ -193,9 +193,9 @@ impl<'a> Socket<'a> { self.retry_config = config; } - /// Set the outgoing options buffer. - pub fn set_outgoing_options_buffer(&mut self, options_buffer: DhcpOptionsBuffer<&'a [u8]>) { - self.outgoing_options = Some(options_buffer); + /// Set the outgoing options. + pub fn set_outgoing_options(&mut self, options: &'a [DhcpOption<'a>]) { + self.outgoing_options = options; } /// Set the buffer into which incoming DHCP packets are copied into. @@ -841,7 +841,7 @@ mod test { dns_servers: None, max_size: None, lease_duration: None, - additional_options: None, + additional_options: &[], }; const DHCP_DISCOVER: DhcpRepr = DhcpRepr { diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 750f4caab..6e18c9898 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -57,79 +57,46 @@ impl MessageType { } /// A buffer for DHCP options. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DhcpOptionsBuffer { +pub struct DhcpOptionsBuffer<'a> { /// The underlying buffer, directly from the DHCP packet representation. - buffer: T, - len: usize, + buffer: &'a mut [u8], } -impl> DhcpOptionsBuffer { - pub fn new(buffer: T) -> Self { - Self { buffer, len: 0 } - } - - pub fn buffer_len(&self) -> usize { - self.len +impl<'a> DhcpOptionsBuffer<'a> { + pub fn new(buffer: &'a mut [u8]) -> Self { + Self { buffer } } - pub fn map(self, f: F) -> DhcpOptionsBuffer - where - F: FnOnce(T) -> U, - { - DhcpOptionsBuffer { - buffer: f(self.buffer), - len: self.len, + /// Emit a [`DhcpOption`] into a [`DhcpOptionsBuffer`]. + pub fn emit(&mut self, option: DhcpOption<'_>) -> Result<()> { + if option.data.len() > u8::MAX as _ { + return Err(Error); } - } - pub fn map_ref<'a, F, U>(&'a self, f: F) -> DhcpOptionsBuffer - where - F: FnOnce(&'a T) -> U, - { - DhcpOptionsBuffer { - buffer: f(&self.buffer), - len: self.len, + let total_len = 2 + option.data.len(); + if self.buffer.len() < total_len { + return Err(Error); } - } -} -impl<'a, T: AsRef<[u8]> + ?Sized> DhcpOptionsBuffer<&'a T> { - /// Parse a [`DhcpOptionsBuffer`] into an iterator of [`DhcpOption`]. - /// - /// This will stop when it reaches [`DhcpOption::EndOfList`]. - pub fn parse(&self) -> impl Iterator> { - let mut buf = &self.buffer.as_ref()[..self.len]; - iter::from_fn(move || match DhcpOption::parse(buf) { - Ok((_, DhcpOption::EndOfList)) | Err(_) => None, - Ok((new_buf, option)) => { - buf = new_buf; - Some(option) - } - }) + let (buf, rest) = core::mem::take(&mut self.buffer).split_at_mut(total_len); + self.buffer = rest; + + buf[0] = option.kind; + buf[1] = option.data.len() as _; + buf[2..].copy_from_slice(option.data); + + Ok(()) } -} -impl + AsRef<[u8]>> DhcpOptionsBuffer { - /// Emit an iterator of [`DhcpOption`] into a [`DhcpOptionsBuffer`]. - pub fn emit<'a, I>(&mut self, options: I) -> Result<()> - where - I: IntoIterator>, - { - let mut buf = self.buffer.as_mut().get_mut(self.len..).unwrap_or(&mut []); - for option in options.into_iter() { - let option_size = option.buffer_len(); - let buf_len = buf.len(); - if option_size > buf_len { - return Err(Error); - } - buf = option.emit(buf); - if buf.len() != buf_len { - self.len += option_size; - } + pub fn end(&mut self) -> Result<()> { + if self.buffer.is_empty() { + return Err(Error); } + self.buffer[0] = field::OPT_END; + self.buffer = &mut []; Ok(()) } } @@ -137,162 +104,9 @@ impl + AsRef<[u8]>> DhcpOptionsBuffer { /// A representation of a single DHCP option. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DhcpOption<'a> { - EndOfList, - Pad, - MessageType(MessageType), - RequestedIp(Ipv4Address), - ClientIdentifier(EthernetAddress), - ServerIdentifier(Ipv4Address), - IpLeaseTime(u32), - Router(Ipv4Address), - SubnetMask(Ipv4Address), - MaximumDhcpMessageSize(u16), - Other { kind: u8, data: &'a [u8] }, -} - -impl<'a> DhcpOption<'a> { - pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], DhcpOption<'a>)> { - // See https://tools.ietf.org/html/rfc2132 for all possible DHCP options. - - let (skip_len, option); - match *buffer.first().ok_or(Error)? { - field::OPT_END => { - skip_len = 1; - option = DhcpOption::EndOfList; - } - field::OPT_PAD => { - skip_len = 1; - option = DhcpOption::Pad; - } - kind => { - let length = *buffer.get(1).ok_or(Error)? as usize; - skip_len = length + 2; - let data = buffer.get(2..skip_len).ok_or(Error)?; - match (kind, length) { - (field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(), - (field::OPT_DHCP_MESSAGE_TYPE, 1) => { - option = DhcpOption::MessageType(MessageType::from(data[0])); - } - (field::OPT_REQUESTED_IP, 4) => { - option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data)); - } - (field::OPT_CLIENT_ID, 7) => { - let hardware_type = Hardware::from(u16::from(data[0])); - if hardware_type != Hardware::Ethernet { - return Err(Error); - } - option = - DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..])); - } - (field::OPT_SERVER_IDENTIFIER, 4) => { - option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data)); - } - (field::OPT_ROUTER, 4) => { - option = DhcpOption::Router(Ipv4Address::from_bytes(data)); - } - (field::OPT_SUBNET_MASK, 4) => { - option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data)); - } - (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { - option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([ - data[0], data[1], - ])); - } - (field::OPT_IP_LEASE_TIME, 4) => { - option = DhcpOption::IpLeaseTime(u32::from_be_bytes([ - data[0], data[1], data[2], data[3], - ])) - } - (_, _) => { - option = DhcpOption::Other { - kind: kind, - data: data, - }; - } - } - } - } - Ok((&buffer[skip_len..], option)) - } - - pub fn buffer_len(&self) -> usize { - match self { - &DhcpOption::EndOfList => 1, - &DhcpOption::Pad => 1, - &DhcpOption::MessageType(_) => 3, - &DhcpOption::ClientIdentifier(eth_addr) => 3 + eth_addr.as_bytes().len(), - &DhcpOption::RequestedIp(ip) - | &DhcpOption::ServerIdentifier(ip) - | &DhcpOption::Router(ip) - | &DhcpOption::SubnetMask(ip) => 2 + ip.as_bytes().len(), - &DhcpOption::MaximumDhcpMessageSize(_) => 4, - &DhcpOption::IpLeaseTime(_) => 6, - &DhcpOption::Other { data, .. } => 2 + data.len(), - } - } - - pub fn emit<'b>(&self, buffer: &'b mut [u8]) -> &'b mut [u8] { - let skip_length; - match *self { - DhcpOption::EndOfList => { - skip_length = 1; - buffer[0] = field::OPT_END; - } - DhcpOption::Pad => { - skip_length = 1; - buffer[0] = field::OPT_PAD; - } - _ => { - skip_length = self.buffer_len(); - buffer[1] = (skip_length - 2) as u8; - match *self { - DhcpOption::EndOfList | DhcpOption::Pad => unreachable!(), - DhcpOption::MessageType(value) => { - buffer[0] = field::OPT_DHCP_MESSAGE_TYPE; - buffer[2] = value.into(); - } - DhcpOption::ClientIdentifier(eth_addr) => { - buffer[0] = field::OPT_CLIENT_ID; - buffer[2] = u16::from(Hardware::Ethernet) as u8; - buffer[3..9].copy_from_slice(eth_addr.as_bytes()); - } - DhcpOption::RequestedIp(ip) => { - buffer[0] = field::OPT_REQUESTED_IP; - buffer[2..6].copy_from_slice(ip.as_bytes()); - } - DhcpOption::ServerIdentifier(ip) => { - buffer[0] = field::OPT_SERVER_IDENTIFIER; - buffer[2..6].copy_from_slice(ip.as_bytes()); - } - DhcpOption::Router(ip) => { - buffer[0] = field::OPT_ROUTER; - buffer[2..6].copy_from_slice(ip.as_bytes()); - } - DhcpOption::SubnetMask(mask) => { - buffer[0] = field::OPT_SUBNET_MASK; - buffer[2..6].copy_from_slice(mask.as_bytes()); - } - DhcpOption::MaximumDhcpMessageSize(size) => { - buffer[0] = field::OPT_MAX_DHCP_MESSAGE_SIZE; - buffer[2..4].copy_from_slice(&size.to_be_bytes()[..]); - } - DhcpOption::IpLeaseTime(lease_time) => { - buffer[0] = field::OPT_IP_LEASE_TIME; - buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]); - } - DhcpOption::Other { - kind, - data: provided, - } => { - buffer[0] = kind; - buffer[2..skip_length].copy_from_slice(provided); - } - } - } - } - &mut buffer[skip_length..] - } +pub struct DhcpOption<'a> { + pub kind: u8, + pub data: &'a [u8], } /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer. @@ -543,14 +357,41 @@ impl> Packet { Flags::from_bits_truncate(NetworkEndian::read_u16(field)) } - /// Return a pointer to the options. + /// Return an iterator over the options. #[inline] - pub fn options(&self) -> DhcpOptionsBuffer<&[u8]> { - let buffer = &self.buffer.as_ref()[field::OPTIONS]; - DhcpOptionsBuffer { - buffer, - len: buffer.len(), - } + pub fn options(&self) -> impl Iterator> + '_ { + let mut buf = &self.buffer.as_ref()[field::OPTIONS]; + iter::from_fn(move || { + loop { + match buf.get(0).copied() { + // No more options, return. + None => return None, + Some(field::OPT_END) => return None, + + // Skip padding. + Some(field::OPT_PAD) => buf = &buf[1..], + Some(kind) => { + if buf.len() < 2 { + return None; + } + + let len = buf[1] as usize; + + if buf.len() < 2 + len { + return None; + } + + let opt = DhcpOption { + kind, + data: &buf[2..2 + len], + }; + + buf = &buf[2 + len..]; + return Some(opt); + } + } + } + }) } pub fn get_sname(&self) -> Result<&str> { @@ -692,7 +533,7 @@ impl + AsMut<[u8]>> Packet { impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// Return a pointer to the options. #[inline] - pub fn options_mut(&mut self) -> DhcpOptionsBuffer<&mut [u8]> { + pub fn options_mut(&mut self) -> DhcpOptionsBuffer<'_> { DhcpOptionsBuffer::new(&mut self.buffer.as_mut()[field::OPTIONS]) } } @@ -812,7 +653,7 @@ pub struct Repr<'a> { /// When returned from [`Repr::parse`], this field will be `None`. /// However, when calling [`Repr::emit`], this field should contain only /// additional DHCP options not known to smoltcp. - pub additional_options: Option>, + pub additional_options: &'a [DhcpOption<'a>], } impl<'a> Repr<'a> { @@ -849,8 +690,8 @@ impl<'a> Repr<'a> { if let Some(list) = self.parameter_request_list { len += list.len() + 2; } - if let Some(additional_options) = self.additional_options { - len += additional_options.buffer_len(); + for opt in self.additional_options { + len += 2 + opt.data.len() } len @@ -894,46 +735,44 @@ impl<'a> Repr<'a> { let mut max_size = None; let mut lease_duration = None; - for option in packet.options().parse() { - match option { - DhcpOption::EndOfList => break, - DhcpOption::Pad => {} - DhcpOption::MessageType(value) => { + for option in packet.options() { + let data = option.data; + match (option.kind, data.len()) { + (field::OPT_DHCP_MESSAGE_TYPE, 1) => { + let value = MessageType::from(data[0]); if value.opcode() == packet.opcode() { message_type = Ok(value); } } - DhcpOption::RequestedIp(ip) => { - requested_ip = Some(ip); + (field::OPT_REQUESTED_IP, 4) => { + requested_ip = Some(Ipv4Address::from_bytes(data)); } - DhcpOption::ClientIdentifier(eth_addr) => { - client_identifier = Some(eth_addr); + (field::OPT_CLIENT_ID, 7) => { + let hardware_type = Hardware::from(u16::from(data[0])); + if hardware_type != Hardware::Ethernet { + return Err(Error); + } + client_identifier = Some(EthernetAddress::from_bytes(&data[1..])); } - DhcpOption::ServerIdentifier(ip) => { - server_identifier = Some(ip); + (field::OPT_SERVER_IDENTIFIER, 4) => { + server_identifier = Some(Ipv4Address::from_bytes(data)); } - DhcpOption::Router(ip) => { - router = Some(ip); + (field::OPT_ROUTER, 4) => { + router = Some(Ipv4Address::from_bytes(data)); } - DhcpOption::SubnetMask(mask) => { - subnet_mask = Some(mask); + (field::OPT_SUBNET_MASK, 4) => { + subnet_mask = Some(Ipv4Address::from_bytes(data)); } - DhcpOption::MaximumDhcpMessageSize(size) => { - max_size = Some(size); + (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { + max_size = Some(u16::from_be_bytes([data[0], data[1]])); } - DhcpOption::IpLeaseTime(duration) => { - lease_duration = Some(duration); + (field::OPT_IP_LEASE_TIME, 4) => { + lease_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) } - DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, - data, - } => { + (field::OPT_PARAMETER_REQUEST_LIST, _) => { parameter_request_list = Some(data); } - DhcpOption::Other { - kind: field::OPT_DOMAIN_NAME_SERVER, - data, - } => { + (field::OPT_DOMAIN_NAME_SERVER, _) => { let mut servers = [None; MAX_DNS_SERVER_COUNT]; let chunk_size = 4; for (server, chunk) in servers.iter_mut().zip(data.chunks(chunk_size)) { @@ -944,7 +783,7 @@ impl<'a> Repr<'a> { } dns_servers = Some(servers); } - DhcpOption::Other { .. } => {} + _ => {} } } @@ -969,7 +808,7 @@ impl<'a> Repr<'a> { max_size, lease_duration, message_type: message_type?, - additional_options: None, + additional_options: &[], }) } @@ -1001,23 +840,66 @@ impl<'a> Repr<'a> { { let mut options = packet.options_mut(); - options.emit( - iter::IntoIterator::into_iter([ - Some(DhcpOption::MessageType(self.message_type)), - self.client_identifier.map(DhcpOption::ClientIdentifier), - self.server_identifier.map(DhcpOption::ServerIdentifier), - self.router.map(DhcpOption::Router), - self.subnet_mask.map(DhcpOption::SubnetMask), - self.requested_ip.map(DhcpOption::RequestedIp), - self.max_size.map(DhcpOption::MaximumDhcpMessageSize), - self.lease_duration.map(DhcpOption::IpLeaseTime), - self.parameter_request_list.map(|list| DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, - data: list, - }), - ]) - .flatten(), - )?; + + options.emit(DhcpOption { + kind: field::OPT_DHCP_MESSAGE_TYPE, + data: &[self.message_type.into()], + })?; + + if let Some(val) = &self.client_identifier { + let mut data = [0; 7]; + data[0] = u16::from(Hardware::Ethernet) as u8; + data[1..].copy_from_slice(val.as_bytes()); + + options.emit(DhcpOption { + kind: field::OPT_CLIENT_ID, + data: &data, + })?; + } + + if let Some(val) = &self.server_identifier { + options.emit(DhcpOption { + kind: field::OPT_SERVER_IDENTIFIER, + data: val.as_bytes(), + })?; + } + + if let Some(val) = &self.router { + options.emit(DhcpOption { + kind: field::OPT_ROUTER, + data: val.as_bytes(), + })?; + } + if let Some(val) = &self.subnet_mask { + options.emit(DhcpOption { + kind: field::OPT_SUBNET_MASK, + data: val.as_bytes(), + })?; + } + if let Some(val) = &self.requested_ip { + options.emit(DhcpOption { + kind: field::OPT_REQUESTED_IP, + data: val.as_bytes(), + })?; + } + if let Some(val) = &self.max_size { + options.emit(DhcpOption { + kind: field::OPT_MAX_DHCP_MESSAGE_SIZE, + data: &val.to_be_bytes(), + })?; + } + if let Some(val) = &self.lease_duration { + options.emit(DhcpOption { + kind: field::OPT_IP_LEASE_TIME, + data: &val.to_be_bytes(), + })?; + } + if let Some(val) = &self.parameter_request_list { + options.emit(DhcpOption { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: val, + })?; + } if let Some(dns_servers) = self.dns_servers { const IP_SIZE: usize = core::mem::size_of::(); @@ -1032,17 +914,17 @@ impl<'a> Repr<'a> { }) .count() * IP_SIZE; - options.emit([DhcpOption::Other { + options.emit(DhcpOption { kind: field::OPT_DOMAIN_NAME_SERVER, data: &servers[..data_len], - }])?; + })?; } - if let Some(additional_options) = self.additional_options { - options.emit(additional_options.parse())?; + for option in self.additional_options { + options.emit(*option)?; } - options.emit([DhcpOption::EndOfList])?; + options.end()?; } Ok(()) @@ -1146,27 +1028,39 @@ mod test { assert_eq!(packet.server_ip(), IP_NULL); assert_eq!(packet.relay_agent_ip(), IP_NULL); assert_eq!(packet.client_hardware_address(), CLIENT_MAC); - let options = packet.options(); - assert_eq!(options.buffer_len(), 3 + 9 + 6 + 4 + 6 + 1 + 7); - - let mut options = options.parse(); + let mut options = packet.options(); assert_eq!( options.next(), - Some(DhcpOption::MessageType(MessageType::Discover)) + Some(DhcpOption { + kind: field::OPT_DHCP_MESSAGE_TYPE, + data: &[0x01] + }) ); assert_eq!( options.next(), - Some(DhcpOption::ClientIdentifier(CLIENT_MAC)) + Some(DhcpOption { + kind: field::OPT_CLIENT_ID, + data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42], + }) ); - assert_eq!(options.next(), Some(DhcpOption::RequestedIp(IP_NULL))); assert_eq!( options.next(), - Some(DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)) + Some(DhcpOption { + kind: field::OPT_REQUESTED_IP, + data: &[0x00, 0x00, 0x00, 0x00], + }) ); assert_eq!( options.next(), - Some(DhcpOption::Other { + Some(DhcpOption { + kind: field::OPT_MAX_DHCP_MESSAGE_SIZE, + data: &DHCP_SIZE.to_be_bytes(), + }) + ); + assert_eq!( + options.next(), + Some(DhcpOption { kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42] }) @@ -1193,26 +1087,39 @@ mod test { packet.set_relay_agent_ip(IP_NULL); packet.set_client_hardware_address(CLIENT_MAC); - { - let mut options = packet.options_mut(); - options - .emit([DhcpOption::MessageType(MessageType::Discover)]) - .unwrap(); - options - .emit([DhcpOption::ClientIdentifier(CLIENT_MAC)]) - .unwrap(); - options.emit([DhcpOption::RequestedIp(IP_NULL)]).unwrap(); - options - .emit([DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE)]) - .unwrap(); - options - .emit([DhcpOption::Other { - kind: field::OPT_PARAMETER_REQUEST_LIST, - data: &[1, 3, 6, 42], - }]) - .unwrap(); - options.emit([DhcpOption::EndOfList]).unwrap(); - } + let mut options = packet.options_mut(); + + options + .emit(DhcpOption { + kind: field::OPT_DHCP_MESSAGE_TYPE, + data: &[0x01], + }) + .unwrap(); + options + .emit(DhcpOption { + kind: field::OPT_CLIENT_ID, + data: &[0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42], + }) + .unwrap(); + options + .emit(DhcpOption { + kind: field::OPT_REQUESTED_IP, + data: &[0x00, 0x00, 0x00, 0x00], + }) + .unwrap(); + options + .emit(DhcpOption { + kind: field::OPT_MAX_DHCP_MESSAGE_SIZE, + data: &DHCP_SIZE.to_be_bytes(), + }) + .unwrap(); + options + .emit(DhcpOption { + kind: field::OPT_PARAMETER_REQUEST_LIST, + data: &[1, 3, 6, 42], + }) + .unwrap(); + options.end().unwrap(); let packet = &mut packet.into_inner()[..]; for byte in &mut packet[269..276] { @@ -1242,7 +1149,7 @@ mod test { dns_servers: None, max_size: None, lease_duration: Some(0xffff_ffff), // Infinite lease - additional_options: None, + additional_options: &[], } } @@ -1266,7 +1173,7 @@ mod test { server_identifier: None, parameter_request_list: Some(&[1, 3, 6, 42]), dns_servers: None, - additional_options: None, + additional_options: &[], } } @@ -1330,15 +1237,15 @@ mod test { #[test] fn test_emit_dhcp_option() { static DATA: &[u8] = &[1, 3, 6]; - let mut bytes = vec![0xa5; 5]; - let dhcp_option = DhcpOption::Other { + let dhcp_option = DhcpOption { kind: field::OPT_PARAMETER_REQUEST_LIST, data: DATA, }; - { - let rest = dhcp_option.emit(&mut bytes); - assert_eq!(rest.len(), 0); - } + + let mut bytes = vec![0xa5; 5]; + let mut writer = DhcpOptionsBuffer::new(&mut bytes); + writer.emit(dhcp_option).unwrap(); + assert_eq!( &bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8] From 2566754daf2933fc25235af6ec141455fe90fe47 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 Aug 2022 13:43:38 +0200 Subject: [PATCH 387/566] dhcp/wire: Rename DhcpOptionsBuffer -> DhcpOptionWriter --- src/wire/dhcpv4.rs | 12 ++++++------ src/wire/mod.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 6e18c9898..65059adbb 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -59,17 +59,17 @@ impl MessageType { /// A buffer for DHCP options. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DhcpOptionsBuffer<'a> { +pub struct DhcpOptionWriter<'a> { /// The underlying buffer, directly from the DHCP packet representation. buffer: &'a mut [u8], } -impl<'a> DhcpOptionsBuffer<'a> { +impl<'a> DhcpOptionWriter<'a> { pub fn new(buffer: &'a mut [u8]) -> Self { Self { buffer } } - /// Emit a [`DhcpOption`] into a [`DhcpOptionsBuffer`]. + /// Emit a [`DhcpOption`] into a [`DhcpOptionWriter`]. pub fn emit(&mut self, option: DhcpOption<'_>) -> Result<()> { if option.data.len() > u8::MAX as _ { return Err(Error); @@ -533,8 +533,8 @@ impl + AsMut<[u8]>> Packet { impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// Return a pointer to the options. #[inline] - pub fn options_mut(&mut self) -> DhcpOptionsBuffer<'_> { - DhcpOptionsBuffer::new(&mut self.buffer.as_mut()[field::OPTIONS]) + pub fn options_mut(&mut self) -> DhcpOptionWriter<'_> { + DhcpOptionWriter::new(&mut self.buffer.as_mut()[field::OPTIONS]) } } @@ -1243,7 +1243,7 @@ mod test { }; let mut bytes = vec![0xa5; 5]; - let mut writer = DhcpOptionsBuffer::new(&mut bytes); + let mut writer = DhcpOptionWriter::new(&mut bytes); writer.emit(dhcp_option).unwrap(); assert_eq!( diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 740dcfa2d..65b5bb93f 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -243,7 +243,7 @@ pub use self::tcp::{ #[cfg(feature = "proto-dhcpv4")] pub use self::dhcpv4::{ - DhcpOption, DhcpOptionsBuffer, MessageType as DhcpMessageType, Packet as DhcpPacket, + DhcpOption, DhcpOptionWriter, MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr, CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT, }; From 188a179f8f9475530f92ecb297a59be44358c4e6 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 5 Aug 2022 22:05:47 -0700 Subject: [PATCH 388/566] Limit the permissions of the clippy action actions-rs/clippy-check only requires read/write access to the "checks" scope. When the "permissions" object is present, all of the scopes default to "none". --- .github/workflows/clippy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index ba4c6f5e3..d43e7279e 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -9,6 +9,8 @@ jobs: runs-on: ubuntu-latest env: RUSTUP_TOOLCHAIN: stable + permissions: + checks: write steps: - uses: actions/checkout@v2 if: github.event_name == 'pull_request_target' From 28e6fd894d70d070e01ca8e4f8568a860e48f536 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 10 Aug 2022 18:23:42 +0100 Subject: [PATCH 389/566] Asserts packets are sent in TCP tests - Assert the emit callback is invoked exactly once. - Add a separate macro for checking that no packets were sent. --- src/socket/tcp.rs | 69 ++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 50a341d11..8e5d64959 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2425,6 +2425,7 @@ mod test { { socket.cx.set_now(timestamp); + let mut sent = 0; let result = socket .socket .dispatch(&mut socket.cx, |_, (ip_repr, tcp_repr)| { @@ -2434,14 +2435,27 @@ mod test { assert_eq!(ip_repr.payload_len(), tcp_repr.buffer_len()); net_trace!("recv: {}", tcp_repr); + sent += 1; Ok(f(Ok(tcp_repr))) }); match result { - Ok(()) => (), + Ok(()) => assert_eq!(sent, 1, "Exactly one packet should be sent"), Err(e) => f(Err(e)), } } + fn recv_nothing(socket: &mut TestSocket, timestamp: Instant) { + socket.cx.set_now(timestamp); + + let result: Result<(), ()> = socket + .socket + .dispatch(&mut socket.cx, |_, (_ip_repr, _tcp_repr)| { + panic!("Should not send a packet") + }); + + assert_eq!(result, Ok(())) + } + macro_rules! send { ($socket:ident, $repr:expr) => (send!($socket, time 0, $repr)); @@ -2456,7 +2470,7 @@ mod test { macro_rules! recv { ($socket:ident, [$( $repr:expr ),*]) => ({ $( recv!($socket, Ok($repr)); )* - recv!($socket, Err(Error::Exhausted)) + recv_nothing!($socket) }); ($socket:ident, $result:expr) => (recv!($socket, time 0, $result)); @@ -2473,6 +2487,11 @@ mod test { (recv(&mut $socket, Instant::from_millis($time), |repr| assert_eq!(repr, $result))); } + macro_rules! recv_nothing { + ($socket:ident) => (recv_nothing!($socket, time 0)); + ($socket:ident, time $time:expr) => (recv_nothing(&mut $socket, Instant::from_millis($time))); + } + macro_rules! sanity { ($socket1:expr, $socket2:expr) => {{ let (s1, s2) = ($socket1, $socket2); @@ -3201,7 +3220,7 @@ mod test { ..RECV_TEMPL }] ); - recv!(s, time 1000, Err(Error::Exhausted)); + recv_nothing!(s, time 1000); assert_eq!(s.state, State::Established); sanity!(s, socket_established()); } @@ -4376,7 +4395,7 @@ mod test { }] ); assert_eq!(s.state, State::TimeWait); - recv!(s, time 60_000, Err(Error::Exhausted)); + recv_nothing!(s, time 60_000); assert_eq!(s.state, State::Closed); } @@ -4910,7 +4929,7 @@ mod test { payload: &b"abcdef"[..], ..RECV_TEMPL })); - recv!(s, time 1050, Err(Error::Exhausted)); + recv_nothing!(s, time 1050); recv!(s, time 2000, Ok(TcpRepr { seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), @@ -4939,9 +4958,9 @@ mod test { payload: &b"012345"[..], ..RECV_TEMPL }), exact); - recv!(s, time 0, Err(Error::Exhausted)); + recv_nothing!(s, time 0); - recv!(s, time 50, Err(Error::Exhausted)); + recv_nothing!(s, time 50); recv!(s, time 1000, Ok(TcpRepr { control: TcpControl::None, @@ -4957,7 +4976,7 @@ mod test { payload: &b"012345"[..], ..RECV_TEMPL }), exact); - recv!(s, time 1550, Err(Error::Exhausted)); + recv_nothing!(s, time 1550); } #[test] @@ -5534,7 +5553,7 @@ mod test { // even though we're in "fast retransmit", we shouldn't // force-send anything because the remote's window is full. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); } // =========================================================================================// @@ -5616,7 +5635,7 @@ mod test { assert_eq!(s.recv_slice(rx_buf), Ok(4)); // check that we do NOT send a window update even if it has changed. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); } #[test] @@ -5649,7 +5668,7 @@ mod test { assert_eq!(s.recv_slice(rx_buf), Ok(4)); // check that we do NOT send a window update even if it has changed. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); } // =========================================================================================// @@ -5765,7 +5784,7 @@ mod test { ..RECV_TEMPL }] ); - recv!(s, time 0, Err(Error::Exhausted)); + recv_nothing!(s, time 0); s.recv(|buffer| { assert_eq!(&buffer[..3], b"abc"); (3, ()) @@ -5777,7 +5796,7 @@ mod test { window_len: 3, ..RECV_TEMPL })); - recv!(s, time 0, Err(Error::Exhausted)); + recv_nothing!(s, time 0); s.recv(|buffer| { assert_eq!(buffer, b"def"); (buffer.len(), ()) @@ -5941,7 +5960,7 @@ mod test { fn test_established_timeout() { let mut s = socket_established(); s.set_timeout(Some(Duration::from_millis(1000))); - recv!(s, time 250, Err(Error::Exhausted)); + recv_nothing!(s, time 250); assert_eq!( s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(1250)) @@ -5988,7 +6007,7 @@ mod test { payload: &[0], ..RECV_TEMPL })); - recv!(s, time 100, Err(Error::Exhausted)); + recv_nothing!(s, time 100); assert_eq!( s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(150)) @@ -6008,19 +6027,19 @@ mod test { payload: &[0], ..RECV_TEMPL })); - recv!(s, time 155, Err(Error::Exhausted)); + recv_nothing!(s, time 155); assert_eq!( s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(205)) ); - recv!(s, time 200, Err(Error::Exhausted)); + recv_nothing!(s, time 200); recv!(s, time 205, Ok(TcpRepr { control: TcpControl::Rst, seq_number: LOCAL_SEQ + 1, ack_number: Some(REMOTE_SEQ + 1), ..RECV_TEMPL })); - recv!(s, time 205, Err(Error::Exhausted)); + recv_nothing!(s, time 205); assert_eq!(s.state, State::Closed); } @@ -6118,7 +6137,7 @@ mod test { s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(100)) ); - recv!(s, time 95, Err(Error::Exhausted)); + recv_nothing!(s, time 95); recv!(s, time 100, Ok(TcpRepr { seq_number: LOCAL_SEQ, ack_number: Some(REMOTE_SEQ + 1), @@ -6130,7 +6149,7 @@ mod test { s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(200)) ); - recv!(s, time 195, Err(Error::Exhausted)); + recv_nothing!(s, time 195); recv!(s, time 200, Ok(TcpRepr { seq_number: LOCAL_SEQ, ack_number: Some(REMOTE_SEQ + 1), @@ -6147,7 +6166,7 @@ mod test { s.socket.poll_at(&mut s.cx), PollAt::Time(Instant::from_millis(350)) ); - recv!(s, time 345, Err(Error::Exhausted)); + recv_nothing!(s, time 345); recv!(s, time 350, Ok(TcpRepr { seq_number: LOCAL_SEQ, ack_number: Some(REMOTE_SEQ + 1), @@ -6512,7 +6531,7 @@ mod test { ); // No ACK is immediately sent. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); // After 10ms, it is sent. recv!(s, time 11, Ok(TcpRepr { @@ -6545,7 +6564,7 @@ mod test { .unwrap(); // However, no ACK or window update is immediately sent. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); // After 10ms, it is sent. recv!(s, time 11, Ok(TcpRepr { @@ -6605,7 +6624,7 @@ mod test { ); // No ACK is immediately sent. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); send!( s, @@ -6644,7 +6663,7 @@ mod test { ); // No ACK is immediately sent. - recv!(s, Err(Error::Exhausted)); + recv_nothing!(s); send!( s, From a25c9f2f47fd085c274e63e685755be507c0c7b7 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 11 Aug 2022 11:53:15 +0100 Subject: [PATCH 390/566] Only clear retransmit timer when all packets are acked --- src/socket/tcp.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8e5d64959..9387a149d 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1460,6 +1460,7 @@ impl<'a> Socket<'a> { // from the sequence space. let mut ack_len = 0; let mut ack_of_fin = false; + let mut ack_all = false; if repr.control != TcpControl::Rst { if let Some(ack_number) = repr.ack_number { // Sequence number corresponding to the first byte in `tx_buffer`. @@ -1477,6 +1478,8 @@ impl<'a> Socket<'a> { tcp_trace!("received ACK of FIN"); ack_of_fin = true; } + + ack_all = self.remote_last_seq == ack_number } self.rtte.on_ack(cx.now(), ack_number); @@ -1585,7 +1588,7 @@ impl<'a> Socket<'a> { // 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 { + if !self.timer.is_retransmit() || ack_all { self.timer.set_for_idle(cx.now(), self.keep_alive); } } @@ -1604,7 +1607,9 @@ impl<'a> Socket<'a> { if ack_of_fin { self.set_state(State::FinWait2); } - self.timer.set_for_idle(cx.now(), self.keep_alive); + if ack_all { + 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 @@ -4979,6 +4984,85 @@ mod test { recv_nothing!(s, time 1550); } + #[test] + fn test_data_retransmit_bursts_half_ack() { + let mut s = socket_established(); + s.remote_mss = 6; + s.send_slice(b"abcdef012345").unwrap(); + + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::None, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }), exact); + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::Psh, + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"012345"[..], + ..RECV_TEMPL + }), exact); + // Acknowledge the first packet + send!(s, time 5, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6), + window_len: 6, + ..SEND_TEMPL + }); + // The second packet should be re-sent. + recv!(s, time 1500, Ok(TcpRepr { + control: TcpControl::Psh, + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"012345"[..], + ..RECV_TEMPL + }), exact); + + recv_nothing!(s, time 1550); + } + + #[test] + fn test_data_retransmit_bursts_half_ack_close() { + let mut s = socket_established(); + s.remote_mss = 6; + s.send_slice(b"abcdef012345").unwrap(); + s.close(); + + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::None, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }), exact); + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"012345"[..], + ..RECV_TEMPL + }), exact); + // Acknowledge the first packet + send!(s, time 5, TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1 + 6), + window_len: 6, + ..SEND_TEMPL + }); + // The second packet should be re-sent. + recv!(s, time 1500, Ok(TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"012345"[..], + ..RECV_TEMPL + }), exact); + + recv_nothing!(s, time 1550); + } + #[test] fn test_send_data_after_syn_ack_retransmit() { let mut s = socket_syn_received(); From 8cf8a4c9679f3e7548fa3dbfd7e1449e600c4281 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Thu, 11 Aug 2022 10:50:34 -0700 Subject: [PATCH 391/566] Enforce minimum supported Rust version This instructs Cargo to immediately fail compilation if the version of Rust is too old [1]. Hopefully, this will save someone some headache. [1]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 62650571b..e4ff78b2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "smoltcp" version = "0.8.0" edition = "2018" +rust-version = "1.60" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." documentation = "https://docs.rs/smoltcp/" From 8153a764339d4114479f4bb9819c3a910687cb45 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Thu, 11 Aug 2022 11:15:14 -0700 Subject: [PATCH 392/566] Pin clippy to minimum supported Rust version The rationale is the same as discussed in 0b0f96e: > It can be rather surprising when new lints pop up when a new stable > toolchain is released. Let's pin this check to a specific version to > avoid those surprises. In deciding which version of clippy to use, I went with the MSRV since that's what's been done historically. The other option was to read from the repo a version number specifically for clippy, but I was afraid that adding one more version number to juggle would increase the odds that it would be forgotten and fall out of sync. Note that this approach uses rustup to install the toolchain dynamically rather than making use of an action. The advantage of this method is that it allows a single pull request to contain the version bump and suggested code changes (this is due to the fact that actions-rs/clippy-check requires a GitHub API token with write permission, but a token of this type is only available when triggering on `pull_request_target` which runs the action using the configuration from the base of the pull request rather than the merge commit). The disadvantage of this approach is that the toolchain setup can no longer be cached by the underlying layering mechanism used by GitHub actions (unlikely to significantly affect this project). --- .github/workflows/clippy.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d43e7279e..111b1ef99 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -7,8 +7,6 @@ name: Clippy check jobs: clippy: runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable permissions: checks: write steps: @@ -18,6 +16,9 @@ jobs: ref: refs/pull/${{ github.event.number }}/head - uses: actions/checkout@v2 if: github.event_name != 'pull_request_target' + - run: sed -n 's,^rust-version = "\(.*\)"$,RUSTUP_TOOLCHAIN=\1,p' Cargo.toml >> $GITHUB_ENV + - run: rustup toolchain install $RUSTUP_TOOLCHAIN + - run: rustup component add clippy - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} From f9f3ab82709bb66fc43dac527d4a6f76707f597e Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Mon, 15 Aug 2022 18:59:54 +0100 Subject: [PATCH 393/566] Send incomplete fin packets even if nagle enabled If we have an incomplete packet to send and the socket has been closed then send it. Without this change (with nagle enabled) we wait for the penultimate packet in the stream to be acked before sending the final packet even if there's plenty of space in the window. --- src/socket/tcp.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 9387a149d..a3d813770 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1898,7 +1898,12 @@ impl<'a> Socket<'a> { _ => false, }; - if self.nagle && data_in_flight && !can_send_full { + // If we're applying the Nagle algorithm we don't want to send more + // until one of: + // * There's no data in flight + // * We can send a full packet + // * We have all the data we'll ever send (we're closing send) + if self.nagle && data_in_flight && !can_send_full && !want_fin { can_send = false; } @@ -6847,6 +6852,29 @@ mod test { ); } + #[test] + fn test_final_packet_in_stream_doesnt_wait_for_nagle() { + let mut s = socket_established(); + s.remote_mss = 6; + s.send_slice(b"abcdef0").unwrap(); + s.socket.close(); + + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::None, + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abcdef"[..], + ..RECV_TEMPL + }), exact); + recv!(s, time 0, Ok(TcpRepr { + control: TcpControl::Fin, + seq_number: LOCAL_SEQ + 1 + 6, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"0"[..], + ..RECV_TEMPL + }), exact); + } + // =========================================================================================// // Tests for packet filtering. // =========================================================================================// From f9d2bbad77b39b33450367128b9f7bd5dc434cfe Mon Sep 17 00:00:00 2001 From: Ritik Mishra Date: Wed, 17 Aug 2022 10:56:37 -0700 Subject: [PATCH 394/566] change `respond` closure to propogate errors up --- src/iface/interface.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 1ce7bf031..f5128da83 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1084,18 +1084,21 @@ impl<'a> Interface<'a> { match device.transmit().ok_or(Error::Exhausted) { Ok(_t) => { #[cfg(feature = "proto-sixlowpan-fragmentation")] - if let Err(_e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { - net_debug!("failed to dispatch IP: {}", _e); + if let Err(e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { + net_debug!("failed to dispatch IP: {}", e); + return Err(e); } #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - if let Err(_e) = inner.dispatch_ip(_t, response, None) { - net_debug!("failed to dispatch IP: {}", _e); + if let Err(e) = inner.dispatch_ip(_t, response, None) { + net_debug!("failed to dispatch IP: {}", e); + return Err(e); } emitted_any = true; } Err(e) => { net_debug!("failed to transmit IP: {}", e); + return Err(e); } } From 72002515ebc7d80c5520f907d0214c89e8462834 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 22 Aug 2022 09:50:14 -0400 Subject: [PATCH 395/566] Allows processing of DNS on 6LowPan connections --- src/iface/interface.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index f5128da83..e5a4caba5 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1788,6 +1788,22 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "socket-dns")] + for dns_socket in sockets + .items_mut() + .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket)) + { + if dns_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { + dns_socket.process( + self, + &IpRepr::Ipv6(ipv6_repr), + &udp_repr, + udp_packet.payload(), + ); + return None; + } + } + // When we are here then then there was no UDP socket that accepted the UDP // message. let payload_len = icmp_reply_payload_len( From cc450441264580f725904636f595eeada23746a6 Mon Sep 17 00:00:00 2001 From: Michael Zimmermann Date: Sun, 28 Aug 2022 12:52:17 +0200 Subject: [PATCH 396/566] fix socket feature check it requires you to have at least one socket type enabled but the feature `socket-dhcp` does not exist because it's name is `socket-dhcpv4`. So if that's only socket type you want enabled you weren't able to do that due to this broken check. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c70efdbad..09bc1169f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ compile_error!("You must enable at least one of the following features: proto-ip feature = "socket-udp", feature = "socket-tcp", feature = "socket-icmp", - feature = "socket-dhcp", + feature = "socket-dhcpv4", feature = "socket-dns", )) ))] From 7a0d4827f13b3a461e3a476f8e2b731620c4475d Mon Sep 17 00:00:00 2001 From: Ed Barnard <1059683+ebarnard@users.noreply.github.com> Date: Wed, 31 Aug 2022 16:08:23 +0000 Subject: [PATCH 397/566] Make constants exposed in DNS socket API public `MAX_ADDRESS_COUNT` is useful when naming the output type of `dns::Socket::get_query_result`. `MAX_SERVER_COUNT` is necessary to ensure `dns::Socket::new` and `dns::Socket::update_servers` do not panic if passing a user-provided list of DNS servers. --- src/socket/dns.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index af4a77755..f06d49e6f 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -12,10 +12,11 @@ use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; #[cfg(feature = "async")] use super::WakerRegistration; +pub const MAX_ADDRESS_COUNT: usize = 4; +pub const MAX_SERVER_COUNT: usize = 4; + const DNS_PORT: u16 = 53; const MAX_NAME_LEN: usize = 255; -const MAX_ADDRESS_COUNT: usize = 4; -const MAX_SERVER_COUNT: usize = 4; const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs From f7bbb57b79b4a5994d043ed234558a8fc04d59eb Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Thu, 1 Sep 2022 13:38:32 +0300 Subject: [PATCH 398/566] Made IPv4 and IPv6 modules a bit DRYer by moving sizes into constants and exposed IPv4-mapping prefix. --- src/wire/ipv4.rs | 17 ++++++++++----- src/wire/ipv6.rs | 57 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 0b0d0e22a..a2a46a1f1 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -21,6 +21,11 @@ pub use super::IpProtocol as Protocol; // accept a packet of the following size. pub const MIN_MTU: usize = 576; +/// Size of IPv4 adderess in octets. +/// +/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc791#section-3.2 +pub const ADDR_SIZE: usize = 4; + #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] pub struct Key { id: u16, @@ -30,14 +35,14 @@ pub struct Key { /// A four-octet IPv4 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] -pub struct Address(pub [u8; 4]); +pub struct Address(pub [u8; ADDR_SIZE]); impl Address { /// An unspecified address. - pub const UNSPECIFIED: Address = Address([0x00; 4]); + pub const UNSPECIFIED: Address = Address([0x00; ADDR_SIZE]); /// The broadcast address. - pub const BROADCAST: Address = Address([0xff; 4]); + pub const BROADCAST: Address = Address([0xff; ADDR_SIZE]); /// All multicast-capable nodes pub const MULTICAST_ALL_SYSTEMS: Address = Address([224, 0, 0, 1]); @@ -55,7 +60,7 @@ impl Address { /// # Panics /// The function panics if `data` is not four octets long. pub fn from_bytes(data: &[u8]) -> Address { - let mut bytes = [0; 4]; + let mut bytes = [0; ADDR_SIZE]; bytes.copy_from_slice(data); Address(bytes) } @@ -72,7 +77,7 @@ impl Address { /// Query whether the address is the broadcast address. pub fn is_broadcast(&self) -> bool { - self.0[0..4] == [255; 4] + self.0[0..4] == [255; ADDR_SIZE] } /// Query whether the address is a multicast address. @@ -871,7 +876,7 @@ mod test { 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff, ]; - static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; + static REPR_PAYLOAD_BYTES: [u8; ADDR_SIZE] = [0xaa, 0x00, 0x00, 0xff]; fn packet_repr() -> Repr { Repr { diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 037977290..8a5586724 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -15,16 +15,26 @@ pub use super::IpProtocol as Protocol; /// [RFC 8200 § 5]: https://tools.ietf.org/html/rfc8200#section-5 pub const MIN_MTU: usize = 1280; +/// Size of IPv6 adderess in octets. +/// +/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2 +pub const ADDR_SIZE: usize = 16; + +/// Size of IPv4-mapping prefix in octets. +/// +/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2 +pub const IPV4_MAPPED_PREFIX_SIZE: usize = ADDR_SIZE - 4; // 4 == ipv4::ADDR_SIZE , cannot DRY here because of dependency on a IPv4 module which is behind the feature + /// A sixteen-octet IPv6 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Address(pub [u8; 16]); +pub struct Address(pub [u8; ADDR_SIZE]); impl Address { /// The [unspecified address]. /// /// [unspecified address]: https://tools.ietf.org/html/rfc4291#section-2.5.2 - pub const UNSPECIFIED: Address = Address([0x00; 16]); + pub const UNSPECIFIED: Address = Address([0x00; ADDR_SIZE]); /// The link-local [all nodes multicast address]. /// @@ -50,18 +60,24 @@ impl Address { 0x01, ]); + /// The prefix used in [IPv4-mapped addresses]. + /// + /// [IPv4-mapped addresses]: https://www.rfc-editor.org/rfc/rfc4291#section-2.5.5.2 + pub const IPV4_MAPPED_PREFIX: [u8; IPV4_MAPPED_PREFIX_SIZE] = + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]; + /// Construct an IPv6 address from parts. #[allow(clippy::too_many_arguments)] pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { - let mut addr = [0u8; 16]; - NetworkEndian::write_u16(&mut addr[0..2], a0); + let mut addr = [0u8; ADDR_SIZE]; + NetworkEndian::write_u16(&mut addr[..2], a0); NetworkEndian::write_u16(&mut addr[2..4], a1); NetworkEndian::write_u16(&mut addr[4..6], a2); NetworkEndian::write_u16(&mut addr[6..8], a3); NetworkEndian::write_u16(&mut addr[8..10], a4); NetworkEndian::write_u16(&mut addr[10..12], a5); NetworkEndian::write_u16(&mut addr[12..14], a6); - NetworkEndian::write_u16(&mut addr[14..16], a7); + NetworkEndian::write_u16(&mut addr[14..], a7); Address(addr) } @@ -70,7 +86,7 @@ impl Address { /// # Panics /// The function panics if `data` is not sixteen octets long. pub fn from_bytes(data: &[u8]) -> Address { - let mut bytes = [0; 16]; + let mut bytes = [0; ADDR_SIZE]; bytes.copy_from_slice(data); Address(bytes) } @@ -81,7 +97,7 @@ impl Address { /// The function panics if `data` is not 8 words long. pub fn from_parts(data: &[u16]) -> Address { assert!(data.len() >= 8); - let mut bytes = [0; 16]; + let mut bytes = [0; ADDR_SIZE]; for (word_idx, chunk) in bytes.chunks_mut(2).enumerate() { NetworkEndian::write_u16(chunk, data[word_idx]); } @@ -122,7 +138,7 @@ impl Address { /// /// [unspecified address]: https://tools.ietf.org/html/rfc4291#section-2.5.2 pub fn is_unspecified(&self) -> bool { - self.0 == [0x00; 16] + self.0 == [0x00; ADDR_SIZE] } /// Query whether the IPv6 address is in the [link-local] scope. @@ -143,15 +159,15 @@ impl Address { /// /// [IPv4 mapped IPv6 address]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 pub fn is_ipv4_mapped(&self) -> bool { - self.0[0..12] == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff] + self.0[..IPV4_MAPPED_PREFIX_SIZE] == Self::IPV4_MAPPED_PREFIX } #[cfg(feature = "proto-ipv4")] /// Convert an IPv4 mapped IPv6 address to an IPv4 address. pub fn as_ipv4(&self) -> Option { if self.is_ipv4_mapped() { - Some(ipv4::Address::new( - self.0[12], self.0[13], self.0[14], self.0[15], + Some(ipv4::Address::from_bytes( + &self.0[IPV4_MAPPED_PREFIX_SIZE..], )) } else { None @@ -162,14 +178,14 @@ impl Address { /// /// # Panics /// This function panics if `mask` is greater than 128. - pub(super) fn mask(&self, mask: u8) -> [u8; 16] { + pub(super) fn mask(&self, mask: u8) -> [u8; ADDR_SIZE] { assert!(mask <= 128); - let mut bytes = [0u8; 16]; + let mut bytes = [0u8; ADDR_SIZE]; let idx = (mask as usize) / 8; let modulus = (mask as usize) % 8; let (first, second) = self.0.split_at(idx); bytes[0..idx].copy_from_slice(first); - if idx < 16 { + if idx < ADDR_SIZE { let part = second[0]; bytes[idx] = part & (!(0xff >> modulus) as u8); } @@ -217,7 +233,10 @@ impl fmt::Display for Address { return write!( f, "::ffff:{}.{}.{}.{}", - self.0[12], self.0[13], self.0[14], self.0[15] + self.0[IPV4_MAPPED_PREFIX_SIZE + 0], + self.0[IPV4_MAPPED_PREFIX_SIZE + 1], + self.0[IPV4_MAPPED_PREFIX_SIZE + 2], + self.0[IPV4_MAPPED_PREFIX_SIZE + 3] ); } @@ -273,10 +292,10 @@ impl fmt::Display for Address { /// Convert the given IPv4 address into a IPv4-mapped IPv6 address impl From for Address { fn from(address: ipv4::Address) -> Self { - let octets = address.0; - Address([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3], - ]) + let mut b = [0_u8; ADDR_SIZE]; + b[..Self::IPV4_MAPPED_PREFIX.len()].copy_from_slice(&Self::IPV4_MAPPED_PREFIX); + b[Self::IPV4_MAPPED_PREFIX.len()..].copy_from_slice(&address.0); + Self(b) } } From 03e830b886ceecf03e5aec51f9dcb0f43dd80eed Mon Sep 17 00:00:00 2001 From: Johannes Draaijer Date: Tue, 6 Sep 2022 09:28:55 +0200 Subject: [PATCH 399/566] DHCPv4: use a Vec to store DNS server addresses instead of an array of options --- examples/dhcp_client.rs | 4 +--- src/socket/dhcpv4.rs | 44 +++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 99d2baae5..9c2851982 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -78,9 +78,7 @@ fn main() { } for (i, s) in config.dns_servers.iter().enumerate() { - if let Some(s) = s { - debug!("DNS server {}: {}", i, s); - } + debug!("DNS server {}: {}", i, s); } } Some(dhcpv4::Event::Deconfigured) => { diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index ba50b0400..5a7b6d364 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -9,6 +9,7 @@ use crate::wire::{ UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN, }; use crate::wire::{DhcpOption, HardwareAddress}; +use heapless::Vec; #[cfg(feature = "async")] use super::WakerRegistration; @@ -24,7 +25,7 @@ const DEFAULT_PARAMETER_REQUEST_LIST: &[u8] = &[ ]; /// IPv4 configuration data provided by the DHCP server. -#[derive(Debug, Eq, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config<'a> { /// Information on how to reach the DHCP server that responded with DHCP @@ -36,7 +37,7 @@ pub struct Config<'a> { /// match the DHCP server's address. pub router: Option, /// DNS servers - pub dns_servers: [Option; DHCP_MAX_DNS_SERVER_COUNT], + pub dns_servers: Vec, /// Received DHCP packet pub packet: Option>, } @@ -417,17 +418,20 @@ impl<'a> Socket<'a> { // Cleanup the DNS servers list, keeping only unicasts/ // TP-Link TD-W8970 sends 0.0.0.0 as second DNS server if there's only one configured :( - let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT]; - if let Some(received) = dhcp_repr.dns_servers { - let mut i = 0; - for addr in received.iter().flatten() { - if addr.is_unicast() { - // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT - dns_servers[i] = Some(*addr); - i += 1; - } - } - } + let mut dns_servers = Vec::new(); + + dhcp_repr + .dns_servers + .iter() + .flatten() + .flatten() + .filter(|s| s.is_unicast()) + .for_each(|a| { + // This will never produce an error, as both the arrays and `dns_servers` + // have length DHCP_MAX_DNS_SERVER_COUNT + dns_servers.push(*a).ok(); + }); + let config = Config { server, address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), @@ -627,7 +631,7 @@ impl<'a> Socket<'a> { server: state.config.server, address: state.config.address, router: state.config.router, - dns_servers: state.config.dns_servers, + dns_servers: state.config.dns_servers.clone(), packet: self .receive_packet_buffer .as_deref() @@ -775,8 +779,10 @@ mod test { const DNS_IP_1: Ipv4Address = Ipv4Address([1, 1, 1, 1]); const DNS_IP_2: Ipv4Address = Ipv4Address([1, 1, 1, 2]); const DNS_IP_3: Ipv4Address = Ipv4Address([1, 1, 1, 3]); - const DNS_IPS: [Option; DHCP_MAX_DNS_SERVER_COUNT] = + const DNS_IPS_ARR: [Option; DHCP_MAX_DNS_SERVER_COUNT] = [Some(DNS_IP_1), Some(DNS_IP_2), Some(DNS_IP_3)]; + const DNS_IPS: &[Ipv4Address] = &[DNS_IP_1, DNS_IP_2, DNS_IP_3]; + const MASK_24: Ipv4Address = Ipv4Address([255, 255, 255, 0]); const MY_MAC: EthernetAddress = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); @@ -860,7 +866,7 @@ mod test { your_ip: MY_IP, router: Some(SERVER_IP), subnet_mask: Some(MASK_24), - dns_servers: Some(DNS_IPS), + dns_servers: Some(DNS_IPS_ARR), lease_duration: Some(1000), ..DHCP_DEFAULT @@ -885,7 +891,7 @@ mod test { your_ip: MY_IP, router: Some(SERVER_IP), subnet_mask: Some(MASK_24), - dns_servers: Some(DNS_IPS), + dns_servers: Some(DNS_IPS_ARR), lease_duration: Some(1000), ..DHCP_DEFAULT @@ -931,7 +937,7 @@ mod test { identifier: SERVER_IP, }, address: Ipv4Cidr::new(MY_IP, 24), - dns_servers: DNS_IPS, + dns_servers: Vec::from_slice(DNS_IPS).unwrap(), router: Some(SERVER_IP), packet: None, }, @@ -962,7 +968,7 @@ mod test { identifier: SERVER_IP, }, address: Ipv4Cidr::new(MY_IP, 24), - dns_servers: DNS_IPS, + dns_servers: Vec::from_slice(DNS_IPS).unwrap(), router: Some(SERVER_IP), packet: None, })) From ca4a98acc690f4dfb088c49a941df7a5c1776dea Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Tue, 6 Sep 2022 21:40:04 +0200 Subject: [PATCH 400/566] Make DhcpRepr use an Option> --- src/socket/dhcpv4.rs | 71 ++++++++++++++++++++++---------------------- src/wire/dhcpv4.rs | 65 +++++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 63 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 5a7b6d364..06d2b313d 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -424,7 +424,6 @@ impl<'a> Socket<'a> { .dns_servers .iter() .flatten() - .flatten() .filter(|s| s.is_unicast()) .for_each(|a| { // This will never produce an error, as both the arrays and `dns_servers` @@ -779,8 +778,6 @@ mod test { const DNS_IP_1: Ipv4Address = Ipv4Address([1, 1, 1, 1]); const DNS_IP_2: Ipv4Address = Ipv4Address([1, 1, 1, 2]); const DNS_IP_3: Ipv4Address = Ipv4Address([1, 1, 1, 3]); - const DNS_IPS_ARR: [Option; DHCP_MAX_DNS_SERVER_COUNT] = - [Some(DNS_IP_1), Some(DNS_IP_2), Some(DNS_IP_3)]; const DNS_IPS: &[Ipv4Address] = &[DNS_IP_1, DNS_IP_2, DNS_IP_3]; const MASK_24: Ipv4Address = Ipv4Address([255, 255, 255, 0]); @@ -858,19 +855,21 @@ mod test { ..DHCP_DEFAULT }; - const DHCP_OFFER: DhcpRepr = DhcpRepr { - message_type: DhcpMessageType::Offer, - server_ip: SERVER_IP, - server_identifier: Some(SERVER_IP), + fn dhcp_offer() -> DhcpRepr<'static> { + DhcpRepr { + message_type: DhcpMessageType::Offer, + server_ip: SERVER_IP, + server_identifier: Some(SERVER_IP), - your_ip: MY_IP, - router: Some(SERVER_IP), - subnet_mask: Some(MASK_24), - dns_servers: Some(DNS_IPS_ARR), - lease_duration: Some(1000), + your_ip: MY_IP, + router: Some(SERVER_IP), + subnet_mask: Some(MASK_24), + dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()), + lease_duration: Some(1000), - ..DHCP_DEFAULT - }; + ..DHCP_DEFAULT + } + } const DHCP_REQUEST: DhcpRepr = DhcpRepr { message_type: DhcpMessageType::Request, @@ -883,19 +882,21 @@ mod test { ..DHCP_DEFAULT }; - const DHCP_ACK: DhcpRepr = DhcpRepr { - message_type: DhcpMessageType::Ack, - server_ip: SERVER_IP, - server_identifier: Some(SERVER_IP), + fn dhcp_ack() -> DhcpRepr<'static> { + DhcpRepr { + message_type: DhcpMessageType::Ack, + server_ip: SERVER_IP, + server_identifier: Some(SERVER_IP), - your_ip: MY_IP, - router: Some(SERVER_IP), - subnet_mask: Some(MASK_24), - dns_servers: Some(DNS_IPS_ARR), - lease_duration: Some(1000), + your_ip: MY_IP, + router: Some(SERVER_IP), + subnet_mask: Some(MASK_24), + dns_servers: Some(Vec::from_slice(DNS_IPS).unwrap()), + lease_duration: Some(1000), - ..DHCP_DEFAULT - }; + ..DHCP_DEFAULT + } + } const DHCP_NAK: DhcpRepr = DhcpRepr { message_type: DhcpMessageType::Nak, @@ -954,11 +955,11 @@ mod test { recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); assert_eq!(s.poll(), None); - send!(s, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, (IP_RECV, UDP_RECV, dhcp_offer())); assert_eq!(s.poll(), None); recv!(s, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); assert_eq!(s.poll(), None); - send!(s, (IP_RECV, UDP_RECV, DHCP_ACK)); + send!(s, (IP_RECV, UDP_RECV, dhcp_ack())); assert_eq!( s.poll(), @@ -994,7 +995,7 @@ mod test { recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); // check after retransmits it still works - send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, time 20_000, (IP_RECV, UDP_RECV, dhcp_offer())); recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); } @@ -1003,7 +1004,7 @@ mod test { let mut s = socket(); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); - send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, time 0, (IP_RECV, UDP_RECV, dhcp_offer())); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); recv!(s, time 1_000, []); recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); @@ -1013,7 +1014,7 @@ mod test { recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); // check after retransmits it still works - send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + send!(s, time 20_000, (IP_RECV, UDP_RECV, dhcp_ack())); match &s.state { ClientState::Renewing(r) => { @@ -1029,7 +1030,7 @@ mod test { let mut s = socket(); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); - send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, time 0, (IP_RECV, UDP_RECV, dhcp_offer())); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); @@ -1041,7 +1042,7 @@ mod test { recv!(s, time 70_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); // check it still works - send!(s, time 60_000, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, time 60_000, (IP_RECV, UDP_RECV, dhcp_offer())); recv!(s, time 60_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); } @@ -1050,7 +1051,7 @@ mod test { let mut s = socket(); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); - send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER)); + send!(s, time 0, (IP_RECV, UDP_RECV, dhcp_offer())); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]); send!(s, time 0, (IP_SERVER_BROADCAST, UDP_RECV, DHCP_NAK)); recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); @@ -1074,7 +1075,7 @@ mod test { _ => panic!("Invalid state"), } - send!(s, time 500_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + send!(s, time 500_000, (IP_RECV, UDP_RECV, dhcp_ack())); assert_eq!(s.poll(), None); match &s.state { @@ -1099,7 +1100,7 @@ mod test { recv!(s, time 875_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); // check it still works - send!(s, time 875_000, (IP_RECV, UDP_RECV, DHCP_ACK)); + send!(s, time 875_000, (IP_RECV, UDP_RECV, dhcp_ack())); match &s.state { ClientState::Renewing(r) => { // NOW the expiration gets bumped diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 65059adbb..2f9018d8c 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -3,6 +3,7 @@ use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use core::iter; +use heapless::Vec; use super::{Error, Result}; use crate::wire::arp::Hardware; @@ -582,7 +583,7 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> { /// length) is set to `6`. /// /// The `options` field has a variable length. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this @@ -645,7 +646,7 @@ pub struct Repr<'a> { /// the client is interested in. pub parameter_request_list: Option<&'a [u8]>, /// DNS servers - pub dns_servers: Option<[Option; MAX_DNS_SERVER_COUNT]>, + pub dns_servers: Option>, /// The maximum size dhcp packet the interface can receive pub max_size: Option, /// The DHCP IP lease duration, specified in seconds. @@ -683,9 +684,9 @@ impl<'a> Repr<'a> { if self.lease_duration.is_some() { len += 6; } - if let Some(dns_servers) = self.dns_servers { + if let Some(dns_servers) = &self.dns_servers { len += 2; - len += dns_servers.iter().flatten().count() * core::mem::size_of::(); + len += dns_servers.iter().count() * core::mem::size_of::(); } if let Some(list) = self.parameter_request_list { len += list.len() + 2; @@ -773,13 +774,13 @@ impl<'a> Repr<'a> { parameter_request_list = Some(data); } (field::OPT_DOMAIN_NAME_SERVER, _) => { - let mut servers = [None; MAX_DNS_SERVER_COUNT]; - let chunk_size = 4; - for (server, chunk) in servers.iter_mut().zip(data.chunks(chunk_size)) { - if chunk.len() != chunk_size { - return Err(Error); - } - *server = Some(Ipv4Address::from_bytes(chunk)); + let mut servers = Vec::new(); + const IP_ADDR_BYTE_LEN: usize = 4; + for chunk in data.chunks(IP_ADDR_BYTE_LEN) { + // We ignore push failures because that will only happen + // if we attempt to push more than 4 addresses, and the only + // solution to that is to support more addresses. + servers.push(Ipv4Address::from_bytes(chunk)).ok(); } dns_servers = Some(servers); } @@ -901,13 +902,12 @@ impl<'a> Repr<'a> { })?; } - if let Some(dns_servers) = self.dns_servers { + if let Some(dns_servers) = &self.dns_servers { const IP_SIZE: usize = core::mem::size_of::(); let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE]; let data_len = dns_servers .iter() - .flatten() .enumerate() .inspect(|(i, ip)| { servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes()); @@ -1210,11 +1210,14 @@ mod test { fn test_emit_offer_dns() { let repr = { let mut repr = offer_repr(); - repr.dns_servers = Some([ - Some(Ipv4Address([163, 1, 74, 6])), - Some(Ipv4Address([163, 1, 74, 7])), - Some(Ipv4Address([163, 1, 74, 3])), - ]); + repr.dns_servers = Some( + Vec::from_slice(&[ + Ipv4Address([163, 1, 74, 6]), + Ipv4Address([163, 1, 74, 7]), + Ipv4Address([163, 1, 74, 3]), + ]) + .unwrap(), + ); repr }; let mut bytes = vec![0xa5; repr.buffer_len()]; @@ -1226,11 +1229,14 @@ mod test { assert_eq!( repr_parsed.dns_servers, - Some([ - Some(Ipv4Address([163, 1, 74, 6])), - Some(Ipv4Address([163, 1, 74, 7])), - Some(Ipv4Address([163, 1, 74, 3])) - ]) + Some( + Vec::from_slice(&[ + Ipv4Address([163, 1, 74, 6]), + Ipv4Address([163, 1, 74, 7]), + Ipv4Address([163, 1, 74, 3]), + ]) + .unwrap() + ) ); } @@ -1263,11 +1269,14 @@ mod test { // length-3 array (see issue #305) assert_eq!( repr.dns_servers, - Some([ - Some(Ipv4Address([163, 1, 74, 6])), - Some(Ipv4Address([163, 1, 74, 7])), - Some(Ipv4Address([163, 1, 74, 3])) - ]) + Some( + Vec::from_slice(&[ + Ipv4Address([163, 1, 74, 6]), + Ipv4Address([163, 1, 74, 7]), + Ipv4Address([163, 1, 74, 3]) + ]) + .unwrap() + ) ); } From db87f0f60355c8c63660d1ac28ceeeaf08737a9b Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Tue, 6 Sep 2022 22:00:59 +0200 Subject: [PATCH 401/566] Activate heapless/defmt if defmt is activated --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index e4ff78b2d..60c785837 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ url = "2.0" std = ["managed/std", "defmt?/alloc"] alloc = ["managed/alloc", "defmt?/alloc"] verbose = [] +defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] "medium-ieee802154" = ["socket", "proto-sixlowpan"] From 2a302ff215d5de360db6d8db955adac3e0920501 Mon Sep 17 00:00:00 2001 From: Klim Tsoutsman Date: Wed, 21 Sep 2022 10:46:49 +1000 Subject: [PATCH 402/566] Remove implicit sized bound on device generics The generic is only ever accessed through a reference and so it doesn't have to be sized. Signed-off-by: Klim Tsoutsman --- src/iface/interface.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index f5128da83..fc182a2e0 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -429,7 +429,7 @@ let iface = builder.finalize(&mut device); /// [neighbor_cache]: #method.neighbor_cache pub fn finalize(self, device: &mut D) -> Interface<'a> where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { let caps = device.capabilities(); @@ -764,7 +764,7 @@ impl<'a> Interface<'a> { timestamp: Instant, ) -> Result where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { self.inner.now = timestamp; @@ -806,7 +806,7 @@ impl<'a> Interface<'a> { timestamp: Instant, ) -> Result where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { self.inner.now = timestamp; @@ -906,7 +906,7 @@ impl<'a> Interface<'a> { sockets: &mut SocketSet<'_>, ) -> Result where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { self.inner.now = timestamp; @@ -1006,7 +1006,7 @@ impl<'a> Interface<'a> { fn socket_ingress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { let mut processed_any = false; let Self { @@ -1060,7 +1060,7 @@ impl<'a> Interface<'a> { fn socket_egress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { let Self { inner, @@ -1172,7 +1172,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-igmp")] fn igmp_egress(&mut self, device: &mut D) -> Result where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { match self.inner.igmp_report_state { IgmpReportState::ToSpecificQuery { @@ -1233,7 +1233,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] fn sixlowpan_egress(&mut self, device: &mut D) -> Result where - D: for<'d> Device<'d>, + D: for<'d> Device<'d> + ?Sized, { let SixlowpanOutPacket { packet_len, From 297d90b0ff1c7f3ad8f7ecc99305dd2acef0bff5 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 17 Jun 2022 17:12:06 +0200 Subject: [PATCH 403/566] IPv4 fragmentation (outgoing) --- examples/client.rs | 10 +- examples/server.rs | 58 ++-- src/iface/interface.rs | 600 ++++++++++++++++++++++++++++++++++------- src/socket/raw.rs | 2 +- src/socket/tcp.rs | 4 +- src/wire/icmpv4.rs | 4 +- src/wire/ip.rs | 6 +- src/wire/ipv4.rs | 2 + src/wire/mod.rs | 2 +- 9 files changed, 554 insertions(+), 134 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 3fe69bdbd..8c7cd4aba 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -51,20 +51,24 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); + #[cfg(feature = "proto-ipv4-fragmentation")] + let mut ipv4_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-ipv4-fragmentation")] { let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); - builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut out_packet_buffer = [0u8; 1280]; + let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); builder = builder .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { diff --git a/examples/server.rs b/examples/server.rs index 07b18c404..7f003dc57 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -4,7 +4,6 @@ use log::debug; use std::collections::BTreeMap; use std::fmt::Write; use std::os::unix::io::AsRawFd; -use std::str; #[cfg(any( feature = "proto-sixlowpan-fragmentation", @@ -32,8 +31,14 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); + let udp_rx_buffer = udp::PacketBuffer::new( + vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], + vec![0; 65535], + ); + let udp_tx_buffer = udp::PacketBuffer::new( + vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], + vec![0; 65535], + ); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); @@ -62,20 +67,31 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); + builder = builder.random_seed( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let mut ipv4_out_packet_cache = [0u8; 10_000]; #[cfg(feature = "proto-ipv4-fragmentation")] { let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); - builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut out_packet_buffer = [0u8; 1280]; + let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); builder = builder .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { @@ -110,22 +126,16 @@ fn main() { let client = match socket.recv() { Ok((data, endpoint)) => { - debug!( - "udp:6969 recv data: {:?} from {}", - str::from_utf8(data).unwrap(), - endpoint - ); - Some(endpoint) + debug!("udp:6969 recv data: {:?} from {}", data, endpoint); + let mut data = data.to_vec(); + data.reverse(); + Some((endpoint, data)) } Err(_) => None, }; - if let Some(endpoint) = client { - let data = b"hello\n"; - debug!( - "udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap() - ); - socket.send_slice(data, endpoint).unwrap(); + if let Some((endpoint, data)) = client { + debug!("udp:6969 send data: {:?} to {}", data, endpoint,); + socket.send_slice(&data, endpoint).unwrap(); } // tcp:6969: respond "hello" @@ -160,10 +170,7 @@ fn main() { let recvd_len = buffer.len(); let mut data = buffer.to_owned(); if !data.is_empty() { - debug!( - "tcp:6970 recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); + debug!("tcp:6970 recv data: {:?}", data); data = data.split(|&b| b == b'\n').collect::>().concat(); data.reverse(); data.extend(b"\n"); @@ -172,10 +179,7 @@ fn main() { }) .unwrap(); if socket.can_send() && !data.is_empty() { - debug!( - "tcp:6970 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); + debug!("tcp:6970 send data: {:?}", data); socket.send_slice(&data[..]).unwrap(); } } else if socket.may_send() { diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ba5e2e6a6..8e103d08c 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -37,6 +37,8 @@ pub(crate) struct FragmentsBuffer<'a> { } pub(crate) struct OutPackets<'a> { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket<'a>, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_packet: SixlowpanOutPacket<'a>, @@ -45,10 +47,90 @@ pub(crate) struct OutPackets<'a> { } impl<'a> OutPackets<'a> { - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] /// Returns `true` when all the data of the outgoing buffers are transmitted. fn all_transmitted(&self) -> bool { - self.sixlowpan_out_packet.finished() || self.sixlowpan_out_packet.is_empty() + #[cfg(feature = "proto-ipv4-fragmentation")] + if !self.ipv4_out_packet.is_empty() { + return false; + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if !self.sixlowpan_out_packet.is_empty() { + return false; + } + + true + } +} + +#[allow(unused)] +#[cfg(feature = "proto-ipv4")] +pub(crate) struct Ipv4OutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The IPv4 representation. + repr: Ipv4Repr, + /// The destination hardware address. + dst_hardware_addr: EthernetAddress, + /// The offset of the next fragment. + frag_offset: u16, + /// The identifier of the stream. + ident: u16, +} + +#[cfg(feature = "proto-ipv4-fragmentation")] +impl<'a> Ipv4OutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + sent_bytes: 0, + repr: Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }, + dst_hardware_addr: EthernetAddress::default(), + frag_offset: 0, + ident: 0, + } + } + + /// Return `true` when everything is transmitted. + #[inline] + fn finished(&self) -> bool { + self.packet_len == self.sent_bytes + } + + /// Returns `true` when there is nothing to transmit. + #[inline] + fn is_empty(&self) -> bool { + self.packet_len == 0 + } + + // Reset the buffer. + fn reset(&mut self) { + self.packet_len = 0; + self.sent_bytes = 0; + self.repr = Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }; + self.dst_hardware_addr = EthernetAddress::default(); } } @@ -163,6 +245,8 @@ pub struct InterfaceInner<'a> { sequence_no: u8, #[cfg(feature = "medium-ieee802154")] pan_id: Option, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: u16, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, @@ -195,14 +279,16 @@ pub struct InterfaceBuilder<'a> { random_seed: u64, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: Option>, + ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice<'a, u8>, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: Option>, + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: Option>, + sixlowpan_out_buffer: ManagedSlice<'a, u8>, } impl<'a> InterfaceBuilder<'a> { @@ -238,7 +324,9 @@ let builder = InterfaceBuilder::new() .ip_addrs(ip_addrs); # #[cfg(feature = "proto-ipv4-fragmentation")] -let builder = builder.ipv4_fragments_cache(ipv4_frag_cache); +let builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(vec![]); let iface = builder.finalize(&mut device); ``` @@ -264,14 +352,16 @@ let iface = builder.finalize(&mut device); random_seed: 0, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: None, + ipv4_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: None, + sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: None, + sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), } } @@ -385,7 +475,16 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_fragments_cache(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { - self.ipv4_fragments = Some(storage); + self.ipv4_fragments = storage; + self + } + + #[cfg(feature = "proto-ipv4-fragmentation")] + pub fn ipv4_out_packet_cache(mut self, storage: T) -> Self + where + T: Into>, + { + self.ipv4_out_buffer = storage.into(); self } @@ -394,7 +493,7 @@ let iface = builder.finalize(&mut device); mut self, storage: PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Self { - self.sixlowpan_fragments = Some(storage); + self.sixlowpan_fragments = storage; self } @@ -412,7 +511,7 @@ let iface = builder.finalize(&mut device); where T: Into>, { - self.sixlowpan_out_buffer = Some(storage.into()); + self.sixlowpan_out_buffer = storage.into(); self } @@ -471,10 +570,7 @@ let iface = builder.finalize(&mut device); ), }; - #[cfg(feature = "medium-ieee802154")] let mut rand = Rand::new(self.random_seed); - #[cfg(not(feature = "medium-ieee802154"))] - let rand = Rand::new(self.random_seed); #[cfg(feature = "medium-ieee802154")] let mut sequence_no; @@ -491,22 +587,29 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan")] loop { - tag = (rand.rand_u32() & 0xffff) as u16; + tag = rand.rand_u16(); if tag != 0 { break; } } + #[cfg(feature = "proto-ipv4")] + let mut ipv4_id; + + #[cfg(feature = "proto-ipv4")] + loop { + ipv4_id = rand.rand_u16(); + if ipv4_id != 0 { + break; + } + } + Interface { fragments: FragmentsBuffer { #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: self - .ipv4_fragments - .expect("Cache for incoming IPv4 fragments is required"), + ipv4_fragments: self.ipv4_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: self - .sixlowpan_fragments - .expect("Cache for incoming 6LoWPAN fragments is required"), + sixlowpan_fragments: self.sixlowpan_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: self.sixlowpan_fragments_cache_timeout, @@ -517,11 +620,10 @@ let iface = builder.finalize(&mut device); _lifetime: core::marker::PhantomData, }, out_packets: OutPackets { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket::new(self.ipv4_out_buffer), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new( - self.sixlowpan_out_buffer - .expect("Cache for outgoing 6LoWPAN fragments is required"), - ), + sixlowpan_out_packet: SixlowpanOutPacket::new(self.sixlowpan_out_buffer), #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] _lifetime: core::marker::PhantomData, @@ -547,6 +649,8 @@ let iface = builder.finalize(&mut device); pan_id: self.pan_id, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id, rand, }, } @@ -603,7 +707,7 @@ impl<'a> IpPacket<'a> { pub(crate) fn emit_payload( &self, - _ip_repr: IpRepr, + _ip_repr: &IpRepr, payload: &mut [u8], caps: &DeviceCapabilities, ) { @@ -645,7 +749,7 @@ impl<'a> IpPacket<'a> { // I'm really not happy about this "solution" but I don't know what else to do. if let Some(max_burst_size) = caps.max_burst_size { let mut max_segment_size = caps.max_transmission_unit; - max_segment_size -= _ip_repr.buffer_len(); + max_segment_size -= _ip_repr.header_len(); max_segment_size -= tcp_repr.header_len(); let max_window_size = max_burst_size * max_segment_size; @@ -928,6 +1032,13 @@ impl<'a> Interface<'a> { return Err(e); } + #[cfg(feature = "proto-ipv4-fragmentation")] + match self.ipv4_egress(device) { + Ok(true) => return Ok(true), + Err(e) => return Err(e), + _ => (), + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] match self.sixlowpan_egress(device) { Ok(true) => return Ok(true), @@ -1021,7 +1132,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch(tx_token, packet) { + if let Err(err) = inner.dispatch(tx_token, packet, Some(_out_packets)) { net_debug!("Failed to send response: {}", err); } } @@ -1029,7 +1140,9 @@ impl<'a> Interface<'a> { #[cfg(feature = "medium-ip")] Medium::Ip => { if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch_ip(tx_token, packet, None) { + if let Err(err) = + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) + { net_debug!("Failed to send response: {}", err); } } @@ -1083,13 +1196,19 @@ impl<'a> Interface<'a> { neighbor_addr = Some(response.ip_repr().dst_addr()); match device.transmit().ok_or(Error::Exhausted) { Ok(_t) => { - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] if let Err(e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { net_debug!("failed to dispatch IP: {}", e); return Err(e); } - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] if let Err(e) = inner.dispatch_ip(_t, response, None) { net_debug!("failed to dispatch IP: {}", e); return Err(e); @@ -1230,11 +1349,60 @@ impl<'a> Interface<'a> { } } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn ipv4_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + // Reset the buffer when we transmitted everything. + if self.out_packets.ipv4_out_packet.finished() { + self.out_packets.ipv4_out_packet.reset(); + } + + if self.out_packets.ipv4_out_packet.is_empty() { + return Ok(false); + } + + let Ipv4OutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.ipv4_out_packet; + + if *packet_len > *sent_bytes { + match device.transmit().ok_or(Error::Exhausted) { + Ok(tx_token) => { + if let Err(e) = self + .inner + .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet) + { + net_debug!("failed to transmit: {}", e); + } + } + Err(e) => { + net_debug!("failed to transmit: {}", e); + } + } + Ok(true) + } else { + Ok(false) + } + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] fn sixlowpan_egress(&mut self, device: &mut D) -> Result where D: for<'d> Device<'d> + ?Sized, { + // Reset the buffer when we transmitted everything. + if self.out_packets.sixlowpan_out_packet.finished() { + self.out_packets.sixlowpan_out_packet.reset(); + } + + if self.out_packets.sixlowpan_out_packet.is_empty() { + return Ok(false); + } + let SixlowpanOutPacket { packet_len, sent_bytes, @@ -1254,11 +1422,6 @@ impl<'a> Interface<'a> { ) { net_debug!("failed to transmit: {}", e); } - - // Reset the buffer when we transmitted everything. - if self.out_packets.sixlowpan_out_packet.finished() { - self.out_packets.sixlowpan_out_packet.reset(); - } } Err(e) => { net_debug!("failed to transmit: {}", e); @@ -1388,6 +1551,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: 1, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: 1, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), @@ -1430,6 +1596,13 @@ impl<'a> InterfaceInner<'a> { no } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn get_ipv4_ident(&mut self) -> u16 { + let ipv4_id = self.ipv4_id; + self.ipv4_id = self.ipv4_id.wrapping_add(1); + ipv4_id + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] fn get_sixlowpan_fragment_tag(&mut self) -> u16 { let tag = self.tag; @@ -2031,11 +2204,21 @@ impl<'a> InterfaceInner<'a> { let f = match fragments.get_packet_assembler_mut(&key) { Ok(f) => f, Err(_) => { - check!(check!(fragments.reserve_with_key(&key)).start( + let p = match fragments.reserve_with_key(&key) { + Ok(p) => p, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(p.start( None, self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), - 0, + 0 )); + check!(fragments.get_packet_assembler_mut(&key)) } }; @@ -2661,7 +2844,12 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "medium-ethernet")] - fn dispatch(&mut self, tx_token: Tx, packet: EthernetPacket) -> Result<()> + fn dispatch( + &mut self, + tx_token: Tx, + packet: EthernetPacket, + _out_packet: Option<&mut OutPackets<'_>>, + ) -> Result<()> where Tx: TxToken, { @@ -2683,7 +2871,7 @@ impl<'a> InterfaceInner<'a> { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, None), + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, _out_packet), } } @@ -2899,65 +3087,196 @@ impl<'a> InterfaceInner<'a> { packet: IpPacket, _out_packet: Option<&mut OutPackets<'_>>, ) -> Result<()> { - let ip_repr = packet.ip_repr(); + let mut ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); - match self.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - #[cfg(feature = "medium-ieee802154")] - (HardwareAddress::Ieee802154(_), _) => unreachable!(), - }; + // Dispatch IEEE802.15.4: - let caps = self.caps.clone(); - self.dispatch_ethernet(tx_token, ip_repr.total_len(), |mut frame| { - frame.set_dst_addr(dst_hardware_addr); - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), - } + #[cfg(feature = "medium-ieee802154")] + if matches!(self.caps.medium, Medium::Ieee802154) { + let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )? { + (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), + _ => unreachable!(), + }; - ip_repr.emit(frame.payload_mut(), &caps.checksum); + return self.dispatch_ieee802154( + dst_hardware_addr, + &ip_repr, + tx_token, + packet, + _out_packet, + ); + } - let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &caps); - }) - } - #[cfg(feature = "medium-ip")] - Medium::Ip => { - let tx_len = ip_repr.total_len(); - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - debug_assert!(tx_buffer.as_ref().len() == tx_len); + // Dispatch IP/Ethernet: - ip_repr.emit(&mut tx_buffer, &self.caps.checksum); + let caps = self.caps.clone(); - let payload = &mut tx_buffer[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &self.caps); + #[cfg(feature = "proto-ipv4-fragmentation")] + let ipv4_id = self.get_ipv4_ident(); - Ok(()) - }) + // First we calculate the total length that we will have to emit. + let mut total_len = ip_repr.buffer_len(); + + // Add the size of the Ethernet header if the medium is Ethernet. + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + total_len = EthernetFrame::<&[u8]>::buffer_len(total_len); + } + + // If the medium is Ethernet, then we need to retrieve the destination hardware address. + #[cfg(feature = "medium-ethernet")] + let (dst_hardware_addr, tx_token) = + match self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())? { + (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), + #[cfg(feature = "medium-ieee802154")] + (HardwareAddress::Ieee802154(_), _) => unreachable!(), + }; + + // Emit function for the Ethernet header. + #[cfg(feature = "medium-ethernet")] + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), - _ => unreachable!(), - }; - self.dispatch_ieee802154(dst_hardware_addr, &ip_repr, tx_token, packet, _out_packet) + Ok(()) + }; + + // Emit function for the IP header and payload. + let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| { + repr.emit(&mut tx_buffer, &self.caps.checksum); + + let payload = &mut tx_buffer[repr.header_len()..]; + packet.emit_payload(repr, payload, &caps); + }; + + let total_ip_len = ip_repr.buffer_len(); + + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(ref mut repr) => { + // If we have an IPv4 packet, then we need to check if we need to fragment it. + if total_ip_len > self.caps.max_transmission_unit { + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4-fragmentation")] { + net_debug!("start fragmentation"); + + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr: out_packet_repr, + frag_offset, + ident, + dst_hardware_addr: dst_address, + } = &mut _out_packet.unwrap().ipv4_out_packet; + + // Calculate how much we will send now (including the Ethernet header). + let tx_len = self.caps.max_transmission_unit; + + let ip_header_len = repr.buffer_len(); + let first_frag_ip_len = self.caps.ip_mtu(); + + if buffer.len() < first_frag_ip_len { + net_debug!("Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + + *dst_address = dst_hardware_addr; + + // Save the total packet len (without the Ethernet header, but with the first + // IP header). + *packet_len = total_ip_len; + + // Save the IP header for other fragments. + *out_packet_repr = *repr; + + // Save how much bytes we will send now. + *sent_bytes = first_frag_ip_len; + + // Modify the IP header + repr.payload_len = first_frag_ip_len - repr.buffer_len(); + + // Emit the IP header to the buffer. + emit_ip(&ip_repr, buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); + *ident = ipv4_id; + ipv4_packet.set_ident(ipv4_id); + ipv4_packet.set_more_frags(true); + ipv4_packet.set_dont_frag(false); + ipv4_packet.set_frag_offset(0); + + if caps.checksum.ipv4.tx() { + ipv4_packet.fill_checksum(); + } + + // Transmit the first packet. + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + // Change the offset for the next packet. + *frag_offset = (first_frag_ip_len - ip_header_len) as u16; + + // Copy the IP header and the payload. + tx_buffer[..first_frag_ip_len] + .copy_from_slice(&buffer[..first_frag_ip_len]); + + Ok(()) + }) + } else { + net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); + Ok(()) + } + } + } else { + // No fragmentation is required. + tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }) + } } + // We don't support IPv6 fragmentation yet. + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) => tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }), } } @@ -3076,6 +3395,11 @@ impl<'a> InterfaceInner<'a> { .. } = &mut _out_packet.unwrap().sixlowpan_out_packet; + if buffer.len() < total_size { + net_debug!("6LoWPAN: Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + *ll_dst_addr = ll_dst_a; *ll_src_addr = ll_src_a; @@ -3316,6 +3640,91 @@ impl<'a> InterfaceInner<'a> { ) } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn dispatch_ipv4_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut Ipv4OutPacket, + ) -> Result<()> { + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr, + dst_hardware_addr, + frag_offset, + ident, + .. + } = out_packet; + + let caps = self.caps.clone(); + + let mtu_max = self.ip_mtu(); + let ip_len = (*packet_len - *sent_bytes + repr.buffer_len()).min(mtu_max); + let payload_len = ip_len - repr.buffer_len(); + + let more_frags = (*packet_len - *sent_bytes) != payload_len; + repr.payload_len = payload_len; + *sent_bytes += payload_len; + + let mut tx_len = ip_len; + #[cfg(feature = "medium-ethernet")] + if matches!(caps.medium, Medium::Ethernet) { + tx_len += EthernetFrame::<&[u8]>::header_len(); + } + + // Emit function for the Ethernet header. + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(*dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), + } + + Ok(()) + }; + + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..repr.buffer_len()]); + repr.emit(&mut packet, &caps.checksum); + packet.set_ident(*ident); + packet.set_more_frags(more_frags); + packet.set_dont_frag(false); + packet.set_frag_offset(*frag_offset); + + if caps.checksum.ipv4.tx() { + packet.fill_checksum(); + } + + tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( + &buffer[*frag_offset as usize + repr.buffer_len() as usize..][..payload_len], + ); + + // Update the frag offset for the next fragment. + *frag_offset += payload_len as u16; + + Ok(()) + }) + } + #[cfg(feature = "proto-igmp")] fn igmp_report_packet<'any>( &self, @@ -3335,7 +3744,6 @@ impl<'a> InterfaceInner<'a> { next_header: IpProtocol::Igmp, payload_len: igmp_repr.buffer_len(), hop_limit: 1, - // TODO: add Router Alert IPv4 header option. See // [#183](https://github.com/m-labs/smoltcp/issues/183). }, igmp_repr, @@ -3408,8 +3816,9 @@ mod test { let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = - iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); + let iface_builder = iface_builder + .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_out_packet_cache(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -3442,8 +3851,9 @@ mod test { .sixlowpan_out_packet_cache(vec![]); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = - iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); + let iface_builder = iface_builder + .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_out_packet_cache(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -4124,7 +4534,7 @@ mod test { solicit.emit( &remote_ip_addr.into(), &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), &ChecksumCapabilities::default(), ); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 08c3ddfd1..f07a9596d 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -261,7 +261,7 @@ impl<'a> Socket<'a> { pub(crate) fn process(&mut self, cx: &mut Context, ip_repr: &IpRepr, payload: &[u8]) { debug_assert!(self.accepts(ip_repr)); - let header_len = ip_repr.buffer_len(); + let header_len = ip_repr.header_len(); let total_len = header_len + payload.len(); net_trace!( diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index a3d813770..6b05bce44 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2099,7 +2099,7 @@ impl<'a> Socket<'a> { // 3. MSS we can send, determined by our MTU. let size = win_limit .min(self.remote_mss) - .min(cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN); + .min(cx.ip_mtu() - ip_repr.header_len() - TCP_HEADER_LEN); let offset = self.remote_last_seq - self.local_seq_no; repr.payload = self.tx_buffer.get_allocated(offset, size); @@ -2161,7 +2161,7 @@ impl<'a> Socket<'a> { if repr.control == TcpControl::Syn { // Fill the MSS option. See RFC 6691 for an explanation of this calculation. - let max_segment_size = cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN; + let max_segment_size = cx.ip_mtu() - ip_repr.header_len() - TCP_HEADER_LEN; repr.max_seg_size = Some(max_segment_size as u16); } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index f1546294b..7d3cf5a4e 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -665,7 +665,7 @@ mod test { packet.set_echo_seq_no(0xabcd); packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]); packet.fill_checksum(); - assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); + assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } fn echo_packet_repr() -> Repr<'static> { @@ -689,7 +689,7 @@ mod test { let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &ChecksumCapabilities::default()); - assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); + assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/ip.rs b/src/wire/ip.rs index b4d6a2d18..519203a4f 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -666,7 +666,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub fn header_len(&self) -> usize { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.buffer_len(), @@ -693,8 +693,8 @@ impl Repr { /// high-level representation. /// /// This is the same as `repr.buffer_len() + repr.payload_len()`. - pub fn total_len(&self) -> usize { - self.buffer_len() + self.payload_len() + pub fn buffer_len(&self) -> usize { + self.header_len() + self.payload_len() } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 0b0d0e22a..2ca3e1aad 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -26,6 +26,7 @@ pub struct Key { id: u16, src_addr: Address, dst_addr: Address, + protocol: Protocol, } /// A four-octet IPv4 address. @@ -457,6 +458,7 @@ impl> Packet { id: self.ident(), src_addr: self.src_addr(), dst_addr: self.dst_addr(), + protocol: self.next_header(), } } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 65b5bb93f..72c145576 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -52,7 +52,7 @@ let repr = Ipv4Repr { dst_addr: Ipv4Address::new(10, 0, 0, 2), next_header: IpProtocol::Tcp, payload_len: 10, - hop_limit: 64 + hop_limit: 64, }; let mut buffer = vec![0; repr.buffer_len() + repr.payload_len]; { // emission From fe26f462dd439f1b24a88331a0483dd742023a2c Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 16 Aug 2022 11:16:49 +0200 Subject: [PATCH 404/566] Better names for buffers (#653) --- examples/client.rs | 14 +++++++------- examples/server.rs | 14 +++++++------- examples/sixlowpan.rs | 12 ++++++------ examples/sixlowpan_benchmark.rs | 8 ++++---- src/iface/interface.rs | 34 ++++++++++++++++----------------- src/iface/mod.rs | 2 +- src/wire/sixlowpan.rs | 4 ++-- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 8c7cd4aba..381243cd5 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -9,7 +9,7 @@ use std::str::{self, FromStr}; feature = "proto-sixlowpan-fragmentation", feature = "proto-ipv4-fragmentation" ))] -use smoltcp::iface::FragmentsCache; +use smoltcp::iface::ReassemblyBuffer; use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; @@ -55,20 +55,20 @@ fn main() { let mut ipv4_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-ipv4-fragmentation")] { - let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); builder = builder - .ipv4_fragments_cache(ipv4_frag_cache) - .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); + .ipv4_reassembly_buffer(ipv4_frag_cache) + .ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { - let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); builder = builder - .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); + .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) + .sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { diff --git a/examples/server.rs b/examples/server.rs index 7f003dc57..3a1b623d9 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -9,7 +9,7 @@ use std::os::unix::io::AsRawFd; feature = "proto-sixlowpan-fragmentation", feature = "proto-ipv4-fragmentation" ))] -use smoltcp::iface::FragmentsCache; +use smoltcp::iface::ReassemblyBuffer; use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{tcp, udp}; @@ -78,20 +78,20 @@ fn main() { let mut ipv4_out_packet_cache = [0u8; 10_000]; #[cfg(feature = "proto-ipv4-fragmentation")] { - let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); builder = builder - .ipv4_fragments_cache(ipv4_frag_cache) - .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); + .ipv4_reassembly_buffer(ipv4_frag_cache) + .ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { - let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); builder = builder - .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); + .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) + .sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 0e58aae8e..369b733f4 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -47,7 +47,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, ReassemblyBuffer, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; @@ -95,18 +95,18 @@ fn main() { #[cfg(feature = "proto-ipv4-fragmentation")] { - let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); - builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); + builder = builder.ipv4_reassembly_buffer(ipv4_frag_cache); } #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut out_packet_buffer = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { - let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); builder = builder - .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) + .sixlowpan_fragmentation_buffer(&mut out_packet_buffer[..]); } let mut iface = builder.finalize(&mut device); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 7d4b22fcc..5b91e73da 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -48,7 +48,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{FragmentsCache, InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, ReassemblyBuffer, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -166,7 +166,7 @@ fn main() { 64, )]; - let cache = FragmentsCache::new(vec![], BTreeMap::new()); + let cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) @@ -174,8 +174,8 @@ fn main() { builder = builder .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) - .sixlowpan_fragments_cache(cache) - .sixlowpan_out_packet_cache(vec![]); + .sixlowpan_reassembly_buffer(cache) + .sixlowpan_fragmentation_buffer(vec![]); let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 8e103d08c..3df7b011d 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -286,7 +286,7 @@ pub struct InterfaceBuilder<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: Duration, + sixlowpan_reassembly_buffer_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice<'a, u8>, } @@ -325,8 +325,8 @@ let builder = InterfaceBuilder::new() # #[cfg(feature = "proto-ipv4-fragmentation")] let builder = builder - .ipv4_fragments_cache(ipv4_frag_cache) - .ipv4_out_packet_cache(vec![]); + .ipv4_reassembly_buffer(ipv4_frag_cache) + .ipv4_fragmentation_buffer(vec![]); let iface = builder.finalize(&mut device); ``` @@ -359,7 +359,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: Duration::from_secs(60), + sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), } @@ -474,13 +474,13 @@ let iface = builder.finalize(&mut device); } #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_fragments_cache(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { + pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { self.ipv4_fragments = storage; self } #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_out_packet_cache(mut self, storage: T) -> Self + pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self where T: Into>, { @@ -489,7 +489,7 @@ let iface = builder.finalize(&mut device); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_fragments_cache( + pub fn sixlowpan_reassembly_buffer( mut self, storage: PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Self { @@ -498,16 +498,16 @@ let iface = builder.finalize(&mut device); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_fragments_cache_timeout(mut self, timeout: Duration) -> Self { + pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { if timeout > Duration::from_secs(60) { net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); } - self.sixlowpan_fragments_cache_timeout = timeout; + self.sixlowpan_reassembly_buffer_timeout = timeout; self } #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_out_packet_cache(mut self, storage: T) -> Self + pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self where T: Into>, { @@ -611,7 +611,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: self.sixlowpan_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: self.sixlowpan_fragments_cache_timeout, + sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, #[cfg(not(any( feature = "proto-ipv4-fragmentation", @@ -3817,8 +3817,8 @@ mod test { #[cfg(feature = "proto-ipv4-fragmentation")] let iface_builder = iface_builder - .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_out_packet_cache(vec![]); + .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_fragmentation_buffer(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -3847,13 +3847,13 @@ mod test { #[cfg(feature = "proto-sixlowpan-fragmentation")] let iface_builder = iface_builder - .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .sixlowpan_out_packet_cache(vec![]); + .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .sixlowpan_fragmentation_buffer(vec![]); #[cfg(feature = "proto-ipv4-fragmentation")] let iface_builder = iface_builder - .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_out_packet_cache(vec![]); + .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_fragmentation_buffer(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); diff --git a/src/iface/mod.rs b/src/iface/mod.rs index bf8ec9644..2193bbfd1 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -23,6 +23,6 @@ pub use self::route::{Route, Routes}; pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] -pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as FragmentsCache}; +pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as ReassemblyBuffer}; pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 170e2c612..1012b5a18 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2067,14 +2067,14 @@ mod test { #[test] fn sixlowpan_three_fragments() { - use crate::iface::FragmentsCache; + use crate::iface::ReassemblyBuffer; use crate::time::Instant; use crate::wire::ieee802154::Frame as Ieee802154Frame; use crate::wire::ieee802154::Repr as Ieee802154Repr; use crate::wire::Ieee802154Address; use std::collections::BTreeMap; - let mut frags_cache = FragmentsCache::new(vec![], BTreeMap::new()); + let mut frags_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); let frame1: &[u8] = &[ 0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, From 092aba1a1c9492aa253c691c5f536f88220a37af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Sep 2022 23:23:07 +0200 Subject: [PATCH 405/566] Also rename socket-dhcpv4 in error message. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 09bc1169f..6eb0568dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,7 +108,7 @@ compile_error!("You must enable at least one of the following features: proto-ip feature = "socket-dns", )) ))] -compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp, socket-dhcp, socket-dns"); +compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp, socket-dhcpv4, socket-dns"); #[cfg(all( feature = "socket", From 1f2519518c495ae2dab04b6690b907d66760c5b5 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 18 Aug 2022 10:10:33 +0200 Subject: [PATCH 406/566] Change egress error handling Previously, error handling was performed in the closure and after the closure as well. Now, error handling is performed in one place. --- src/iface/interface.rs | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 3df7b011d..dce041f4b 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1194,32 +1194,24 @@ impl<'a> Interface<'a> { let mut neighbor_addr = None; let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); - match device.transmit().ok_or(Error::Exhausted) { - Ok(_t) => { - #[cfg(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - ))] - if let Err(e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { - net_debug!("failed to dispatch IP: {}", e); - return Err(e); - } + let t = device.transmit().ok_or_else(|| { + net_debug!("failed to transmit IP: {}", Error::Exhausted); + Error::Exhausted + })?; - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - if let Err(e) = inner.dispatch_ip(_t, response, None) { - net_debug!("failed to dispatch IP: {}", e); - return Err(e); - } - emitted_any = true; - } - Err(e) => { - net_debug!("failed to transmit IP: {}", e); - return Err(e); - } - } + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] + inner.dispatch_ip(t, response, Some(_out_packets))?; + + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] + inner.dispatch_ip(t, response, None)?; + + emitted_any = true; Ok(()) }; From f5fa08907937ff2133664a508c241f578ae964d5 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 22 Aug 2022 10:26:28 -0400 Subject: [PATCH 407/566] Adds one-shot mDNS resolution RFC 6762 Section 5.1 specifies a one-shot multicast DNS query. This query has minimal differences from a standard DNS query, mostly just using a multicast address and a different port (5353 vs 53). A fully standards compliant mDNS implementation would use UDP source port 5353 as well to issue queries, however we MUST NOT use that port and continue using an ephemeral port until features such as service discovery are implemented. This change also allows specifying what kind of DNS query we wish to perform. https://www.rfc-editor.org/rfc/rfc6762#section-5.1 --- src/socket/dns.rs | 49 +++++++++++++++++++++++++++++++++++++++-------- src/wire/mod.rs | 3 +++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index f06d49e6f..e0d46222d 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -16,11 +16,20 @@ pub const MAX_ADDRESS_COUNT: usize = 4; pub const MAX_SERVER_COUNT: usize = 4; const DNS_PORT: u16 = 53; +const MDNS_DNS_PORT: u16 = 5353; const MAX_NAME_LEN: usize = 255; const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs +#[cfg(feature = "proto-ipv6")] +const MDNS_IPV6_ADDR: IpAddress = IpAddress::Ipv6(crate::wire::Ipv6Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, +])); + +#[cfg(feature = "proto-ipv4")] +const MDNS_IPV4_ADDR: IpAddress = IpAddress::Ipv4(crate::wire::Ipv4Address([224, 0, 0, 251])); + /// Error returned by [`Socket::start_query`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -81,6 +90,7 @@ struct PendingQuery { delay: Duration, server_idx: usize, + mdns: bool, } #[derive(Debug)] @@ -185,6 +195,7 @@ impl<'a> Socket<'a> { &mut self, cx: &mut Context, name: &str, + query_type: Type, ) -> Result { let mut name = name.as_bytes(); @@ -200,6 +211,12 @@ impl<'a> Socket<'a> { let mut raw_name: Vec = Vec::new(); + let mut mdns = false; + if name.split(|&c| c == b'.').last().unwrap() == b"local" { + net_trace!("Starting a mDNS query"); + mdns = true; + } + for s in name.split(|&c| c == b'.') { if s.len() > 63 { net_trace!("invalid name: too long label"); @@ -224,7 +241,7 @@ impl<'a> Socket<'a> { .push(0x00) .map_err(|_| StartQueryError::NameTooLong)?; - self.start_query_raw(cx, &raw_name) + self.start_query_raw(cx, &raw_name, query_type, mdns) } /// Start a query with a raw (wire-format) DNS name. @@ -235,19 +252,33 @@ impl<'a> Socket<'a> { &mut self, cx: &mut Context, raw_name: &[u8], + query_type: Type, + mdns: bool, ) -> Result { let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?; + if mdns { + // as per RFC 6762 any DNS query ending in .local. MUST be sent as mdns + // so we internally overwrite the servers for any of those queries + self.update_servers(&[ + #[cfg(feature = "proto-ipv6")] + MDNS_IPV6_ADDR, + #[cfg(feature = "proto-ipv4")] + MDNS_IPV4_ADDR, + ]); + } + self.queries[handle.0] = Some(DnsQuery { state: State::Pending(PendingQuery { name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?, - type_: Type::A, + type_: query_type, txid: cx.rand().rand_u16(), port: cx.rand().rand_source_port(), delay: RETRANSMIT_DELAY, timeout_at: None, retransmit_at: Instant::ZERO, server_idx: 0, + mdns, }), #[cfg(feature = "async")] waker: WakerRegistration::new(), @@ -313,11 +344,12 @@ impl<'a> Socket<'a> { } pub(crate) fn accepts(&self, ip_repr: &IpRepr, udp_repr: &UdpRepr) -> bool { - udp_repr.src_port == DNS_PORT + (udp_repr.src_port == DNS_PORT && self .servers .iter() - .any(|server| *server == ip_repr.src_addr()) + .any(|server| *server == ip_repr.src_addr())) + || (udp_repr.src_port == MDNS_DNS_PORT) } pub(crate) fn process( @@ -500,7 +532,6 @@ impl<'a> Socket<'a> { // Try next server. We check below whether we've tried all servers. pq.server_idx += 1; } - // Check if we've run out of servers to try. if pq.server_idx >= self.servers.len() { net_trace!("already tried all servers."); @@ -526,7 +557,7 @@ impl<'a> Socket<'a> { opcode: Opcode::Query, question: Question { name: &pq.name, - type_: Type::A, + type_: pq.type_, }, }; @@ -534,9 +565,11 @@ impl<'a> Socket<'a> { let payload = &mut payload[..repr.buffer_len()]; repr.emit(&mut Packet::new_unchecked(payload)); + let dst_port = if pq.mdns { MDNS_DNS_PORT } else { DNS_PORT }; + let udp_repr = UdpRepr { src_port: pq.port, - dst_port: 53, + dst_port, }; let dst_addr = self.servers[pq.server_idx]; @@ -550,7 +583,7 @@ impl<'a> Socket<'a> { ); net_trace!( - "sending {} octets to {:?}:{}", + "sending {} octets to {} from port {}", payload.len(), ip_repr.dst_addr(), udp_repr.src_port diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 72c145576..ed8f64c39 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -248,6 +248,9 @@ pub use self::dhcpv4::{ MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT, }; +#[cfg(feature = "proto-dns")] +pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType}; + /// Parsing a packet failed. /// /// Either it is malformed, or it is not supported by smoltcp. From 2d8f6c1cc1ea47b93a494a04ad693f09b636d5bf Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 22 Aug 2022 10:43:47 -0400 Subject: [PATCH 408/566] Cleans up some clippy lints --- src/iface/interface.rs | 16 ++++------------ src/wire/dhcpv4.rs | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index dce041f4b..fbf633b76 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1015,22 +1015,14 @@ impl<'a> Interface<'a> { self.inner.now = timestamp; #[cfg(feature = "proto-ipv4-fragmentation")] - if let Err(e) = self - .fragments + self.fragments .ipv4_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?)) - { - return Err(e); - } + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; #[cfg(feature = "proto-sixlowpan-fragmentation")] - if let Err(e) = self - .fragments + self.fragments .sixlowpan_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?)) - { - return Err(e); - } + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; #[cfg(feature = "proto-ipv4-fragmentation")] match self.ipv4_egress(device) { diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 2f9018d8c..68daea18a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -364,7 +364,7 @@ impl> Packet { let mut buf = &self.buffer.as_ref()[field::OPTIONS]; iter::from_fn(move || { loop { - match buf.get(0).copied() { + match buf.first().copied() { // No more options, return. None => return None, Some(field::OPT_END) => return None, From 35cea49b8a2c07830a969cffca838ce85227de21 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 22 Aug 2022 11:07:27 -0400 Subject: [PATCH 409/566] Adds DNS query type to example --- examples/dns.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/dns.rs b/examples/dns.rs index 385b2d893..ea86e402d 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -13,7 +13,7 @@ use smoltcp::phy::{wait as phy_wait, Medium}; use smoltcp::socket::dns::{self, GetQueryResultError}; use smoltcp::time::Instant; use smoltcp::wire::{ - EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, + DnsQueryType, EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, }; use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; @@ -68,7 +68,9 @@ fn main() { let dns_handle = sockets.add(dns_socket); let socket = sockets.get_mut::(dns_handle); - let query = socket.start_query(iface.context(), name).unwrap(); + let query = socket + .start_query(iface.context(), name, DnsQueryType::A) + .unwrap(); loop { let timestamp = Instant::now(); From c6e2d8af01a366d7e4852c9029d9000bc4e509c6 Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Tue, 30 Aug 2022 09:41:13 -0400 Subject: [PATCH 410/566] Don't overwrite the DNS servers list permanently --- src/socket/dns.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/socket/dns.rs b/src/socket/dns.rs index e0d46222d..949950fd4 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -257,17 +257,6 @@ impl<'a> Socket<'a> { ) -> Result { let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?; - if mdns { - // as per RFC 6762 any DNS query ending in .local. MUST be sent as mdns - // so we internally overwrite the servers for any of those queries - self.update_servers(&[ - #[cfg(feature = "proto-ipv6")] - MDNS_IPV6_ADDR, - #[cfg(feature = "proto-ipv4")] - MDNS_IPV4_ADDR, - ]); - } - self.queries[handle.0] = Some(DnsQuery { state: State::Pending(PendingQuery { name: Vec::from_slice(raw_name).map_err(|_| StartQueryError::NameTooLong)?, @@ -514,6 +503,19 @@ impl<'a> Socket<'a> { for q in self.queries.iter_mut().flatten() { if let State::Pending(pq) = &mut q.state { + // As per RFC 6762 any DNS query ending in .local. MUST be sent as mdns + // so we internally overwrite the servers for any of those queries + // in this function. + let servers = if pq.mdns { + &[ + #[cfg(feature = "proto-ipv6")] + MDNS_IPV6_ADDR, + #[cfg(feature = "proto-ipv4")] + MDNS_IPV4_ADDR, + ] + } else { + self.servers.as_slice() + }; let timeout = if let Some(timeout) = pq.timeout_at { timeout } else { @@ -533,14 +535,14 @@ impl<'a> Socket<'a> { pq.server_idx += 1; } // Check if we've run out of servers to try. - if pq.server_idx >= self.servers.len() { + if pq.server_idx >= servers.len() { net_trace!("already tried all servers."); q.set_state(State::Failure); continue; } // Check so the IP address is valid - if self.servers[pq.server_idx].is_unspecified() { + if servers[pq.server_idx].is_unspecified() { net_trace!("invalid unspecified DNS server addr."); q.set_state(State::Failure); continue; @@ -572,7 +574,7 @@ impl<'a> Socket<'a> { dst_port, }; - let dst_addr = self.servers[pq.server_idx]; + let dst_addr = servers[pq.server_idx]; let src_addr = cx.get_source_address(dst_addr).unwrap(); // TODO remove unwrap let ip_repr = IpRepr::new( src_addr, From e4af71180fc109b06c4840a6e47b1c255b33fd86 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Sep 2022 16:56:40 +0200 Subject: [PATCH 411/566] Fix 6LoWPAN fragmentation --- src/iface/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index fbf633b76..b95c9700f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -3331,7 +3331,7 @@ impl<'a> InterfaceInner<'a> { let mut total_size = 0; total_size += iphc_repr.buffer_len(); let mut _compressed_headers_len = iphc_repr.buffer_len(); - let mut _uncompressed_headers_len = ip_repr.buffer_len(); + let mut _uncompressed_headers_len = ip_repr.header_len(); #[allow(unreachable_patterns)] match packet { From 58fb0cbc4e9d910a31f6cb3b53f8ffeec6284dfd Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Fri, 23 Sep 2022 13:28:56 -0700 Subject: [PATCH 412/566] Use renewal time from DHCP server ACK, if given Per RFC 2132 section 9.11 the server can manually specify a renewal (T1) time different from the default of half the lease time through option code 58. This PR updates the behavior of the dhcp client to use that value, if provided, and only if not provided does it default to half of the lease duration. Since the current state of smoltcp does not seem to follow the REBINDING state, I also made it look for a value in option code 59 (which should be the rebinding (T2) interval) and use that if no T1 interval interval is provided. This behavior seems sensible to me, given that we're not following the REBINDING part of the spec, but I can change it to ignore option code 59, or any other handling, if that is preferred. --- src/socket/dhcpv4.rs | 21 +++++++++++++++++++-- src/wire/dhcpv4.rs | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 06d2b313d..d4a6ce87e 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -439,8 +439,23 @@ impl<'a> Socket<'a> { packet: None, }; - // RFC 2131 indicates clients should renew a lease halfway through its expiration. - let renew_at = now + lease_duration / 2; + // Set renew time as per RFC 2131: + // The renew time (T1) can be specified by the server using option 58: + let renew_duration = dhcp_repr + .renew_duration + .map(|d| Duration::from_secs(d as u64)) + // Since we don't follow the REBINDING part of the spec, when no + // explicit T1 time is given, we will also consider the rebinding + // time if it is given and less than the default. + .or_else(|| { + dhcp_repr + .rebind_duration + .map(|d| Duration::from_secs(d as u64).min(lease_duration / 2)) + }) + // Otherwise, we use the default T1 time, which is half the lease + // duration. + .unwrap_or(lease_duration / 2); + let renew_at = now + renew_duration; let expires_at = now + lease_duration; Some((config, renew_at, expires_at)) @@ -497,6 +512,8 @@ impl<'a> Socket<'a> { ), max_size: Some((cx.ip_mtu() - MAX_IPV4_HEADER_LEN - UDP_HEADER_LEN) as u16), lease_duration: None, + renew_duration: None, + rebind_duration: None, dns_servers: None, additional_options: self.outgoing_options, }; diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 2f9018d8c..b7466b64a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -651,6 +651,10 @@ pub struct Repr<'a> { pub max_size: Option, /// The DHCP IP lease duration, specified in seconds. pub lease_duration: Option, + /// The DHCP IP renew duration (T1 interval), in seconds, if specified in the packet. + pub renew_duration: Option, + /// The DHCP IP rebind duration (T2 interval), in seconds, if specified in the packet. + pub rebind_duration: Option, /// When returned from [`Repr::parse`], this field will be `None`. /// However, when calling [`Repr::emit`], this field should contain only /// additional DHCP options not known to smoltcp. @@ -735,6 +739,8 @@ impl<'a> Repr<'a> { let mut dns_servers = None; let mut max_size = None; let mut lease_duration = None; + let mut renew_duration = None; + let mut rebind_duration = None; for option in packet.options() { let data = option.data; @@ -767,6 +773,12 @@ impl<'a> Repr<'a> { (field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => { max_size = Some(u16::from_be_bytes([data[0], data[1]])); } + (field::OPT_RENEWAL_TIME_VALUE, 4) => { + renew_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) + } + (field::OPT_REBINDING_TIME_VALUE, 4) => { + rebind_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) + } (field::OPT_IP_LEASE_TIME, 4) => { lease_duration = Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]])) } @@ -808,6 +820,8 @@ impl<'a> Repr<'a> { dns_servers, max_size, lease_duration, + renew_duration, + rebind_duration, message_type: message_type?, additional_options: &[], }) From 6a4c549a94ace414340bfcd220c87d77d67bafcb Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Fri, 23 Sep 2022 14:05:31 -0700 Subject: [PATCH 413/566] Fix failing tests --- src/socket/dhcpv4.rs | 2 ++ src/wire/dhcpv4.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d4a6ce87e..d42abb914 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -860,6 +860,8 @@ mod test { parameter_request_list: None, dns_servers: None, max_size: None, + renew_duration: None, + rebind_duration: None, lease_duration: None, additional_options: &[], }; diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index b7466b64a..04388247d 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -1162,6 +1162,8 @@ mod test { parameter_request_list: None, dns_servers: None, max_size: None, + renew_duration: None, + rebind_duration: None, lease_duration: Some(0xffff_ffff), // Infinite lease additional_options: &[], } @@ -1181,6 +1183,8 @@ mod test { broadcast: false, secs: 0, max_size: Some(DHCP_SIZE), + renew_duration: None, + rebind_duration: None, lease_duration: None, requested_ip: Some(IP_NULL), client_identifier: Some(CLIENT_MAC), From ab49e5617cf3722c125dddf9d27623449587fa26 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Sep 2022 16:56:40 +0200 Subject: [PATCH 414/566] Fix 6LoWPAN fragmentation --- src/iface/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index dce041f4b..b26c4a5e2 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -3339,7 +3339,7 @@ impl<'a> InterfaceInner<'a> { let mut total_size = 0; total_size += iphc_repr.buffer_len(); let mut _compressed_headers_len = iphc_repr.buffer_len(); - let mut _uncompressed_headers_len = ip_repr.buffer_len(); + let mut _uncompressed_headers_len = ip_repr.header_len(); #[allow(unreachable_patterns)] match packet { From 23cb8bd5d04864664a4f74e17c95a2645e875c6c Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 26 Sep 2022 10:48:09 -0400 Subject: [PATCH 415/566] Move mDNS implementation behind feature flag Swaps the bool in the raw query API to an enum that can statically prevent mdns usage without the feature flag enabled. --- Cargo.toml | 3 ++- src/socket/dns.rs | 33 +++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 60c785837..06cde3ace 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "socket-icmp" = ["socket"] "socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"] "socket-dns" = ["socket", "proto-dns"] +"socket-mdns" = ["socket-dns"] "async" = [] @@ -69,7 +70,7 @@ default = [ "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns", "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation", - "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", + "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns", "async" ] diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 949950fd4..a287d0b11 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -90,7 +90,14 @@ struct PendingQuery { delay: Duration, server_idx: usize, - mdns: bool, + mdns: MulticastDns, +} + +#[derive(Debug)] +pub enum MulticastDns { + Disabled, + #[cfg(feature = "socket-mdns")] + Enabled, } #[derive(Debug)] @@ -211,10 +218,11 @@ impl<'a> Socket<'a> { let mut raw_name: Vec = Vec::new(); - let mut mdns = false; + let mut mdns = MulticastDns::Disabled; + #[cfg(feature = "socket-mdns")] if name.split(|&c| c == b'.').last().unwrap() == b"local" { net_trace!("Starting a mDNS query"); - mdns = true; + mdns = MulticastDns::Enabled; } for s in name.split(|&c| c == b'.') { @@ -253,7 +261,7 @@ impl<'a> Socket<'a> { cx: &mut Context, raw_name: &[u8], query_type: Type, - mdns: bool, + mdns: MulticastDns, ) -> Result { let handle = self.find_free_query().ok_or(StartQueryError::NoFreeSlot)?; @@ -506,16 +514,17 @@ impl<'a> Socket<'a> { // As per RFC 6762 any DNS query ending in .local. MUST be sent as mdns // so we internally overwrite the servers for any of those queries // in this function. - let servers = if pq.mdns { - &[ + let servers = match pq.mdns { + #[cfg(feature = "socket-mdns")] + MulticastDns::Enabled => &[ #[cfg(feature = "proto-ipv6")] MDNS_IPV6_ADDR, #[cfg(feature = "proto-ipv4")] MDNS_IPV4_ADDR, - ] - } else { - self.servers.as_slice() + ], + MulticastDns::Disabled => self.servers.as_slice(), }; + let timeout = if let Some(timeout) = pq.timeout_at { timeout } else { @@ -567,7 +576,11 @@ impl<'a> Socket<'a> { let payload = &mut payload[..repr.buffer_len()]; repr.emit(&mut Packet::new_unchecked(payload)); - let dst_port = if pq.mdns { MDNS_DNS_PORT } else { DNS_PORT }; + let dst_port = match pq.mdns { + #[cfg(feature = "socket-mdns")] + MulticastDns::Enabled => MDNS_DNS_PORT, + MulticastDns::Disabled => DNS_PORT, + }; let udp_repr = UdpRepr { src_port: pq.port, From b88021e0628966aa0539a77a9145886d76b22a04 Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Fri, 23 Sep 2022 16:54:41 -0700 Subject: [PATCH 416/566] DHCP indicate new config if there's a packet buffer provided \# Description If the user provides a buffer in which to store the packets, then the contents of the received packet will be buffered and included in the returned Config when the DHCP connection is made. However, it isn't included in the Config struct until the value is returned to the user, so the equality check for whether to call `config_changed` disregards any additional information in the buffer beyond what this library parses. For my purposes, I need access to the contents of the buffer when they change as a result of a new packet, even if everything else is the same. Since two packets will almost certainly not be the same thanks to the magic cookie (unless the packet gets duplicated on the network, which is an acceptably low risk for my use of smoltcp), an acceptable option for my uses is to just always return the new configuration when a packet is received (gated on whether a buffer is provided to return the packet into). \# Alternatives While this approach is the easiest and best for my uses, I can think of the following alternatives which would also work and might be prefered for other use-cases: * Allow the user to specify whether they wish to receive all packets instead of opting all users who provide a buffer into this behavior * Allow the user to provide a closure which compares the old and new packets and returns true if this represents a new config which should be returned. * Compare the old packet to the new packet (either byte-by-byte or looking at the provided options) and only return a new config if differences are found. \# Verification In my setup, I was seeing bugs that were caused by smoltcp not exposing the config when the only changes were in the additional options that I want to use but which smoltcp doesn't use directly. Using this branch instead of the main release fixed those bugs and I was able to verify that it behaves the way I expected. I think this verification, along with CI tests passing, should be sufficient for verifying this PR. --- src/socket/dhcpv4.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d42abb914..5e110602f 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -361,8 +361,17 @@ impl<'a> Socket<'a> { ) { state.renew_at = renew_at; state.expires_at = expires_at; + // The `receive_packet_buffer` field isn't populated until + // the client asks for the state, but receiving any packet + // will change it, so we indicate that the config has + // changed every time if the receive packet buffer is set, + // but we only write changes to the rest of the config now. + let config_changed = + state.config != config || self.receive_packet_buffer.is_some(); if state.config != config { state.config = config; + } + if config_changed { self.config_changed(); } } From ba1d4db42290ab3feaf7dbfa1bda5995348fc77a Mon Sep 17 00:00:00 2001 From: Benjamin Brittain Date: Mon, 26 Sep 2022 10:21:04 -0400 Subject: [PATCH 417/566] Changes egress functions to pass up Err(Exhausted) --- src/iface/interface.rs | 49 ++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index b95c9700f..5d1e51414 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1027,14 +1027,20 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] match self.ipv4_egress(device) { Ok(true) => return Ok(true), - Err(e) => return Err(e), + Err(e) => { + net_debug!("failed to transmit: {}", e); + return Err(e); + } _ => (), } #[cfg(feature = "proto-sixlowpan-fragmentation")] match self.sixlowpan_egress(device) { Ok(true) => return Ok(true), - Err(e) => return Err(e), + Err(e) => { + net_debug!("failed to transmit: {}", e); + return Err(e); + } _ => (), } @@ -1354,20 +1360,13 @@ impl<'a> Interface<'a> { } = &self.out_packets.ipv4_out_packet; if *packet_len > *sent_bytes { - match device.transmit().ok_or(Error::Exhausted) { - Ok(tx_token) => { - if let Err(e) = self - .inner - .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet) - { - net_debug!("failed to transmit: {}", e); - } - } - Err(e) => { - net_debug!("failed to transmit: {}", e); - } + match device.transmit() { + Some(tx_token) => self + .inner + .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet), + None => Err(Error::Exhausted), } - Ok(true) + .map(|_| true) } else { Ok(false) } @@ -1398,20 +1397,14 @@ impl<'a> Interface<'a> { } if *packet_len > *sent_bytes { - match device.transmit().ok_or(Error::Exhausted) { - Ok(tx_token) => { - if let Err(e) = self.inner.dispatch_ieee802154_out_packet( - tx_token, - &mut self.out_packets.sixlowpan_out_packet, - ) { - net_debug!("failed to transmit: {}", e); - } - } - Err(e) => { - net_debug!("failed to transmit: {}", e); - } + match device.transmit() { + Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( + tx_token, + &mut self.out_packets.sixlowpan_out_packet, + ), + None => Err(Error::Exhausted), } - Ok(true) + .map(|_| true) } else { Ok(false) } From 64ce56be6e7da5a0635e36c09aeafe58ba75be25 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 28 Sep 2022 12:17:52 +0200 Subject: [PATCH 418/566] remove redundant check and add docs --- src/iface/interface.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 5d1e51414..ee04c5cb9 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1339,6 +1339,11 @@ impl<'a> Interface<'a> { } } + /// Process fragments that still need to be sent for IPv4 packets. + /// + /// This function returns a boolean value indicating whether any packets were + /// processed or emitted, and thus, whether the readiness of any socket might + /// have changed. #[cfg(feature = "proto-ipv4-fragmentation")] fn ipv4_egress(&mut self, device: &mut D) -> Result where @@ -1372,6 +1377,11 @@ impl<'a> Interface<'a> { } } + /// Process fragments that still need to be sent for 6LoWPAN packets. + /// + /// This function returns a boolean value indicating whether any packets were + /// processed or emitted, and thus, whether the readiness of any socket might + /// have changed. #[cfg(feature = "proto-sixlowpan-fragmentation")] fn sixlowpan_egress(&mut self, device: &mut D) -> Result where @@ -1392,10 +1402,6 @@ impl<'a> Interface<'a> { .. } = &self.out_packets.sixlowpan_out_packet; - if *packet_len == 0 { - return Ok(false); - } - if *packet_len > *sent_bytes { match device.transmit() { Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( From bd220ebcdf181e95b2bcdd622a2e749937249f12 Mon Sep 17 00:00:00 2001 From: caiyuanhao Date: Sat, 8 Oct 2022 19:32:29 +0800 Subject: [PATCH 419/566] fix boundary case of assembler::remove_contig_at --- src/storage/assembler.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 8fb6390d5..8a52d53c3 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -171,8 +171,9 @@ impl Assembler { } // Removing the last one. - self.contigs[at] = Contig::empty(); - &mut self.contigs[at] + let p = &mut self.contigs[self.contigs.len() - 1]; + *p = Contig::empty(); + p } /// Add a contig at the given index, and return a pointer to it. @@ -476,6 +477,18 @@ mod test { assert_eq!(assr, contigs![(4, 4), (4, 0)]); } + #[test] + fn test_boundary_case_remove_front() { + let mut vec = vec![(1, 1); CONTIG_COUNT]; + vec[0] = (0, 2); + let mut assr = Assembler::from(vec); + assert_eq!(assr.remove_front(), Some(2)); + let mut vec = vec![(1, 1); CONTIG_COUNT]; + vec[CONTIG_COUNT - 1] = (2, 0); + let exp_assr = Assembler::from(vec); + assert_eq!(assr, exp_assr); + } + #[test] fn test_iter_empty() { let assr = Assembler::new(16); From d2e8e993fe824aa25bbd8374b1630d839fabe704 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 30 Sep 2022 14:57:58 +0200 Subject: [PATCH 420/566] add addr resolv with context --- fuzz/fuzz_targets/sixlowpan_packet.rs | 1 + src/iface/interface.rs | 30 +++ src/wire/mod.rs | 2 +- src/wire/sixlowpan.rs | 262 ++++++++++++++++++-------- 4 files changed, 218 insertions(+), 77 deletions(-) diff --git a/fuzz/fuzz_targets/sixlowpan_packet.rs b/fuzz/fuzz_targets/sixlowpan_packet.rs index d9ba99bb8..37038e456 100644 --- a/fuzz/fuzz_targets/sixlowpan_packet.rs +++ b/fuzz/fuzz_targets/sixlowpan_packet.rs @@ -43,6 +43,7 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { &frame, fuzz.ll_src_addr.map(Into::into), fuzz.ll_dst_addr.map(Into::into), + &[], ) { let mut buffer = vec![0; iphc_repr.buffer_len()]; let mut iphc_frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ee04c5cb9..968407221 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -247,6 +247,8 @@ pub struct InterfaceInner<'a> { pan_id: Option, #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: u16, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, @@ -289,6 +291,9 @@ pub struct InterfaceBuilder<'a> { sixlowpan_reassembly_buffer_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice<'a, u8>, + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], } impl<'a> InterfaceBuilder<'a> { @@ -362,6 +367,9 @@ let iface = builder.finalize(&mut device); sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], } } @@ -473,12 +481,14 @@ let iface = builder.finalize(&mut device); self } + /// Set the IPv4 reassembly buffer the interface will use. #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { self.ipv4_fragments = storage; self } + /// Set the IPv4 fragments buffer the interface will use. #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self where @@ -488,6 +498,17 @@ let iface = builder.finalize(&mut device); self } + /// Set the address contexts the interface will use. + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_address_context( + mut self, + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + ) -> Self { + self.sixlowpan_address_context = sixlowpan_address_context; + self + } + + /// Set the 6LoWPAN reassembly buffer the interface will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer( mut self, @@ -497,6 +518,7 @@ let iface = builder.finalize(&mut device); self } + /// Set the timeout value the 6LoWPAN reassembly buffer will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { if timeout > Duration::from_secs(60) { @@ -506,6 +528,7 @@ let iface = builder.finalize(&mut device); self } + /// Set the 6LoWPAN fragments buffer the interface will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self where @@ -651,6 +674,8 @@ let iface = builder.finalize(&mut device); tag, #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], rand, }, } @@ -1534,6 +1559,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: 1, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], + #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: 1, @@ -1804,6 +1832,7 @@ impl<'a> InterfaceInner<'a> { &iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, + self.sixlowpan_address_context )); // The uncompressed header size always starts with 40, since this is the size @@ -1888,6 +1917,7 @@ impl<'a> InterfaceInner<'a> { &iphc_packet, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, + self.sixlowpan_address_context, )); let payload = iphc_packet.payload(); diff --git a/src/wire/mod.rs b/src/wire/mod.rs index ed8f64c39..30f4a8250 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -151,7 +151,7 @@ pub use self::sixlowpan::{ NhcPacket as SixlowpanNhcPacket, UdpNhcPacket as SixlowpanUdpNhcPacket, UdpNhcRepr as SixlowpanUdpNhcRepr, }, - NextHeader as SixlowpanNextHeader, SixlowpanPacket, + AddressContext as SixlowpanAddressContext, NextHeader as SixlowpanNextHeader, SixlowpanPacket, }; #[cfg(feature = "medium-ieee802154")] diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 1012b5a18..42031601a 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2,11 +2,25 @@ //! IEEE802.154-based networks. //! //! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 +use core::ops::Deref; + use super::{Error, Result}; use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AddressContext<'a>(pub &'a [u8]); + +impl<'a> Deref for AddressContext<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + /// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with /// and without context information. The decompression with context information is not yet /// implemented. @@ -14,7 +28,7 @@ use crate::wire::IpProtocol; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UnresolvedAddress<'a> { WithoutContext(AddressMode<'a>), - WithContext(AddressMode<'a>), + WithContext((usize, AddressMode<'a>)), Reserved, } @@ -51,8 +65,30 @@ const LINK_LOCAL_PREFIX: [u8; 2] = [0xfe, 0x80]; const EUI64_MIDDLE_VALUE: [u8; 2] = [0xff, 0xfe]; impl<'a> UnresolvedAddress<'a> { - pub fn resolve(self, ll_address: Option) -> Result { + pub fn resolve( + self, + ll_address: Option, + addr_context: &[AddressContext<'_>], + ) -> Result { let mut bytes = [0; 16]; + + let copy_context = |index: usize, bytes: &mut [u8]| -> Result<()> { + if index >= addr_context.len() { + return Err(Error); + } + + let context = addr_context[index]; + let len = context.len(); + + if len > 8 { + return Err(Error); + } + + bytes[..len].copy_from_slice(&context); + + Ok(()) + }; + match self { UnresolvedAddress::WithoutContext(mode) => match mode { AddressMode::FullInline(addr) => Ok(ipv6::Address::from_bytes(addr)), @@ -104,8 +140,35 @@ impl<'a> UnresolvedAddress<'a> { _ => Err(Error), }, UnresolvedAddress::WithContext(mode) => match mode { - AddressMode::Unspecified => Ok(ipv6::Address::UNSPECIFIED), - AddressMode::NotSupported => Err(Error), + (_, AddressMode::Unspecified) => Ok(ipv6::Address::UNSPECIFIED), + (index, AddressMode::InLine64bits(inline)) => { + copy_context(index, &mut bytes[..])?; + bytes[16 - inline.len()..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + (index, AddressMode::InLine16bits(inline)) => { + copy_context(index, &mut bytes[..])?; + bytes[16 - inline.len()..].copy_from_slice(inline); + Ok(ipv6::Address::from_bytes(&bytes[..])) + } + (index, AddressMode::FullyElided) => { + match ll_address { + Some(LlAddress::Short(ll)) => { + bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]); + bytes[14..].copy_from_slice(&ll); + } + Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() { + Some(addr) => bytes[8..].copy_from_slice(&addr), + None => return Err(Error), + }, + Some(LlAddress::Absent) => return Err(Error), + None => return Err(Error), + } + + copy_context(index, &mut bytes[..])?; + + Ok(ipv6::Address::from_bytes(&bytes[..])) + } _ => Err(Error), }, UnresolvedAddress::Reserved => Err(Error), @@ -421,7 +484,10 @@ pub mod iphc { //! //! [RFC 6282 § 3.1]: https://datatracker.ietf.org/doc/html/rfc6282#section-3.1 - use super::{AddressMode, Error, NextHeader, Result, UnresolvedAddress, DISPATCH_IPHC_HEADER}; + use super::{ + AddressContext, AddressMode, Error, NextHeader, Result, UnresolvedAddress, + DISPATCH_IPHC_HEADER, + }; use crate::wire::{ieee802154::Address as LlAddress, ipv6, IpProtocol}; use byteorder::{ByteOrder, NetworkEndian}; @@ -573,7 +639,7 @@ pub mod iphc { pub fn src_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); - Some(data[1] >> 4) + Some(data[2] >> 4) } else { None } @@ -583,7 +649,7 @@ pub mod iphc { pub fn dst_context_id(&self) -> Option { if self.cid_field() == 1 { let data = self.buffer.as_ref(); - Some(data[1] & 0x0f) + Some(data[2] & 0x0f) } else { None } @@ -640,30 +706,52 @@ pub mod iphc { + self.next_header_size() + self.hop_limit_size()) as usize; + let data = self.buffer.as_ref(); match (self.sac_field(), self.sam_field()) { - (0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) + (0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )), + (0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )), + (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), + (1, 0b00) => Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::Unspecified, + ))), + (1, 0b01) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine64bits(&data[start..][..8]), + ))) + } else { + Err(Error) + } } - (0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine64bits(&data[start..][..8]), - )) + (1, 0b10) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine16bits(&data[start..][..2]), + ))) + } else { + Err(Error) + } } - (0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine16bits(&data[start..][..2]), - )) + (1, 0b11) => { + if let Some(id) = self.src_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::FullyElided, + ))) + } else { + Err(Error) + } } - (0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), - (1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::Unspecified)), - (1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), _ => Err(Error), } } @@ -676,55 +764,65 @@ pub mod iphc { + self.hop_limit_size() + self.src_address_size()) as usize; + let data = self.buffer.as_ref(); match (self.m_field(), self.dac_field(), self.dam_field()) { - (0, 0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) - } - (0, 0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine64bits(&data[start..][..8]), - )) - } - (0, 0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::InLine16bits(&data[start..][..2]), - )) - } + (0, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (0, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine64bits(&data[start..][..8]), + )), + (0, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::InLine16bits(&data[start..][..2]), + )), (0, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullyElided)), (0, 1, 0b00) => Ok(UnresolvedAddress::Reserved), - (0, 1, 0b01) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (0, 1, 0b10) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (0, 1, 0b11) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), - (1, 0, 0b00) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( - &data[start..][..16], - ))) - } - (1, 0, 0b01) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast48bits(&data[start..][..6]), - )) + (0, 1, 0b01) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine64bits(&data[start..][..8]), + ))) + } else { + Err(Error) + } } - (1, 0, 0b10) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast32bits(&data[start..][..4]), - )) + (0, 1, 0b10) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::InLine16bits(&data[start..][..2]), + ))) + } else { + Err(Error) + } } - (1, 0, 0b11) => { - let data = self.buffer.as_ref(); - Ok(UnresolvedAddress::WithoutContext( - AddressMode::Multicast8bits(&data[start..][..1]), - )) + (0, 1, 0b11) => { + if let Some(id) = self.dst_context_id() { + Ok(UnresolvedAddress::WithContext(( + id as usize, + AddressMode::FullyElided, + ))) + } else { + Err(Error) + } } - (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)), + (1, 0, 0b00) => Ok(UnresolvedAddress::WithoutContext(AddressMode::FullInline( + &data[start..][..16], + ))), + (1, 0, 0b01) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast48bits(&data[start..][..6]), + )), + (1, 0, 0b10) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast32bits(&data[start..][..4]), + )), + (1, 0, 0b11) => Ok(UnresolvedAddress::WithoutContext( + AddressMode::Multicast8bits(&data[start..][..1]), + )), + (1, 1, 0b00) => Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::NotSupported, + ))), (1, 1, 0b01 | 0b10 | 0b11) => Ok(UnresolvedAddress::Reserved), _ => Err(Error), } @@ -1086,6 +1184,7 @@ pub mod iphc { packet: &Packet<&T>, ll_src_addr: Option, ll_dst_addr: Option, + addr_context: &[AddressContext<'_>], ) -> Result { // Ensure basic accessors will work. packet.check_len()?; @@ -1095,8 +1194,8 @@ pub mod iphc { return Err(Error); } - let src_addr = packet.src_addr()?.resolve(ll_src_addr)?; - let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr)?; + let src_addr = packet.src_addr()?.resolve(ll_src_addr, addr_context)?; + let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr, addr_context)?; Ok(Self { src_addr, @@ -1293,11 +1392,17 @@ pub mod iphc { assert_eq!( packet.src_addr(), - Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::FullyElided + ))) ); assert_eq!( packet.dst_addr(), - Ok(UnresolvedAddress::WithContext(AddressMode::NotSupported)) + Ok(UnresolvedAddress::WithContext(( + 0, + AddressMode::FullyElided + ))) ); } } @@ -2205,8 +2310,13 @@ mod test { unreachable!() }; - let iphc_repr = - iphc::Repr::parse(&iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr).unwrap(); + let iphc_repr = iphc::Repr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + &[], + ) + .unwrap(); assert_eq!( iphc_repr.dst_addr, From 82faba25cb32dfd3ef62c012331eaadec577c977 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Sep 2022 09:50:39 +0200 Subject: [PATCH 421/566] Use process_udp for UDP handling in 6LoWPAN --- src/iface/interface.rs | 154 +++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 968407221..d09b4816a 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1950,59 +1950,17 @@ impl<'a> InterfaceInner<'a> { let udp_repr = check!(SixlowpanUdpNhcRepr::parse( &udp_packet, &iphc_repr.src_addr, - &iphc_repr.dst_addr, + &iphc_repr.dst_addr )); - // Look for UDP sockets that will accept the UDP packet. - // If it does not accept the packet, then send an ICMP message. - // - // NOTE(thvdveld): this is currently the same code as in self.process_udp. - // However, we cannot use that one because the payload passed to it is a - // normal IPv6 UDP payload, which is not what we have here. - for udp_socket in sockets - .items_mut() - .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) - { - if udp_socket.accepts(self, &IpRepr::Ipv6(ipv6_repr), &udp_repr) { - udp_socket.process( - self, - &IpRepr::Ipv6(ipv6_repr), - &udp_repr, - udp_packet.payload(), - ); - return None; - } - } - - #[cfg(feature = "socket-dns")] - for dns_socket in sockets - .items_mut() - .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket)) - { - if dns_socket.accepts(&IpRepr::Ipv6(ipv6_repr), &udp_repr) { - dns_socket.process( - self, - &IpRepr::Ipv6(ipv6_repr), - &udp_repr, - udp_packet.payload(), - ); - return None; - } - } - - // When we are here then then there was no UDP socket that accepted the UDP - // message. - let payload_len = icmp_reply_payload_len( - payload.len(), - IPV6_MIN_MTU, - ipv6_repr.buffer_len(), - ); - let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ipv6_repr, - data: &payload[0..payload_len], - }; - self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr) + self.process_udp( + sockets, + IpRepr::Ipv6(ipv6_repr), + udp_repr.0, + false, + udp_packet.payload(), + payload, + ) } } } @@ -2162,7 +2120,22 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpProtocol::Udp => { - self.process_udp(sockets, ipv6_repr.into(), handled_by_raw_socket, ip_payload) + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &ipv6_repr.src_addr.into(), + &ipv6_repr.dst_addr.into(), + &self.checksum_caps(), + )); + + self.process_udp( + sockets, + ipv6_repr.into(), + udp_repr, + handled_by_raw_socket, + udp_packet.payload(), + ip_payload, + ) } #[cfg(feature = "socket-tcp")] @@ -2331,7 +2304,22 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] IpProtocol::Udp => { - self.process_udp(sockets, ip_repr, handled_by_raw_socket, ip_payload) + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &ipv4_repr.src_addr.into(), + &ipv4_repr.dst_addr.into(), + &self.checksum_caps(), + )); + + self.process_udp( + sockets, + ip_repr, + udp_repr, + handled_by_raw_socket, + udp_packet.payload(), + ip_payload, + ) } #[cfg(feature = "socket-tcp")] @@ -2754,19 +2742,11 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ip_repr: IpRepr, + udp_repr: UdpRepr, handled_by_raw_socket: bool, + udp_payload: &'frame [u8], ip_payload: &'frame [u8], ) -> Option> { - let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let udp_packet = check!(UdpPacket::new_checked(ip_payload)); - let udp_repr = check!(UdpRepr::parse( - &udp_packet, - &src_addr, - &dst_addr, - &self.caps.checksum - )); - let udp_payload = udp_packet.payload(); - #[cfg(feature = "socket-udp")] for udp_socket in sockets .items_mut() @@ -4159,7 +4139,9 @@ mod test { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( - iface.inner.process_udp(&mut sockets, ip_repr, false, data), + iface + .inner + .process_udp(&mut sockets, ip_repr, udp_repr, false, &UDP_PAYLOAD, data), Some(expected_repr) ); @@ -4185,9 +4167,14 @@ mod test { // 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 sockets, ip_repr, false, packet_broadcast.into_inner()), + iface.inner.process_udp( + &mut sockets, + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet_broadcast.into_inner(), + ), None ); } @@ -4255,9 +4242,14 @@ mod test { // Packet should be handled by bound UDP socket assert_eq!( - iface - .inner - .process_udp(&mut sockets, ip_repr, false, packet.into_inner()), + iface.inner.process_udp( + &mut sockets, + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet.into_inner(), + ), None ); @@ -4449,16 +4441,26 @@ mod test { // 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 sockets, ip_repr.into(), false, payload), + iface.inner.process_udp( + &mut sockets, + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) ); #[cfg(feature = "proto-ipv6")] assert_eq!( - iface - .inner - .process_udp(&mut sockets, ip_repr.into(), false, payload), + iface.inner.process_udp( + &mut sockets, + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) ); } From c294ce599fa0a2560d142f0a05c59e35e65d375e Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Sep 2022 15:13:54 +0200 Subject: [PATCH 422/566] Move 6LoWPAN frag handling to own function --- src/iface/interface.rs | 215 ++++++++++++++++++++++------------------- 1 file changed, 116 insertions(+), 99 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index d09b4816a..9ebd8c38f 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1806,105 +1806,9 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-sixlowpan-fragmentation")] SixlowpanPacket::FragmentHeader => { - let (fragments, timeout) = _fragments.unwrap(); - - // We have a fragment header, which means we cannot process the 6LoWPAN packet, - // unless we have a complete one after processing this fragment. - let frag = check!(SixlowpanFragPacket::new_checked(payload)); - - // The key specifies to which 6LoWPAN fragment it belongs too. - // It is based on the link layer addresses, the tag and the size. - let key = frag.get_key(ieee802154_repr); - - // The offset of this fragment in increments of 8 octets. - let offset = frag.datagram_offset() as usize * 8; - - if frag.is_first_fragment() { - // The first fragment contains the total size of the IPv6 packet. - // However, we received a packet that is compressed following the 6LoWPAN - // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN - // packet size. The packet size can be different because of first the - // compression of the IP header and when UDP is used (because the UDP header - // can also be compressed). Other headers are not compressed by 6LoWPAN. - - let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); - let iphc_repr = check!(SixlowpanIphcRepr::parse( - &iphc, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - self.sixlowpan_address_context - )); - - // The uncompressed header size always starts with 40, since this is the size - // of a IPv6 header. - let mut uncompressed_header_size = 40; - let mut compressed_header_size = iphc.header_len(); - - // We need to check if we have an UDP packet, since this header can also be - // compressed by 6LoWPAN. We currently don't support extension headers yet. - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { - SixlowpanNhcPacket::ExtHeader => { - net_debug!("6LoWPAN: extension headers not supported"); - return None; - } - SixlowpanNhcPacket::UdpHeader => { - let udp_packet = - check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); - - uncompressed_header_size += 8; - compressed_header_size += - 1 + udp_packet.ports_size() + udp_packet.checksum_size(); - } - } - } - SixlowpanNextHeader::Uncompressed(_) => (), - } - - // We reserve a spot in the packet assembler set and add the required - // information to the packet assembler. - // This information is the total size of the packet when it is fully assmbled. - // We also pass the header size, since this is needed when other fragments - // (other than the first one) are added. - let frag_slot = match fragments.reserve_with_key(&key) { - Ok(frag) => frag, - Err(Error::PacketAssemblerSetFull) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); - } - e => check!(e), - }; - - check!(frag_slot.start( - Some( - frag.datagram_size() as usize - uncompressed_header_size - + compressed_header_size - ), - self.now + timeout, - -((uncompressed_header_size - compressed_header_size) as isize), - )); - } - - let frags = check!(fragments.get_packet_assembler_mut(&key)); - - net_trace!("6LoWPAN: received packet fragment"); - - // Add the fragment to the packet assembler. - match frags.add(frag.payload(), offset) { - Ok(true) => { - net_trace!("6LoWPAN: fragmented packet now complete"); - check!(fragments.get_assembled_packet(&key)) - } - Ok(false) => { - return None; - } - Err(Error::PacketAssemblerOverlap) => { - net_trace!("6LoWPAN: overlap in packet"); - frags.mark_discarded(); - return None; - } - Err(_) => return None, + match self.process_sixlowpan_fragment(ieee802154_repr, payload, _fragments) { + Some(payload) => payload, + None => return None, } } SixlowpanPacket::IphcHeader => payload.as_ref(), @@ -1983,6 +1887,119 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + fn process_sixlowpan_fragment<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + ieee802154_repr: &Ieee802154Repr, + payload: &'payload T, + fragments: Option<( + &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + Duration, + )>, + ) -> Option<&'output [u8]> { + let (fragments, timeout) = fragments.unwrap(); + + // We have a fragment header, which means we cannot process the 6LoWPAN packet, + // unless we have a complete one after processing this fragment. + let frag = check!(SixlowpanFragPacket::new_checked(payload)); + + // The key specifies to which 6LoWPAN fragment it belongs too. + // It is based on the link layer addresses, the tag and the size. + let key = frag.get_key(ieee802154_repr); + + // The offset of this fragment in increments of 8 octets. + let offset = frag.datagram_offset() as usize * 8; + + if frag.is_first_fragment() { + // The first fragment contains the total size of the IPv6 packet. + // However, we received a packet that is compressed following the 6LoWPAN + // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN + // packet size. The packet size can be different because of first the + // compression of the IP header and when UDP is used (because the UDP header + // can also be compressed). Other headers are not compressed by 6LoWPAN. + + let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); + let iphc_repr = check!(SixlowpanIphcRepr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + self.sixlowpan_address_context, + )); + + // The uncompressed header size always starts with 40, since this is the size + // of a IPv6 header. + let mut uncompressed_header_size = 40; + let mut compressed_header_size = iphc.header_len(); + + // We need to check if we have an UDP packet, since this header can also be + // compressed by 6LoWPAN. We currently don't support extension headers yet. + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + SixlowpanNhcPacket::ExtHeader => { + net_debug!("6LoWPAN: extension headers not supported"); + return None; + } + SixlowpanNhcPacket::UdpHeader => { + let udp_packet = + check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); + + uncompressed_header_size += 8; + compressed_header_size += + 1 + udp_packet.ports_size() + udp_packet.checksum_size(); + } + } + } + SixlowpanNextHeader::Uncompressed(_) => (), + } + + // We reserve a spot in the packet assembler set and add the required + // information to the packet assembler. + // This information is the total size of the packet when it is fully assmbled. + // We also pass the header size, since this is needed when other fragments + // (other than the first one) are added. + let frag_slot = match fragments.reserve_with_key(&key) { + Ok(frag) => frag, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(frag_slot.start( + Some( + frag.datagram_size() as usize - uncompressed_header_size + + compressed_header_size + ), + self.now + timeout, + -((uncompressed_header_size - compressed_header_size) as isize), + )); + } + + let frags = check!(fragments.get_packet_assembler_mut(&key)); + + net_trace!("6LoWPAN: received packet fragment"); + + // Add the fragment to the packet assembler. + match frags.add(frag.payload(), offset) { + Ok(true) => { + net_trace!("6LoWPAN: fragmented packet now complete"); + match fragments.get_assembled_packet(&key) { + Ok(packet) => Some(packet), + _ => unreachable!(), + } + } + Ok(false) => None, + Err(Error::PacketAssemblerOverlap) => { + net_trace!("6LoWPAN: overlap in packet"); + frags.mark_discarded(); + None + } + Err(_) => None, + } + } + #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] fn process_arp<'frame, T: AsRef<[u8]>>( &mut self, From 18e26c7f22e582ca447d65390b253acfe6c0c699 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Sep 2022 15:14:23 +0200 Subject: [PATCH 423/566] Remove cfg_if in src/iface/interface --- src/iface/interface.rs | 428 ++++++++++++++++++++++------------------- 1 file changed, 226 insertions(+), 202 deletions(-) diff --git a/src/iface/interface.rs b/src/iface/interface.rs index 9ebd8c38f..14fae7184 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -1696,12 +1696,16 @@ impl<'a> InterfaceInner<'a> { EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - cfg_if::cfg_if! { - if #[cfg(feature = "proto-ipv4-fragmentation")] { + #[cfg(feature = "proto-ipv4-fragmentation")] + { self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - .map(EthernetPacket::Ip) } else { - self.process_ipv4(sockets, &ipv4_packet, None).map(EthernetPacket::Ip) + .map(EthernetPacket::Ip) } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + self.process_ipv4(sockets, &ipv4_packet, None) + .map(EthernetPacket::Ip) } } #[cfg(feature = "proto-ipv6")] @@ -1726,13 +1730,15 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - cfg_if::cfg_if! { - if #[cfg(feature = "proto-ipv4-fragmentation")] { - self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - } else { - self.process_ipv4(sockets, &ipv4_packet, None) - } + #[cfg(feature = "proto-ipv4-fragmentation")] + { + self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) + } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + self.process_ipv4(sockets, &ipv4_packet, None) } } #[cfg(feature = "proto-ipv6")] @@ -1775,12 +1781,22 @@ impl<'a> InterfaceInner<'a> { match ieee802154_frame.payload() { Some(payload) => { - cfg_if::cfg_if! { - if #[cfg(feature = "proto-sixlowpan-fragmentation")] { - self.process_sixlowpan(sockets, &ieee802154_repr, payload, Some((&mut _fragments.sixlowpan_fragments, _fragments.sixlowpan_fragments_cache_timeout))) - } else { - self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) - } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + self.process_sixlowpan( + sockets, + &ieee802154_repr, + payload, + Some(( + &mut _fragments.sixlowpan_fragments, + _fragments.sixlowpan_fragments_cache_timeout, + )), + ) + } + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + { + self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) } } None => None, @@ -3186,80 +3202,82 @@ impl<'a> InterfaceInner<'a> { IpRepr::Ipv4(ref mut repr) => { // If we have an IPv4 packet, then we need to check if we need to fragment it. if total_ip_len > self.caps.max_transmission_unit { - cfg_if::cfg_if! { - if #[cfg(feature = "proto-ipv4-fragmentation")] { - net_debug!("start fragmentation"); - - let Ipv4OutPacket { - buffer, - packet_len, - sent_bytes, - repr: out_packet_repr, - frag_offset, - ident, - dst_hardware_addr: dst_address, - } = &mut _out_packet.unwrap().ipv4_out_packet; - - // Calculate how much we will send now (including the Ethernet header). - let tx_len = self.caps.max_transmission_unit; - - let ip_header_len = repr.buffer_len(); - let first_frag_ip_len = self.caps.ip_mtu(); - - if buffer.len() < first_frag_ip_len { - net_debug!("Fragmentation buffer is too small"); - return Err(Error::Exhausted); - } + #[cfg(feature = "proto-ipv4-fragmentation")] + { + net_debug!("start fragmentation"); + + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr: out_packet_repr, + frag_offset, + ident, + dst_hardware_addr: dst_address, + } = &mut _out_packet.unwrap().ipv4_out_packet; + + // Calculate how much we will send now (including the Ethernet header). + let tx_len = self.caps.max_transmission_unit; + + let ip_header_len = repr.buffer_len(); + let first_frag_ip_len = self.caps.ip_mtu(); + + if buffer.len() < first_frag_ip_len { + net_debug!("Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + + *dst_address = dst_hardware_addr; - *dst_address = dst_hardware_addr; + // Save the total packet len (without the Ethernet header, but with the first + // IP header). + *packet_len = total_ip_len; - // Save the total packet len (without the Ethernet header, but with the first - // IP header). - *packet_len = total_ip_len; + // Save the IP header for other fragments. + *out_packet_repr = *repr; - // Save the IP header for other fragments. - *out_packet_repr = *repr; + // Save how much bytes we will send now. + *sent_bytes = first_frag_ip_len; - // Save how much bytes we will send now. - *sent_bytes = first_frag_ip_len; + // Modify the IP header + repr.payload_len = first_frag_ip_len - repr.buffer_len(); - // Modify the IP header - repr.payload_len = first_frag_ip_len - repr.buffer_len(); + // Emit the IP header to the buffer. + emit_ip(&ip_repr, buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); + *ident = ipv4_id; + ipv4_packet.set_ident(ipv4_id); + ipv4_packet.set_more_frags(true); + ipv4_packet.set_dont_frag(false); + ipv4_packet.set_frag_offset(0); - // Emit the IP header to the buffer. - emit_ip(&ip_repr, buffer); - let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); - *ident = ipv4_id; - ipv4_packet.set_ident(ipv4_id); - ipv4_packet.set_more_frags(true); - ipv4_packet.set_dont_frag(false); - ipv4_packet.set_frag_offset(0); + if caps.checksum.ipv4.tx() { + ipv4_packet.fill_checksum(); + } - if caps.checksum.ipv4.tx() { - ipv4_packet.fill_checksum(); + // Transmit the first packet. + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; } - // Transmit the first packet. - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&ip_repr, tx_buffer)?; - tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; - } - - // Change the offset for the next packet. - *frag_offset = (first_frag_ip_len - ip_header_len) as u16; - - // Copy the IP header and the payload. - tx_buffer[..first_frag_ip_len] - .copy_from_slice(&buffer[..first_frag_ip_len]); - - Ok(()) - }) - } else { - net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); + // Change the offset for the next packet. + *frag_offset = (first_frag_ip_len - ip_header_len) as u16; + + // Copy the IP header and the payload. + tx_buffer[..first_frag_ip_len] + .copy_from_slice(&buffer[..first_frag_ip_len]); + Ok(()) - } + }) + } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); + Ok(()) } } else { // No fragmentation is required. @@ -3382,145 +3400,151 @@ impl<'a> InterfaceInner<'a> { let ieee_len = ieee_repr.buffer_len(); if total_size + ieee_len > 125 { - cfg_if::cfg_if! { - if #[cfg(feature = "proto-sixlowpan-fragmentation")] { - // The packet does not fit in one Ieee802154 frame, so we need fragmentation. - // We do this by emitting everything in the `out_packet.buffer` from the interface. - // After emitting everything into that buffer, we send the first fragment heere. - // When `poll` is called again, we check if out_packet was fully sent, otherwise we - // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. - - // `dispatch_ieee802154_out_packet` requires some information about the total packet size, - // the link local source and destination address... - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - datagram_offset, - .. - } = &mut _out_packet.unwrap().sixlowpan_out_packet; - - if buffer.len() < total_size { - net_debug!("6LoWPAN: Fragmentation buffer is too small"); - return Err(Error::Exhausted); - } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + // The packet does not fit in one Ieee802154 frame, so we need fragmentation. + // We do this by emitting everything in the `out_packet.buffer` from the interface. + // After emitting everything into that buffer, we send the first fragment heere. + // When `poll` is called again, we check if out_packet was fully sent, otherwise we + // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + + // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // the link local source and destination address... + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + datagram_offset, + .. + } = &mut _out_packet.unwrap().sixlowpan_out_packet; + + if buffer.len() < total_size { + net_debug!("6LoWPAN: Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } - *ll_dst_addr = ll_dst_a; - *ll_src_addr = ll_src_a; + *ll_dst_addr = ll_dst_a; + *ll_src_addr = ll_src_a; - let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); - iphc_repr.emit(&mut iphc_packet); + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); - let b = &mut buffer[iphc_repr.buffer_len()..]; + let b = &mut buffer[iphc_repr.buffer_len()..]; - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( - &mut b[..udp_repr.header_len() + payload.len()], - ); - udp_repr.emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - } - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); - tcp_repr.emit( - &mut tcp_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &self.caps.checksum, - ); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - let mut icmp_packet = - Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - } - _ => return Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut b[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } - *packet_len = total_size; + *packet_len = total_size; - // The datagram size that we need to set in the first fragment header is equal to the - // IPv6 payload length + 40. - *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + // The datagram size that we need to set in the first fragment header is equal to the + // IPv6 payload length + 40. + *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; - // We generate a random tag. - let tag = self.get_sixlowpan_fragment_tag(); - // We save the tag for the other fragments that will be created when calling `poll` - // multiple times. - *datagram_tag = tag; + // We generate a random tag. + let tag = self.get_sixlowpan_fragment_tag(); + // We save the tag for the other fragments that will be created when calling `poll` + // multiple times. + *datagram_tag = tag; - let frag1 = SixlowpanFragRepr::FirstFragment { - size: *datagram_size, - tag, - }; - let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, - tag, - offset: 0, - }; + let frag1 = SixlowpanFragRepr::FirstFragment { + size: *datagram_size, + tag, + }; + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag, + offset: 0, + }; - // We calculate how much data we can send in the first fragment and the other - // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight - // (except for the last fragment) since the offset field in the fragment is an offset - // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. - // - // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + // We calculate how much data we can send in the first fragment and the other + // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight + // (except for the last fragment) since the offset field in the fragment is an offset + // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. + // + // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 - let header_diff = _uncompressed_headers_len - _compressed_headers_len; - let frag1_size = - (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); + let header_diff = _uncompressed_headers_len - _compressed_headers_len; + let frag1_size = + (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); - *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; + *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; - *sent_bytes = frag1_size; - *datagram_offset = frag1_size + header_diff; + *sent_bytes = frag1_size; + *datagram_offset = frag1_size + header_diff; - tx_token.consume( - self.now, - ieee_len + frag1.buffer_len() + frag1_size, - |mut tx_buf| { - // Add the IEEE header. - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; + tx_token.consume( + self.now, + ieee_len + frag1.buffer_len() + frag1_size, + |mut tx_buf| { + // Add the IEEE header. + let mut ieee_packet = + Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; - // Add the first fragment header - let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); - frag1.emit(&mut frag1_packet); - tx_buf = &mut tx_buf[frag1.buffer_len()..]; + // Add the first fragment header + let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); + frag1.emit(&mut frag1_packet); + tx_buf = &mut tx_buf[frag1.buffer_len()..]; - // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + // Add the buffer part. + tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); - Ok(()) - }, - ) - } else { - net_debug!("Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support."); - Ok(()) - } + Ok(()) + }, + ) + } + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + { + net_debug!( + "Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support." + ); + Ok(()) } } else { // We don't need fragmentation, so we emit everything to the TX token. From 7633e48db7e225659fc3fc7695561ed98961968a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 10 Oct 2022 12:31:23 +0200 Subject: [PATCH 424/566] Increase version (for making patches work) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 06cde3ace..7f689a466 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smoltcp" -version = "0.8.0" +version = "0.9.0" edition = "2018" rust-version = "1.60" authors = ["whitequark "] From 5ab74b322801c45d1c696441ef7b0f94c733e75b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 10 Oct 2022 15:07:19 +0200 Subject: [PATCH 425/566] use same version number as released --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7f689a466..b27ef5ffd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smoltcp" -version = "0.9.0" +version = "0.8.1" edition = "2018" rust-version = "1.60" authors = ["whitequark "] From 6dca8068f93e4f962e1d83c15220e1cedce1c4c3 Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Mon, 10 Oct 2022 09:48:52 -0400 Subject: [PATCH 426/566] Make public Ipv6RoutingType --- src/wire/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 30f4a8250..41d1f5721 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -192,7 +192,9 @@ pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopR pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr}; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr}; +pub use self::ipv6routing::{ + Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr, Type as Ipv6RoutingType, +}; #[cfg(feature = "proto-ipv4")] pub use self::icmpv4::{ From 185a00829d53fce3ee79d5335bff62195755cdc0 Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Wed, 12 Oct 2022 17:59:27 +0000 Subject: [PATCH 427/566] First pass constification of the wire module --- src/wire/arp.rs | 12 ++++++------ src/wire/dhcpv4.rs | 8 ++++---- src/wire/dns.rs | 6 +++--- src/wire/ethernet.rs | 14 +++++++------- src/wire/icmpv4.rs | 4 ++-- src/wire/icmpv6.rs | 8 ++++---- src/wire/ieee802154.rs | 12 ++++++------ src/wire/igmp.rs | 6 +++--- src/wire/ip.rs | 36 ++++++++++++++++++------------------ src/wire/ipv4.rs | 12 ++++++------ src/wire/ipv6.rs | 16 ++++++++-------- src/wire/ipv6fragment.rs | 4 ++-- src/wire/ipv6hopbyhop.rs | 6 +++--- src/wire/ipv6option.rs | 6 +++--- src/wire/ipv6routing.rs | 8 ++++---- src/wire/mld.rs | 4 ++-- src/wire/mod.rs | 6 +++--- src/wire/ndisc.rs | 2 +- src/wire/ndiscoption.rs | 6 +++--- src/wire/sixlowpan.rs | 12 ++++++------ src/wire/tcp.rs | 12 ++++++------ src/wire/udp.rs | 6 +++--- 22 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/wire/arp.rs b/src/wire/arp.rs index 773c3a9d3..bd83009cf 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -39,25 +39,25 @@ mod field { pub const OPER: Field = 6..8; #[inline] - pub fn SHA(hardware_len: u8, _protocol_len: u8) -> Field { + pub const fn SHA(hardware_len: u8, _protocol_len: u8) -> Field { let start = OPER.end; start..(start + hardware_len as usize) } #[inline] - pub fn SPA(hardware_len: u8, protocol_len: u8) -> Field { + pub const fn SPA(hardware_len: u8, protocol_len: u8) -> Field { let start = SHA(hardware_len, protocol_len).end; start..(start + protocol_len as usize) } #[inline] - pub fn THA(hardware_len: u8, protocol_len: u8) -> Field { + pub const fn THA(hardware_len: u8, protocol_len: u8) -> Field { let start = SPA(hardware_len, protocol_len).end; start..(start + hardware_len as usize) } #[inline] - pub fn TPA(hardware_len: u8, protocol_len: u8) -> Field { + pub const fn TPA(hardware_len: u8, protocol_len: u8) -> Field { let start = THA(hardware_len, protocol_len).end; start..(start + protocol_len as usize) } @@ -65,7 +65,7 @@ mod field { impl> Packet { /// Imbue a raw octet buffer with ARP packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -289,7 +289,7 @@ impl Repr { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match *self { Repr::EthernetIpv4 { .. } => field::TPA(6, 4).end, } diff --git a/src/wire/dhcpv4.rs b/src/wire/dhcpv4.rs index 9e1622d23..40a03c15a 100644 --- a/src/wire/dhcpv4.rs +++ b/src/wire/dhcpv4.rs @@ -44,7 +44,7 @@ bitflags! { } impl MessageType { - fn opcode(&self) -> OpCode { + const fn opcode(&self) -> OpCode { match *self { MessageType::Discover | MessageType::Inform @@ -233,7 +233,7 @@ pub(crate) mod field { impl> Packet { /// Imbue a raw octet buffer with DHCP packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -1143,7 +1143,7 @@ mod test { assert_eq!(packet, DISCOVER_BYTES); } - fn offer_repr() -> Repr<'static> { + const fn offer_repr() -> Repr<'static> { Repr { message_type: MessageType::Offer, transaction_id: 0x3d1d, @@ -1169,7 +1169,7 @@ mod test { } } - fn discover_repr() -> Repr<'static> { + const fn discover_repr() -> Repr<'static> { Repr { message_type: MessageType::Discover, transaction_id: 0x3d1d, diff --git a/src/wire/dns.rs b/src/wire/dns.rs index d29e1d87e..7cd008324 100644 --- a/src/wire/dns.rs +++ b/src/wire/dns.rs @@ -83,7 +83,7 @@ pub struct Packet> { impl> Packet { /// Imbue a raw octet buffer with DNS packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -311,7 +311,7 @@ impl<'a> Question<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { self.name.len() + 4 } @@ -412,7 +412,7 @@ pub struct Repr<'a> { impl<'a> Repr<'a> { /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { field::HEADER_END + self.question.buffer_len() } diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 71911bbba..7c0081460 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -43,7 +43,7 @@ impl Address { } /// Return an Ethernet address as a sequence of octets, in big-endian. - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { &self.0 } @@ -58,12 +58,12 @@ impl Address { } /// Query whether the "multicast" bit in the OUI is set. - pub fn is_multicast(&self) -> bool { + pub const fn is_multicast(&self) -> bool { self.0[0] & 0x01 != 0 } /// Query whether the "locally administered" bit in the OUI is set. - pub fn is_local(&self) -> bool { + pub const fn is_local(&self) -> bool { self.0[0] & 0x02 != 0 } } @@ -100,7 +100,7 @@ pub const HEADER_LEN: usize = field::PAYLOAD.start; impl> Frame { /// Imbue a raw octet buffer with Ethernet frame structure. - pub fn new_unchecked(buffer: T) -> Frame { + pub const fn new_unchecked(buffer: T) -> Frame { Frame { buffer } } @@ -131,13 +131,13 @@ impl> Frame { } /// Return the length of a frame header. - pub fn header_len() -> usize { + pub const fn header_len() -> usize { HEADER_LEN } /// Return the length of a buffer required to hold a packet with the payload /// of a given length. - pub fn buffer_len(payload_len: usize) -> usize { + pub const fn buffer_len(payload_len: usize) -> usize { HEADER_LEN + payload_len } @@ -277,7 +277,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { HEADER_LEN } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index 7d3cf5a4e..c665c5d07 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -184,7 +184,7 @@ mod field { impl> Packet { /// Imbue a raw octet buffer with ICMPv4 packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -468,7 +468,7 @@ impl<'a> Repr<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => { field::ECHO_SEQNO.end + data.len() diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index cfce7656f..bd3246c2f 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -55,7 +55,7 @@ impl Message { /// is an [NDISC] message type. /// /// [NDISC]: https://tools.ietf.org/html/rfc4861 - pub fn is_ndisc(&self) -> bool { + pub const fn is_ndisc(&self) -> bool { match *self { Message::RouterSolicit | Message::RouterAdvert @@ -70,7 +70,7 @@ impl Message { /// is an [MLD] message type. /// /// [MLD]: https://tools.ietf.org/html/rfc3810 - pub fn is_mld(&self) -> bool { + pub const fn is_mld(&self) -> bool { match *self { Message::MldQuery | Message::MldReport => true, _ => false, @@ -247,7 +247,7 @@ pub(super) mod field { impl> Packet { /// Imbue a raw octet buffer with ICMPv6 packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -625,7 +625,7 @@ impl<'a> Repr<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index f9dd98f31..b85a18b19 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -43,7 +43,7 @@ enum_with_unknown! { impl AddressingMode { /// Return the size in octets of the address. - fn size(&self) -> usize { + const fn size(&self) -> usize { match self { AddressingMode::Absent => 0, AddressingMode::Short => 2, @@ -103,11 +103,11 @@ impl Address { *self == Self::BROADCAST } - fn short_from_bytes(a: [u8; 2]) -> Self { + const fn short_from_bytes(a: [u8; 2]) -> Self { Self::Short(a) } - fn extended_from_bytes(a: [u8; 8]) -> Self { + const fn extended_from_bytes(a: [u8; 8]) -> Self { Self::Extended(a) } @@ -125,7 +125,7 @@ impl Address { } } - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { match self { Address::Absent => &[], Address::Short(value) => value, @@ -224,7 +224,7 @@ macro_rules! set_fc_bit_field { impl> Frame { /// Input a raw octet buffer with Ethernet frame structure. - pub fn new_unchecked(buffer: T) -> Frame { + pub const fn new_unchecked(buffer: T) -> Frame { Frame { buffer } } @@ -784,7 +784,7 @@ impl Repr { /// Return the length of a buffer required to hold a packet with the payload of a given length. #[inline] - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { 3 + 2 + match self.dst_addr { Some(Address::Absent) | None => 0, diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 56f362d21..72c87150c 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -54,7 +54,7 @@ impl fmt::Display for Message { /// [RFC 2236]: https://tools.ietf.org/html/rfc2236 impl> Packet { /// Imbue a raw octet buffer with IGMPv2 packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -246,7 +246,7 @@ impl Repr { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { // always 8 bytes field::GROUP_ADDRESS.end } @@ -304,7 +304,7 @@ fn max_resp_code_to_duration(value: u8) -> Duration { Duration::from_millis(decisecs * 100) } -fn duration_to_max_resp_code(duration: Duration) -> u8 { +const fn duration_to_max_resp_code(duration: Duration) -> u8 { let decisecs = duration.total_millis() / 100; if decisecs < 128 { decisecs as u8 diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 519203a4f..5223813ad 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -23,7 +23,7 @@ impl Version { /// /// This function never returns `Ok(IpVersion::Unspecified)`; instead, /// unknown versions result in `Err(Error)`. - pub fn of_packet(data: &[u8]) -> Result { + pub const fn of_packet(data: &[u8]) -> Result { match data[0] >> 4 { #[cfg(feature = "proto-ipv4")] 4 => Ok(Version::Ipv4), @@ -93,7 +93,7 @@ pub enum Address { impl Address { /// Create an address wrapping an IPv4 address with the given octets. #[cfg(feature = "proto-ipv4")] - pub fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address { + pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address { Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3)) } @@ -105,7 +105,7 @@ impl Address { } /// Return the protocol version. - pub fn version(&self) -> Version { + pub const fn version(&self) -> Version { match self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(_) => Version::Ipv4, @@ -115,7 +115,7 @@ impl Address { } /// Return an address as a sequence of octets, in big-endian. - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { match *self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(ref addr) => addr.as_bytes(), @@ -135,7 +135,7 @@ impl Address { } /// Query whether the address is a valid multicast address. - pub fn is_multicast(&self) -> bool { + pub const fn is_multicast(&self) -> bool { match *self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_multicast(), @@ -289,7 +289,7 @@ impl Cidr { } /// Return the IP address of this CIDR block. - pub fn address(&self) -> Address { + pub const fn address(&self) -> Address { match *self { #[cfg(feature = "proto-ipv4")] Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()), @@ -299,7 +299,7 @@ impl Cidr { } /// Return the prefix length of this CIDR block. - pub fn prefix_len(&self) -> u8 { + pub const fn prefix_len(&self) -> u8 { match *self { #[cfg(feature = "proto-ipv4")] Cidr::Ipv4(cidr) => cidr.prefix_len(), @@ -386,7 +386,7 @@ pub struct Endpoint { impl Endpoint { /// Create an endpoint address from given address and port. - pub fn new(addr: Address, port: u16) -> Endpoint { + pub const fn new(addr: Address, port: u16) -> Endpoint { Endpoint { addr: addr, port } } } @@ -457,7 +457,7 @@ pub struct ListenEndpoint { impl ListenEndpoint { /// Query whether the endpoint has a specified address and port. - pub fn is_specified(&self) -> bool { + pub const fn is_specified(&self) -> bool { self.addr.is_some() && self.port != 0 } } @@ -596,7 +596,7 @@ impl Repr { } /// Return the protocol version. - pub fn version(&self) -> Version { + pub const fn version(&self) -> Version { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(_) => Version::Ipv4, @@ -606,7 +606,7 @@ impl Repr { } /// Return the source address. - pub fn src_addr(&self) -> Address { + pub const fn src_addr(&self) -> Address { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr), @@ -616,7 +616,7 @@ impl Repr { } /// Return the destination address. - pub fn dst_addr(&self) -> Address { + pub const fn dst_addr(&self) -> Address { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr), @@ -626,7 +626,7 @@ impl Repr { } /// Return the next header (protocol). - pub fn next_header(&self) -> Protocol { + pub const fn next_header(&self) -> Protocol { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.next_header, @@ -636,7 +636,7 @@ impl Repr { } /// Return the payload length. - pub fn payload_len(&self) -> usize { + pub const fn payload_len(&self) -> usize { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.payload_len, @@ -656,7 +656,7 @@ impl Repr { } /// Return the TTL value. - pub fn hop_limit(&self) -> u8 { + pub const fn hop_limit(&self) -> u8 { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit, @@ -666,7 +666,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn header_len(&self) -> usize { + pub const fn header_len(&self) -> usize { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.buffer_len(), @@ -693,7 +693,7 @@ impl Repr { /// high-level representation. /// /// This is the same as `repr.buffer_len() + repr.payload_len()`. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { self.header_len() + self.payload_len() } } @@ -703,7 +703,7 @@ pub mod checksum { use super::*; - fn propagate_carries(word: u32) -> u16 { + const fn propagate_carries(word: u32) -> u16 { let sum = (word >> 16) + (word & 0xffff); ((sum >> 16) as u16) + (sum as u16) } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index e0e7b5ebc..f49e6ab49 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -67,7 +67,7 @@ impl Address { } /// Return an IPv4 address as a sequence of octets, in big-endian. - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { &self.0 } @@ -192,7 +192,7 @@ impl Cidr { } /// Return the network mask of this IPv4 CIDR. - pub fn netmask(&self) -> Address { + pub const fn netmask(&self) -> Address { if self.prefix_len == 0 { return Address([0, 0, 0, 0]); } @@ -229,7 +229,7 @@ impl Cidr { } /// Return the network block of this IPv4 CIDR. - pub fn network(&self) -> Cidr { + pub const fn network(&self) -> Cidr { let mask = self.netmask().0; let network = [ self.address.0[0] & mask[0], @@ -303,7 +303,7 @@ pub const HEADER_LEN: usize = field::DST_ADDR.end; impl> Packet { /// Imbue a raw octet buffer with IPv4 packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -662,7 +662,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { // We never emit any options. field::DST_ADDR.end } @@ -880,7 +880,7 @@ mod test { static REPR_PAYLOAD_BYTES: [u8; ADDR_SIZE] = [0xaa, 0x00, 0x00, 0xff]; - fn packet_repr() -> Repr { + const fn packet_repr() -> Repr { Repr { src_addr: Address([0x11, 0x12, 0x13, 0x14]), dst_addr: Address([0x21, 0x22, 0x23, 0x24]), diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 8a5586724..988355192 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -116,7 +116,7 @@ impl Address { } /// Return an IPv6 address as a sequence of octets, in big-endian. - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { &self.0 } @@ -130,7 +130,7 @@ impl Address { /// Query whether the IPv6 address is a [multicast address]. /// /// [multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7 - pub fn is_multicast(&self) -> bool { + pub const fn is_multicast(&self) -> bool { self.0[0] == 0xff } @@ -333,12 +333,12 @@ impl Cidr { } /// Return the address of this IPv6 CIDR block. - pub fn address(&self) -> Address { + pub const fn address(&self) -> Address { self.address } /// Return the prefix length of this IPv6 CIDR block. - pub fn prefix_len(&self) -> u8 { + pub const fn prefix_len(&self) -> u8 { self.prefix_len } @@ -425,7 +425,7 @@ pub const HEADER_LEN: usize = field::DST_ADDR.end; impl> Packet { /// Create a raw octet buffer with an IPv6 packet structure. #[inline] - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -464,7 +464,7 @@ impl> Packet { /// Return the header length. #[inline] - pub fn header_len(&self) -> usize { + pub const fn header_len(&self) -> usize { // This is not a strictly necessary function, but it makes // code more readable. field::DST_ADDR.end @@ -670,7 +670,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { // This function is not strictly necessary, but it can make client code more readable. field::DST_ADDR.end } @@ -1055,7 +1055,7 @@ mod test { 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff, ]; - fn packet_repr() -> Repr { + const fn packet_repr() -> Repr { Repr { src_addr: Address([ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index a36a3635b..a6d9d5063 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -36,7 +36,7 @@ mod field { impl> Header { /// Create a raw octet buffer with an IPv6 Fragment Header structure. - pub fn new_unchecked(buffer: T) -> Header { + pub const fn new_unchecked(buffer: T) -> Header { Header { buffer } } @@ -188,7 +188,7 @@ impl Repr { /// Return the length, in bytes, of a header that will be emitted from this high-level /// representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { field::IDENT.end } diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 9bf98ff45..434885f70 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -42,7 +42,7 @@ mod field { // // Length of the header is in 8-octet units, not including the first 8 octets. The first two // octets are the next header type and the header length. - pub fn OPTIONS(length_field: u8) -> Field { + pub const fn OPTIONS(length_field: u8) -> Field { let bytes = length_field as usize * 8 + 8; 2..bytes } @@ -50,7 +50,7 @@ mod field { impl> Header { /// Create a raw octet buffer with an IPv6 Hop-by-Hop Options Header structure. - pub fn new_unchecked(buffer: T) -> Header { + pub const fn new_unchecked(buffer: T) -> Header { Header { buffer } } @@ -183,7 +183,7 @@ impl<'a> Repr<'a> { /// Return the length, in bytes, of a header that will be emitted from this high-level /// representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { field::OPTIONS(self.length).end } diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index fdeb8a1b2..17dfea7f0 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -81,14 +81,14 @@ mod field { // 8-bit unsigned integer. Length of the DATA field of this option, in octets. pub const LENGTH: usize = 1; // Variable-length field. Option-Type-specific data. - pub fn DATA(length: u8) -> Field { + pub const fn DATA(length: u8) -> Field { 2..length as usize + 2 } } impl> Ipv6Option { /// Create a raw octet buffer with an IPv6 Extension Header Option structure. - pub fn new_unchecked(buffer: T) -> Ipv6Option { + pub const fn new_unchecked(buffer: T) -> Ipv6Option { Ipv6Option { buffer } } @@ -245,7 +245,7 @@ impl<'a> Repr<'a> { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match *self { Repr::Pad1 => 1, Repr::PadN(length) => field::DATA(length).end, diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 9decf997b..b781b774c 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -91,7 +91,7 @@ mod field { // // Length of the header is in 8-octet units, not including the first 8 octets. The first four // octets are the next header type, the header length, routing type and segments left. - pub fn DATA(length_field: u8) -> Field { + pub const fn DATA(length_field: u8) -> Field { let bytes = length_field as usize * 8 + 8; 4..bytes } @@ -134,7 +134,7 @@ mod field { // 8-bit field containing the Pad value. pub const PAD: usize = 5; // Variable length field containing addresses - pub fn ADDRESSES(length_field: u8) -> Field { + pub const fn ADDRESSES(length_field: u8) -> Field { let data = DATA(length_field); 8..data.end } @@ -143,7 +143,7 @@ mod field { /// Core getter methods relevant to any routing type. impl> Header { /// Create a raw octet buffer with an IPv6 Routing Header structure. - pub fn new(buffer: T) -> Header { + pub const fn new(buffer: T) -> Header { Header { buffer } } @@ -457,7 +457,7 @@ impl<'a> Repr<'a> { /// Return the length, in bytes, of a header that will be emitted from this high-level /// representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end, } diff --git a/src/wire/mld.rs b/src/wire/mld.rs index 16326f1e1..18872b502 100644 --- a/src/wire/mld.rs +++ b/src/wire/mld.rs @@ -173,7 +173,7 @@ pub struct AddressRecord> { impl> AddressRecord { /// Imbue a raw octet buffer with a Address Record structure. - pub fn new_unchecked(buffer: T) -> Self { + pub const fn new_unchecked(buffer: T) -> Self { Self { buffer } } @@ -338,7 +338,7 @@ impl<'a> Repr<'a> { } /// Return the length of a packet that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { Repr::Query { data, .. } => field::QUERY_NUM_SRCS.end + data.len(), Repr::Report { data, .. } => field::NR_MCAST_RCRDS.end + data.len(), diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 41d1f5721..c0684a9f1 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -284,7 +284,7 @@ pub enum HardwareAddress { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] impl HardwareAddress { - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { match self { #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => addr.as_bytes(), @@ -372,11 +372,11 @@ impl RawHardwareAddress { &self.data[..self.len as usize] } - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.len as usize } - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.len == 0 } diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index 5c7cf4d29..a6a99a1b1 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -351,7 +351,7 @@ impl<'a> Repr<'a> { } } - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { &Repr::RouterSolicit { lladdr } => match lladdr { Some(addr) => { diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 6ced627ea..a990b243d 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -75,7 +75,7 @@ mod field { // Minimum length of an option. pub const MIN_OPT_LEN: usize = 8; // Variable-length field. Option-Type-specific data. - pub fn DATA(length: u8) -> Field { + pub const fn DATA(length: u8) -> Field { 2..length as usize * 8 } @@ -145,7 +145,7 @@ mod field { /// Core getter methods relevant to any type of NDISC option. impl> NdiscOption { /// Create a raw octet buffer with an NDISC Option structure. - pub fn new_unchecked(buffer: T) -> NdiscOption { + pub const fn new_unchecked(buffer: T) -> NdiscOption { NdiscOption { buffer } } @@ -499,7 +499,7 @@ impl<'a> Repr<'a> { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => { let len = 2 + addr.len(); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 42031601a..243b00c89 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -282,7 +282,7 @@ pub mod frag { impl> Packet { /// Input a raw octet buffer with a 6LoWPAN Fragment header structure. - pub fn new_unchecked(buffer: T) -> Self { + pub const fn new_unchecked(buffer: T) -> Self { Self { buffer } } @@ -445,7 +445,7 @@ pub mod frag { } /// Returns the length of the Fragment header. - pub fn buffer_len(&self) -> usize { + pub const fn buffer_len(&self) -> usize { match self { Self::FirstFragment { .. } => field::FIRST_FRAGMENT_REST.start, Self::Fragment { .. } => field::NEXT_FRAGMENT_REST.start, @@ -558,7 +558,7 @@ pub mod iphc { impl> Packet { /// Input a raw octet buffer with a 6LoWPAN IPHC header structure. - pub fn new_unchecked(buffer: T) -> Self { + pub const fn new_unchecked(buffer: T) -> Self { Packet { buffer } } @@ -1526,7 +1526,7 @@ pub mod nhc { impl> ExtHeaderPacket { /// Input a raw octet buffer with a 6LoWPAN NHC Extension Header structure. - pub fn new_unchecked(buffer: T) -> Self { + pub const fn new_unchecked(buffer: T) -> Self { ExtHeaderPacket { buffer } } @@ -1745,7 +1745,7 @@ pub mod nhc { impl> UdpNhcPacket { /// Input a raw octet buffer with a LOWPAN_NHC frame structure for UDP. - pub fn new_unchecked(buffer: T) -> Self { + pub const fn new_unchecked(buffer: T) -> Self { Self { buffer } } @@ -1786,7 +1786,7 @@ pub mod nhc { get_field!(ports_field, 0b11, 0); /// Returns the index of the start of the next header compressed fields. - fn nhc_fields_start(&self) -> usize { + const fn nhc_fields_start(&self) -> usize { 1 } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index b814abc70..ce59df27b 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -87,7 +87,7 @@ mod field { pub const CHECKSUM: Field = 16..18; pub const URGENT: Field = 18..20; - pub fn OPTIONS(length: u8) -> Field { + pub const fn OPTIONS(length: u8) -> Field { URGENT.end..(length as usize) } @@ -113,7 +113,7 @@ pub const HEADER_LEN: usize = field::URGENT.end; impl> Packet { /// Imbue a raw octet buffer with TCP packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -753,7 +753,7 @@ pub enum Control { #[allow(clippy::len_without_is_empty)] impl Control { /// Return the length of a control flag, in terms of sequence space. - pub fn len(self) -> usize { + pub const fn len(self) -> usize { match self { Control::Syn | Control::Fin => 1, _ => 0, @@ -761,7 +761,7 @@ impl Control { } /// Turn the PSH flag into no flag, and keep the rest as-is. - pub fn quash_psh(self) -> Control { + pub const fn quash_psh(self) -> Control { match self { Control::Psh => Control::None, _ => self, @@ -971,12 +971,12 @@ impl<'a> Repr<'a> { } /// Return the length of the segment, in terms of sequence space. - pub fn segment_len(&self) -> usize { + pub const fn segment_len(&self) -> usize { self.payload.len() + self.control.len() } /// Return whether the segment has no flags set (except PSH) and no data. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { match self.control { _ if !self.payload.is_empty() => false, Control::Syn | Control::Fin | Control::Rst => false, diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 3505d84a0..0a7bc49cd 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -23,7 +23,7 @@ mod field { pub const LENGTH: Field = 4..6; pub const CHECKSUM: Field = 6..8; - pub fn PAYLOAD(length: u16) -> Field { + pub const fn PAYLOAD(length: u16) -> Field { CHECKSUM.end..(length as usize) } } @@ -33,7 +33,7 @@ pub const HEADER_LEN: usize = field::CHECKSUM.end; #[allow(clippy::len_without_is_empty)] impl> Packet { /// Imbue a raw octet buffer with UDP packet structure. - pub fn new_unchecked(buffer: T) -> Packet { + pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } @@ -246,7 +246,7 @@ impl Repr { } /// Return the length of the packet header that will be emitted from this high-level representation. - pub fn header_len(&self) -> usize { + pub const fn header_len(&self) -> usize { HEADER_LEN } From 0b17cba60894557720827ac67d6e95354b6b6860 Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Wed, 12 Oct 2022 18:44:04 +0000 Subject: [PATCH 428/566] Bump MSRV to 1.61 --- .github/workflows/test.yml | 4 ++-- README.md | 2 +- src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 72b3fdbab..160f6452f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.60.0 + - 1.61.0 - nightly features: @@ -65,7 +65,7 @@ jobs: # Failure is permitted on nightly. rust: - stable - - 1.60.0 + - 1.61.0 - nightly features: diff --git a/README.md b/README.md index 9c07fb291..042f1c473 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.60 and later. +and compiles on stable Rust 1.61 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. diff --git a/src/lib.rs b/src/lib.rs index 6eb0568dc..08d16c085 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,7 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.60 and up with any valid set of features. +//! This crate is guaranteed to compile on stable Rust 1.61 and up with any valid set of features. //! It *might* compile on older versions but that may change in any new patch release. //! //! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which From a99c059e4f6ec78d71a43c6411c123c285fe6bdc Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Wed, 12 Oct 2022 18:53:29 +0000 Subject: [PATCH 429/566] Set rust-version = 1.61 in Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b27ef5ffd..994b9e644 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "smoltcp" version = "0.8.1" edition = "2018" -rust-version = "1.60" +rust-version = "1.61" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." documentation = "https://docs.rs/smoltcp/" From 20a4ca796b52a445e9f7e586de12895f3d4dd80a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Sun, 23 Oct 2022 15:16:25 +0200 Subject: [PATCH 430/566] Split interface into multiple files --- src/iface/interface.rs | 5178 ------------------------------ src/iface/interface/ethernet.rs | 89 + src/iface/interface/ipv4.rs | 565 ++++ src/iface/interface/ipv6.rs | 308 ++ src/iface/interface/mod.rs | 2279 +++++++++++++ src/iface/interface/sixlowpan.rs | 648 ++++ src/iface/interface/tests.rs | 1369 ++++++++ 7 files changed, 5258 insertions(+), 5178 deletions(-) delete mode 100644 src/iface/interface.rs create mode 100644 src/iface/interface/ethernet.rs create mode 100644 src/iface/interface/ipv4.rs create mode 100644 src/iface/interface/ipv6.rs create mode 100644 src/iface/interface/mod.rs create mode 100644 src/iface/interface/sixlowpan.rs create mode 100644 src/iface/interface/tests.rs diff --git a/src/iface/interface.rs b/src/iface/interface.rs deleted file mode 100644 index 14fae7184..000000000 --- a/src/iface/interface.rs +++ /dev/null @@ -1,5178 +0,0 @@ -// Heads up! Before working on this file you should read the parts -// of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work -// and RFCs 8200 and 4861 for any IPv6 and NDISC work. - -use core::cmp; -use managed::{ManagedMap, ManagedSlice}; - -#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] -use super::fragmentation::PacketAssemblerSet; -use super::socket_set::SocketSet; -use crate::iface::Routes; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] -use crate::iface::{NeighborAnswer, NeighborCache}; -use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; -use crate::rand::Rand; -#[cfg(feature = "socket-dhcpv4")] -use crate::socket::dhcpv4; -#[cfg(feature = "socket-dns")] -use crate::socket::dns; -use crate::socket::*; -use crate::time::{Duration, Instant}; -use crate::wire::*; -use crate::{Error, Result}; - -pub(crate) struct FragmentsBuffer<'a> { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: Duration, - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - _lifetime: core::marker::PhantomData<&'a ()>, -} - -pub(crate) struct OutPackets<'a> { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket<'a>, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket<'a>, - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - _lifetime: core::marker::PhantomData<&'a ()>, -} - -impl<'a> OutPackets<'a> { - #[cfg(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - ))] - /// Returns `true` when all the data of the outgoing buffers are transmitted. - fn all_transmitted(&self) -> bool { - #[cfg(feature = "proto-ipv4-fragmentation")] - if !self.ipv4_out_packet.is_empty() { - return false; - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - if !self.sixlowpan_out_packet.is_empty() { - return false; - } - - true - } -} - -#[allow(unused)] -#[cfg(feature = "proto-ipv4")] -pub(crate) struct Ipv4OutPacket<'a> { - /// The buffer that holds the unfragmented 6LoWPAN packet. - buffer: ManagedSlice<'a, u8>, - /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. - packet_len: usize, - /// The amount of bytes that already have been transmitted. - sent_bytes: usize, - - /// The IPv4 representation. - repr: Ipv4Repr, - /// The destination hardware address. - dst_hardware_addr: EthernetAddress, - /// The offset of the next fragment. - frag_offset: u16, - /// The identifier of the stream. - ident: u16, -} - -#[cfg(feature = "proto-ipv4-fragmentation")] -impl<'a> Ipv4OutPacket<'a> { - pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { - Self { - buffer, - packet_len: 0, - sent_bytes: 0, - repr: Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: Ipv4Address::default(), - next_header: IpProtocol::Unknown(0), - payload_len: 0, - hop_limit: 0, - }, - dst_hardware_addr: EthernetAddress::default(), - frag_offset: 0, - ident: 0, - } - } - - /// Return `true` when everything is transmitted. - #[inline] - fn finished(&self) -> bool { - self.packet_len == self.sent_bytes - } - - /// Returns `true` when there is nothing to transmit. - #[inline] - fn is_empty(&self) -> bool { - self.packet_len == 0 - } - - // Reset the buffer. - fn reset(&mut self) { - self.packet_len = 0; - self.sent_bytes = 0; - self.repr = Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: Ipv4Address::default(), - next_header: IpProtocol::Unknown(0), - payload_len: 0, - hop_limit: 0, - }; - self.dst_hardware_addr = EthernetAddress::default(); - } -} - -#[allow(unused)] -#[cfg(feature = "proto-sixlowpan")] -pub(crate) struct SixlowpanOutPacket<'a> { - /// The buffer that holds the unfragmented 6LoWPAN packet. - buffer: ManagedSlice<'a, u8>, - /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. - packet_len: usize, - /// The amount of bytes that already have been transmitted. - sent_bytes: usize, - - /// The datagram size that is used for the fragmentation headers. - datagram_size: u16, - /// The datagram tag that is used for the fragmentation headers. - datagram_tag: u16, - datagram_offset: usize, - - /// The size of the FRAG_N packets. - fragn_size: usize, - - /// The link layer IEEE802.15.4 source address. - ll_dst_addr: Ieee802154Address, - /// The link layer IEEE802.15.4 source address. - ll_src_addr: Ieee802154Address, -} - -#[cfg(feature = "proto-sixlowpan-fragmentation")] -impl<'a> SixlowpanOutPacket<'a> { - pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { - Self { - buffer, - packet_len: 0, - datagram_size: 0, - datagram_tag: 0, - datagram_offset: 0, - sent_bytes: 0, - fragn_size: 0, - ll_dst_addr: Ieee802154Address::Absent, - ll_src_addr: Ieee802154Address::Absent, - } - } - - /// Return `true` when everything is transmitted. - #[inline] - fn finished(&self) -> bool { - self.packet_len == self.sent_bytes - } - - /// Returns `true` when there is nothing to transmit. - #[inline] - fn is_empty(&self) -> bool { - self.packet_len == 0 - } - - // Reset the buffer. - fn reset(&mut self) { - self.packet_len = 0; - self.datagram_size = 0; - self.datagram_tag = 0; - self.sent_bytes = 0; - self.fragn_size = 0; - self.ll_dst_addr = Ieee802154Address::Absent; - self.ll_src_addr = Ieee802154Address::Absent; - } -} - -macro_rules! check { - ($e:expr) => { - match $e { - Ok(x) => x, - Err(_) => { - // concat!/stringify! doesn't work with defmt macros - #[cfg(not(feature = "defmt"))] - net_trace!(concat!("iface: malformed ", stringify!($e))); - #[cfg(feature = "defmt")] - net_trace!("iface: malformed"); - return Default::default(); - } - } - }; -} - -/// A network interface. -/// -/// The network interface logically owns a number of other data structures; to avoid -/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be -/// a `&mut [T]`, or `Vec` if a heap is available. -pub struct Interface<'a> { - inner: InterfaceInner<'a>, - fragments: FragmentsBuffer<'a>, - out_packets: OutPackets<'a>, -} - -/// The device independent part of an Ethernet network interface. -/// -/// Separating the device from the data required for processing and dispatching makes -/// it possible to borrow them independently. For example, the tx and rx tokens borrow -/// the `device` mutably until they're used, which makes it impossible to call other -/// methods on the `Interface` in this time (since its `device` field is borrowed -/// exclusively). However, it is still possible to call methods on its `inner` field. -pub struct InterfaceInner<'a> { - caps: DeviceCapabilities, - now: Instant, - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option>, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: Option, - #[cfg(feature = "medium-ieee802154")] - sequence_no: u8, - #[cfg(feature = "medium-ieee802154")] - pan_id: Option, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_id: u16, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], - #[cfg(feature = "proto-sixlowpan-fragmentation")] - tag: u16, - ip_addrs: ManagedSlice<'a, IpCidr>, - #[cfg(feature = "proto-ipv4")] - any_ip: bool, - routes: Routes<'a>, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, - /// When to report for (all or) the next multicast group membership via IGMP - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState, - rand: Rand, -} - -/// A builder structure used for creating a network interface. -pub struct InterfaceBuilder<'a> { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: Option, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option>, - #[cfg(feature = "medium-ieee802154")] - pan_id: Option, - ip_addrs: ManagedSlice<'a, IpCidr>, - #[cfg(feature = "proto-ipv4")] - any_ip: bool, - routes: Routes<'a>, - /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, - random_seed: u64, - - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_buffer: ManagedSlice<'a, u8>, - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_buffer_timeout: Duration, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: ManagedSlice<'a, u8>, - - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], -} - -impl<'a> InterfaceBuilder<'a> { - /// Create a builder used for creating a network interface using the - /// given device and address. - #[cfg_attr( - all(feature = "medium-ethernet", not(feature = "proto-sixlowpan")), - doc = r##" -# Examples - -``` -# use std::collections::BTreeMap; -#[cfg(feature = "proto-ipv4-fragmentation")] -use smoltcp::iface::FragmentsCache; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; -# use smoltcp::phy::{Loopback, Medium}; -use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; - -let mut device = // ... -# Loopback::new(Medium::Ethernet); -let hw_addr = // ... -# EthernetAddress::default(); -let neighbor_cache = // ... -# NeighborCache::new(BTreeMap::new()); -# #[cfg(feature = "proto-ipv4-fragmentation")] -# let ipv4_frag_cache = // ... -# FragmentsCache::new(vec![], BTreeMap::new()); -let ip_addrs = // ... -# []; -let builder = InterfaceBuilder::new() - .hardware_addr(hw_addr.into()) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs); - -# #[cfg(feature = "proto-ipv4-fragmentation")] -let builder = builder - .ipv4_reassembly_buffer(ipv4_frag_cache) - .ipv4_fragmentation_buffer(vec![]); - -let iface = builder.finalize(&mut device); -``` - "## - )] - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - InterfaceBuilder { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: None, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: None, - - #[cfg(feature = "medium-ieee802154")] - pan_id: None, - - ip_addrs: ManagedSlice::Borrowed(&mut []), - #[cfg(feature = "proto-ipv4")] - any_ip: false, - routes: Routes::new(ManagedMap::Borrowed(&mut [])), - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), - random_seed: 0, - - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), - - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], - } - } - - /// Set the random seed for this interface. - /// - /// It is strongly recommended that the random seed is different on each boot, - /// to avoid problems with TCP port/sequence collisions. - /// - /// The seed doesn't have to be cryptographically secure. - pub fn random_seed(mut self, random_seed: u64) -> Self { - self.random_seed = random_seed; - self - } - - /// Set the Hardware address the interface will use. See also - /// [hardware_addr]. - /// - /// # Panics - /// This function panics if the address is not unicast. - /// - /// [hardware_addr]: struct.Interface.html#method.hardware_addr - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn hardware_addr(mut self, addr: HardwareAddress) -> Self { - InterfaceInner::check_hardware_addr(&addr); - self.hardware_addr = Some(addr); - self - } - - /// Set the IEEE802.15.4 PAN ID the interface will use. - /// - /// **NOTE**: we use the same PAN ID for destination and source. - #[cfg(feature = "medium-ieee802154")] - pub fn pan_id(mut self, pan_id: Ieee802154Pan) -> Self { - self.pan_id = Some(pan_id); - self - } - - /// Set the IP addresses the interface will use. See also - /// [ip_addrs]. - /// - /// # Panics - /// This function panics if any of the addresses are not unicast. - /// - /// [ip_addrs]: struct.Interface.html#method.ip_addrs - pub fn ip_addrs(mut self, ip_addrs: T) -> Self - where - T: Into>, - { - let ip_addrs = ip_addrs.into(); - InterfaceInner::check_ip_addrs(&ip_addrs); - self.ip_addrs = ip_addrs; - self - } - - /// Enable or disable the AnyIP capability, allowing packets to be received - /// locally on IPv4 addresses other than the interface's configured [ip_addrs]. - /// When AnyIP is enabled and a route prefix in [routes] specifies one of - /// the interface's [ip_addrs] as its gateway, the interface will accept - /// packets addressed to that prefix. - /// - /// # IPv6 - /// - /// This option is not available or required for IPv6 as packets sent to - /// the interface are not filtered by IPv6 address. - /// - /// [routes]: struct.Interface.html#method.routes - /// [ip_addrs]: struct.Interface.html#method.ip_addrs - #[cfg(feature = "proto-ipv4")] - pub fn any_ip(mut self, enabled: bool) -> Self { - self.any_ip = enabled; - self - } - - /// Set the IP routes the interface will use. See also - /// [routes]. - /// - /// [routes]: struct.Interface.html#method.routes - pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a> - where - T: Into>, - { - self.routes = routes.into(); - self - } - - /// Provide storage for multicast groups. - /// - /// Join multicast groups by calling [`join_multicast_group()`] on an `Interface`. - /// Using [`join_multicast_group()`] will send initial membership reports. - /// - /// A previously destroyed interface can be recreated by reusing the multicast group - /// storage, i.e. providing a non-empty storage to `ipv4_multicast_groups()`. - /// Note that this way initial membership reports are **not** sent. - /// - /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group - #[cfg(feature = "proto-igmp")] - pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self - where - T: Into>, - { - self.ipv4_multicast_groups = ipv4_multicast_groups.into(); - self - } - - /// Set the Neighbor Cache the interface will use. - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { - self.neighbor_cache = Some(neighbor_cache); - self - } - - /// Set the IPv4 reassembly buffer the interface will use. - #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { - self.ipv4_fragments = storage; - self - } - - /// Set the IPv4 fragments buffer the interface will use. - #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self - where - T: Into>, - { - self.ipv4_out_buffer = storage.into(); - self - } - - /// Set the address contexts the interface will use. - #[cfg(feature = "proto-sixlowpan")] - pub fn sixlowpan_address_context( - mut self, - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], - ) -> Self { - self.sixlowpan_address_context = sixlowpan_address_context; - self - } - - /// Set the 6LoWPAN reassembly buffer the interface will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_reassembly_buffer( - mut self, - storage: PacketAssemblerSet<'a, SixlowpanFragKey>, - ) -> Self { - self.sixlowpan_fragments = storage; - self - } - - /// Set the timeout value the 6LoWPAN reassembly buffer will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { - if timeout > Duration::from_secs(60) { - net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); - } - self.sixlowpan_reassembly_buffer_timeout = timeout; - self - } - - /// Set the 6LoWPAN fragments buffer the interface will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self - where - T: Into>, - { - self.sixlowpan_out_buffer = storage.into(); - self - } - - /// Create a network interface using the previously provided configuration. - /// - /// # Panics - /// If a required option is not provided, this function will panic. Required - /// options are: - /// - /// - [ethernet_addr] - /// - [neighbor_cache] - /// - /// [ethernet_addr]: #method.ethernet_addr - /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self, device: &mut D) -> Interface<'a> - where - D: for<'d> Device<'d> + ?Sized, - { - let caps = device.capabilities(); - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let (hardware_addr, neighbor_cache) = match caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => ( - Some( - self.hardware_addr - .expect("hardware_addr required option was not set"), - ), - Some( - self.neighbor_cache - .expect("neighbor_cache required option was not set"), - ), - ), - #[cfg(feature = "medium-ip")] - Medium::Ip => { - assert!( - self.hardware_addr.is_none(), - "hardware_addr is set, but device medium is IP" - ); - assert!( - self.neighbor_cache.is_none(), - "neighbor_cache is set, but device medium is IP" - ); - (None, None) - } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => ( - Some( - self.hardware_addr - .expect("hardware_addr required option was not set"), - ), - Some( - self.neighbor_cache - .expect("neighbor_cache required option was not set"), - ), - ), - }; - - let mut rand = Rand::new(self.random_seed); - - #[cfg(feature = "medium-ieee802154")] - let mut sequence_no; - #[cfg(feature = "medium-ieee802154")] - loop { - sequence_no = (rand.rand_u32() & 0xff) as u8; - if sequence_no != 0 { - break; - } - } - - #[cfg(feature = "proto-sixlowpan")] - let mut tag; - - #[cfg(feature = "proto-sixlowpan")] - loop { - tag = rand.rand_u16(); - if tag != 0 { - break; - } - } - - #[cfg(feature = "proto-ipv4")] - let mut ipv4_id; - - #[cfg(feature = "proto-ipv4")] - loop { - ipv4_id = rand.rand_u16(); - if ipv4_id != 0 { - break; - } - } - - Interface { - fragments: FragmentsBuffer { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: self.ipv4_fragments, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: self.sixlowpan_fragments, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, - - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - _lifetime: core::marker::PhantomData, - }, - out_packets: OutPackets { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket::new(self.ipv4_out_buffer), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new(self.sixlowpan_out_buffer), - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - _lifetime: core::marker::PhantomData, - }, - inner: InterfaceInner { - now: Instant::from_secs(0), - caps, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr, - ip_addrs: self.ip_addrs, - #[cfg(feature = "proto-ipv4")] - any_ip: self.any_ip, - routes: self.routes, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: self.ipv4_multicast_groups, - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState::Inactive, - #[cfg(feature = "medium-ieee802154")] - sequence_no, - #[cfg(feature = "medium-ieee802154")] - pan_id: self.pan_id, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - tag, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_id, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], - rand, - }, - } - } -} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[cfg(feature = "medium-ethernet")] -enum EthernetPacket<'a> { - #[cfg(feature = "proto-ipv4")] - Arp(ArpRepr), - Ip(IpPacket<'a>), -} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) enum IpPacket<'a> { - #[cfg(feature = "proto-ipv4")] - Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)), - #[cfg(feature = "proto-igmp")] - Igmp((Ipv4Repr, IgmpRepr)), - #[cfg(feature = "proto-ipv6")] - Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)), - #[cfg(feature = "socket-raw")] - Raw((IpRepr, &'a [u8])), - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - Udp((IpRepr, UdpRepr, &'a [u8])), - #[cfg(feature = "socket-tcp")] - Tcp((IpRepr, TcpRepr<'a>)), - #[cfg(feature = "socket-dhcpv4")] - Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)), -} - -impl<'a> IpPacket<'a> { - pub(crate) fn ip_repr(&self) -> IpRepr { - match self { - #[cfg(feature = "proto-ipv4")] - IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), - #[cfg(feature = "proto-igmp")] - IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr), - #[cfg(feature = "socket-raw")] - IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), - #[cfg(feature = "socket-dhcpv4")] - IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr), - } - } - - pub(crate) fn emit_payload( - &self, - _ip_repr: &IpRepr, - payload: &mut [u8], - caps: &DeviceCapabilities, - ) { - match self { - #[cfg(feature = "proto-ipv4")] - IpPacket::Icmpv4((_, icmpv4_repr)) => { - icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum) - } - #[cfg(feature = "proto-igmp")] - IpPacket::Igmp((_, igmp_repr)) => { - igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)) - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit( - &_ip_repr.src_addr(), - &_ip_repr.dst_addr(), - &mut Icmpv6Packet::new_unchecked(payload), - &caps.checksum, - ), - #[cfg(feature = "socket-raw")] - IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit( - &mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), - &_ip_repr.dst_addr(), - inner_payload.len(), - |buf| buf.copy_from_slice(inner_payload), - &caps.checksum, - ), - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, mut tcp_repr)) => { - // This is a terrible hack to make TCP performance more acceptable on systems - // where the TCP buffers are significantly larger than network buffers, - // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window) - // together with four 1500 B Ethernet receive buffers. If left untreated, - // this would result in our peer pushing our window and sever packet loss. - // - // I'm really not happy about this "solution" but I don't know what else to do. - if let Some(max_burst_size) = caps.max_burst_size { - let mut max_segment_size = caps.max_transmission_unit; - max_segment_size -= _ip_repr.header_len(); - max_segment_size -= tcp_repr.header_len(); - - let max_window_size = max_burst_size * max_segment_size; - if tcp_repr.window_len as usize > max_window_size { - tcp_repr.window_len = max_window_size as u16; - } - } - - tcp_repr.emit( - &mut TcpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), - &_ip_repr.dst_addr(), - &caps.checksum, - ); - } - #[cfg(feature = "socket-dhcpv4")] - IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit( - &mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), - &_ip_repr.dst_addr(), - dhcp_repr.buffer_len(), - |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), - &caps.checksum, - ), - } - } -} - -#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] -fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize { - // Send back as much of the original payload as will fit within - // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for - // more details. - // - // Since the entire network layer packet must fit within the minimum - // MTU supported, the payload must not exceed the following: - // - // - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size - cmp::min(len, mtu - header_len * 2 - 8) -} - -#[cfg(feature = "proto-igmp")] -enum IgmpReportState { - Inactive, - ToGeneralQuery { - version: IgmpVersion, - timeout: Instant, - interval: Duration, - next_index: usize, - }, - ToSpecificQuery { - version: IgmpVersion, - timeout: Instant, - group: Ipv4Address, - }, -} - -impl<'a> Interface<'a> { - /// Get the socket context. - /// - /// The context is needed for some socket methods. - pub fn context(&mut self) -> &mut InterfaceInner<'a> { - &mut self.inner - } - - /// Get the HardwareAddress address of the interface. - /// - /// # Panics - /// This function panics if the medium is not Ethernet or Ieee802154. - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn hardware_addr(&self) -> HardwareAddress { - #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] - assert!(self.inner.caps.medium == Medium::Ethernet); - #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] - assert!(self.inner.caps.medium == Medium::Ieee802154); - - #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] - assert!( - self.inner.caps.medium == Medium::Ethernet - || self.inner.caps.medium == Medium::Ieee802154 - ); - - self.inner.hardware_addr.unwrap() - } - - /// Set the HardwareAddress address of the interface. - /// - /// # Panics - /// This function panics if the address is not unicast, and if the medium is not Ethernet or - /// Ieee802154. - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn set_hardware_addr(&mut self, addr: HardwareAddress) { - #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] - assert!(self.inner.caps.medium == Medium::Ethernet); - #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] - assert!(self.inner.caps.medium == Medium::Ieee802154); - - #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] - assert!( - self.inner.caps.medium == Medium::Ethernet - || self.inner.caps.medium == Medium::Ieee802154 - ); - - InterfaceInner::check_hardware_addr(&addr); - self.inner.hardware_addr = Some(addr); - } - - /// Add an address to a list of subscribed multicast IP addresses. - /// - /// 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>( - &mut self, - device: &mut D, - addr: T, - timestamp: Instant, - ) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - self.inner.now = timestamp; - - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(addr) => { - let is_not_new = self - .inner - .ipv4_multicast_groups - .insert(addr, ()) - .map_err(|_| Error::Exhausted)? - .is_some(); - if is_not_new { - Ok(false) - } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) - { - // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - Ok(true) - } else { - Ok(false) - } - } - // Multicast is not yet implemented for other address families - #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), - } - } - - /// Remove an address from the subscribed multicast IP addresses. - /// - /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` - /// indicates whether an immediate leave packet has been sent. - pub fn leave_multicast_group>( - &mut self, - device: &mut D, - addr: T, - timestamp: Instant, - ) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - self.inner.now = timestamp; - - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(addr) => { - let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none(); - if was_not_present { - Ok(false) - } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { - // Send group leave packet - let tx_token = device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - Ok(true) - } else { - Ok(false) - } - } - // Multicast is not yet implemented for other address families - #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), - } - } - - /// Check whether the interface listens to given destination multicast IP address. - pub fn has_multicast_group>(&self, addr: T) -> bool { - self.inner.has_multicast_group(addr) - } - - /// Get the IP addresses of the interface. - pub fn ip_addrs(&self) -> &[IpCidr] { - self.inner.ip_addrs.as_ref() - } - - /// Get the first IPv4 address if present. - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_addr(&self) -> Option { - self.ip_addrs() - .iter() - .find_map(|cidr| match cidr.address() { - IpAddress::Ipv4(addr) => Some(addr), - #[allow(unreachable_patterns)] - _ => None, - }) - } - - /// Update the IP addresses of the interface. - /// - /// # Panics - /// This function panics if any of the addresses are not unicast. - pub fn update_ip_addrs)>(&mut self, f: F) { - f(&mut self.inner.ip_addrs); - InterfaceInner::flush_cache(&mut self.inner); - InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) - } - - /// Check whether the interface has the given IP address assigned. - pub fn has_ip_addr>(&self, addr: T) -> bool { - self.inner.has_ip_addr(addr) - } - - /// Get the first IPv4 address of the interface. - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_address(&self) -> Option { - self.inner.ipv4_address() - } - - pub fn routes(&self) -> &Routes<'a> { - &self.inner.routes - } - - pub fn routes_mut(&mut self) -> &mut Routes<'a> { - &mut self.inner.routes - } - - /// Transmit packets queued in the given sockets, and receive packets queued - /// in the device. - /// - /// This function returns a boolean value indicating whether any packets were - /// processed or emitted, and thus, whether the readiness of any socket might - /// have changed. - /// - /// # Errors - /// This method will routinely return errors in response to normal network - /// activity as well as certain boundary conditions such as buffer exhaustion. - /// These errors are provided as an aid for troubleshooting, and are meant - /// to be logged and ignored. - /// - /// As a special case, `Err(Error::Unrecognized)` is returned in response to - /// packets containing any unsupported protocol, option, or form, which is - /// a very common occurrence and on a production system it should not even - /// be logged. - pub fn poll( - &mut self, - timestamp: Instant, - device: &mut D, - sockets: &mut SocketSet<'_>, - ) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - self.inner.now = timestamp; - - #[cfg(feature = "proto-ipv4-fragmentation")] - self.fragments - .ipv4_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - self.fragments - .sixlowpan_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; - - #[cfg(feature = "proto-ipv4-fragmentation")] - match self.ipv4_egress(device) { - Ok(true) => return Ok(true), - Err(e) => { - net_debug!("failed to transmit: {}", e); - return Err(e); - } - _ => (), - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - match self.sixlowpan_egress(device) { - Ok(true) => return Ok(true), - Err(e) => { - net_debug!("failed to transmit: {}", e); - return Err(e); - } - _ => (), - } - - let mut readiness_may_have_changed = false; - - loop { - let processed_any = self.socket_ingress(device, sockets); - let emitted_any = self.socket_egress(device, sockets); - - #[cfg(feature = "proto-igmp")] - self.igmp_egress(device)?; - - if processed_any || emitted_any { - readiness_may_have_changed = true; - } else { - break; - } - } - - Ok(readiness_may_have_changed) - } - - /// Return a _soft deadline_ for calling [poll] the next time. - /// The [Instant] returned is the time at which you should call [poll] next. - /// It is harmless (but wastes energy) to call it before the [Instant], and - /// potentially harmful (impacting quality of service) to call it after the - /// [Instant] - /// - /// [poll]: #method.poll - /// [Instant]: struct.Instant.html - pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { - self.inner.now = timestamp; - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - if !self.out_packets.all_transmitted() { - return Some(Instant::from_millis(0)); - } - - let inner = &mut self.inner; - - sockets - .items() - .filter_map(move |item| { - let socket_poll_at = item.socket.poll_at(inner); - match item - .meta - .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr)) - { - PollAt::Ingress => None, - PollAt::Time(instant) => Some(instant), - PollAt::Now => Some(Instant::from_millis(0)), - } - }) - .min() - } - - /// Return an _advisory wait time_ for calling [poll] the next time. - /// The [Duration] returned is the time left to wait before calling [poll] next. - /// It is harmless (but wastes energy) to call it before the [Duration] has passed, - /// and potentially harmful (impacting quality of service) to call it after the - /// [Duration] has passed. - /// - /// [poll]: #method.poll - /// [Duration]: struct.Duration.html - pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { - match self.poll_at(timestamp, sockets) { - Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), - Some(_) => Some(Duration::from_millis(0)), - _ => None, - } - } - - fn socket_ingress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool - where - D: for<'d> Device<'d> + ?Sized, - { - let mut processed_any = false; - let Self { - inner, - fragments: ref mut _fragments, - out_packets: _out_packets, - } = self; - - while let Some((rx_token, tx_token)) = device.receive() { - let res = rx_token.consume(inner.now, |frame| { - match inner.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch(tx_token, packet, Some(_out_packets)) { - net_debug!("Failed to send response: {}", err); - } - } - } - #[cfg(feature = "medium-ip")] - Medium::Ip => { - if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { - if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(_out_packets)) - { - net_debug!("Failed to send response: {}", err); - } - } - } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - if let Some(packet) = inner.process_ieee802154(sockets, &frame, _fragments) - { - if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(_out_packets)) - { - net_debug!("Failed to send response: {}", err); - } - } - } - } - processed_any = true; - Ok(()) - }); - - if let Err(err) = res { - net_debug!("Failed to consume RX token: {}", err); - } - } - - processed_any - } - - fn socket_egress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool - where - D: for<'d> Device<'d> + ?Sized, - { - let Self { - inner, - out_packets: _out_packets, - .. - } = self; - let _caps = device.capabilities(); - - let mut emitted_any = false; - for item in sockets.items_mut() { - if !item - .meta - .egress_permitted(inner.now, |ip_addr| inner.has_neighbor(&ip_addr)) - { - continue; - } - - let mut neighbor_addr = None; - let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { - neighbor_addr = Some(response.ip_repr().dst_addr()); - let t = device.transmit().ok_or_else(|| { - net_debug!("failed to transmit IP: {}", Error::Exhausted); - Error::Exhausted - })?; - - #[cfg(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - ))] - inner.dispatch_ip(t, response, Some(_out_packets))?; - - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - inner.dispatch_ip(t, response, None)?; - - emitted_any = true; - - Ok(()) - }; - - let result = match &mut item.socket { - #[cfg(feature = "socket-raw")] - Socket::Raw(socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Raw(response)) - }), - #[cfg(feature = "socket-icmp")] - Socket::Icmp(socket) => socket.dispatch(inner, |inner, response| match response { - #[cfg(feature = "proto-ipv4")] - (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { - respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) - } - #[cfg(feature = "proto-ipv6")] - (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { - respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) - } - #[allow(unreachable_patterns)] - _ => unreachable!(), - }), - #[cfg(feature = "socket-udp")] - Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) - }), - #[cfg(feature = "socket-tcp")] - Socket::Tcp(socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Tcp(response)) - }), - #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Dhcpv4(response)) - }), - #[cfg(feature = "socket-dns")] - Socket::Dns(ref mut socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) - }), - }; - - match result { - Err(Error::Exhausted) => break, // Device buffer full. - Err(Error::Unaddressable) => { - // `NeighborCache` already takes care of rate limiting the neighbor discovery - // requests from the socket. However, without an additional rate limiting - // mechanism, we would spin on every socket that has yet to discover its - // neighbor. - item.meta.neighbor_missing( - inner.now, - neighbor_addr.expect("non-IP response packet"), - ); - break; - } - Err(err) => { - net_debug!( - "{}: cannot dispatch egress packet: {}", - item.meta.handle, - err - ); - } - Ok(()) => {} - } - } - emitted_any - } - - /// Depending on `igmp_report_state` and the therein contained - /// timeouts, send IGMP membership reports. - #[cfg(feature = "proto-igmp")] - fn igmp_egress(&mut self, device: &mut D) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - match self.inner.igmp_report_state { - IgmpReportState::ToSpecificQuery { - version, - timeout, - group, - } if self.inner.now >= timeout => { - if let Some(pkt) = self.inner.igmp_report_packet(version, group) { - // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - } - - self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(true) - } - IgmpReportState::ToGeneralQuery { - version, - timeout, - interval, - next_index, - } if self.inner.now >= timeout => { - let addr = self - .inner - .ipv4_multicast_groups - .iter() - .nth(next_index) - .map(|(addr, ())| *addr); - - match addr { - Some(addr) => { - if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { - // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - } - - let next_timeout = (timeout + interval).max(self.inner.now); - self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, - timeout: next_timeout, - interval, - next_index: next_index + 1, - }; - Ok(true) - } - - None => { - self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(false) - } - } - } - _ => Ok(false), - } - } - - /// Process fragments that still need to be sent for IPv4 packets. - /// - /// This function returns a boolean value indicating whether any packets were - /// processed or emitted, and thus, whether the readiness of any socket might - /// have changed. - #[cfg(feature = "proto-ipv4-fragmentation")] - fn ipv4_egress(&mut self, device: &mut D) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - // Reset the buffer when we transmitted everything. - if self.out_packets.ipv4_out_packet.finished() { - self.out_packets.ipv4_out_packet.reset(); - } - - if self.out_packets.ipv4_out_packet.is_empty() { - return Ok(false); - } - - let Ipv4OutPacket { - packet_len, - sent_bytes, - .. - } = &self.out_packets.ipv4_out_packet; - - if *packet_len > *sent_bytes { - match device.transmit() { - Some(tx_token) => self - .inner - .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet), - None => Err(Error::Exhausted), - } - .map(|_| true) - } else { - Ok(false) - } - } - - /// Process fragments that still need to be sent for 6LoWPAN packets. - /// - /// This function returns a boolean value indicating whether any packets were - /// processed or emitted, and thus, whether the readiness of any socket might - /// have changed. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - fn sixlowpan_egress(&mut self, device: &mut D) -> Result - where - D: for<'d> Device<'d> + ?Sized, - { - // Reset the buffer when we transmitted everything. - if self.out_packets.sixlowpan_out_packet.finished() { - self.out_packets.sixlowpan_out_packet.reset(); - } - - if self.out_packets.sixlowpan_out_packet.is_empty() { - return Ok(false); - } - - let SixlowpanOutPacket { - packet_len, - sent_bytes, - .. - } = &self.out_packets.sixlowpan_out_packet; - - if *packet_len > *sent_bytes { - match device.transmit() { - Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( - tx_token, - &mut self.out_packets.sixlowpan_out_packet, - ), - None => Err(Error::Exhausted), - } - .map(|_| true) - } else { - Ok(false) - } - } -} - -impl<'a> InterfaceInner<'a> { - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn now(&self) -> Instant { - self.now - } - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn hardware_addr(&self) -> Option { - self.hardware_addr - } - - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities { - self.caps.checksum.clone() - } - - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn ip_mtu(&self) -> usize { - self.caps.ip_mtu() - } - - #[allow(unused)] // unused depending on which sockets are enabled, and in tests - pub(crate) fn rand(&mut self) -> &mut Rand { - &mut self.rand - } - - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { - let v = dst_addr.version(); - for cidr in self.ip_addrs.iter() { - let addr = cidr.address(); - if addr.version() == v { - return Some(addr); - } - } - None - } - - #[cfg(feature = "proto-ipv4")] - #[allow(unused)] - pub(crate) fn get_source_address_ipv4( - &mut self, - _dst_addr: Ipv4Address, - ) -> Option { - for cidr in self.ip_addrs.iter() { - #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled - if let IpCidr::Ipv4(cidr) = cidr { - return Some(cidr.address()); - } - } - None - } - - #[cfg(feature = "proto-ipv6")] - #[allow(unused)] - pub(crate) fn get_source_address_ipv6( - &mut self, - _dst_addr: Ipv6Address, - ) -> Option { - for cidr in self.ip_addrs.iter() { - #[allow(irrefutable_let_patterns)] // if only ipv6 is enabled - if let IpCidr::Ipv6(cidr) = cidr { - return Some(cidr.address()); - } - } - None - } - - #[cfg(test)] - pub(crate) fn mock() -> Self { - Self { - 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, - }, - now: Instant::from_millis_const(0), - - ip_addrs: ManagedSlice::Owned(vec![ - #[cfg(feature = "proto-ipv4")] - IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(Ipv6Cidr::new( - Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), - 64, - )), - ]), - rand: Rand::new(1234), - routes: Routes::new(&mut [][..]), - - #[cfg(feature = "proto-ipv4")] - any_ip: false, - - #[cfg(feature = "medium-ieee802154")] - pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), - #[cfg(feature = "medium-ieee802154")] - sequence_no: 1, - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - tag: 1, - - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], - - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_id: 1, - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( - crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), - )), - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: None, - - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState::Inactive, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), - } - } - - #[cfg(test)] - #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn set_now(&mut self, now: Instant) { - self.now = now - } - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - fn check_hardware_addr(addr: &HardwareAddress) { - if !addr.is_unicast() { - panic!("Ethernet address {} is not unicast", addr) - } - } - - fn check_ip_addrs(addrs: &[IpCidr]) { - for cidr in addrs { - if !cidr.address().is_unicast() && !cidr.address().is_unspecified() { - panic!("IP address {} is not unicast", cidr.address()) - } - } - } - - #[cfg(feature = "medium-ieee802154")] - fn get_sequence_number(&mut self) -> u8 { - let no = self.sequence_no; - self.sequence_no = self.sequence_no.wrapping_add(1); - no - } - - #[cfg(feature = "proto-ipv4-fragmentation")] - fn get_ipv4_ident(&mut self) -> u16 { - let ipv4_id = self.ipv4_id; - self.ipv4_id = self.ipv4_id.wrapping_add(1); - ipv4_id - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - fn get_sixlowpan_fragment_tag(&mut self) -> u16 { - let tag = self.tag; - self.tag = self.tag.wrapping_add(1); - tag - } - - /// Determine if the given `Ipv6Address` is the solicited node - /// multicast address for a IPv6 addresses assigned to the interface. - /// See [RFC 4291 § 2.7.1] for more details. - /// - /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1 - #[cfg(feature = "proto-ipv6")] - pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool { - self.ip_addrs.iter().any(|cidr| { - match *cidr { - IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => { - // Take the lower order 24 bits of the IPv6 address and - // append those bits to FF02:0:0:0:0:1:FF00::/104. - addr.as_bytes()[14..] == cidr.address().as_bytes()[14..] - } - _ => false, - } - }) - } - - /// Check whether the interface has the given IP address assigned. - fn has_ip_addr>(&self, addr: T) -> bool { - let addr = addr.into(); - self.ip_addrs.iter().any(|probe| probe.address() == addr) - } - - /// Get the first IPv4 address of the interface. - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_address(&self) -> Option { - self.ip_addrs.iter().find_map(|addr| match *addr { - IpCidr::Ipv4(cidr) => Some(cidr.address()), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None, - }) - } - - /// Check whether the interface listens to given destination multicast IP address. - /// - /// If built without feature `proto-igmp` this function will - /// always return `false`. - pub fn has_multicast_group>(&self, addr: T) -> bool { - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(key) => { - key == Ipv4Address::MULTICAST_ALL_SYSTEMS - || self.ipv4_multicast_groups.get(&key).is_some() - } - #[allow(unreachable_patterns)] - _ => false, - } - } - - #[cfg(feature = "medium-ethernet")] - fn process_ethernet<'frame, T: AsRef<[u8]>>( - &mut self, - sockets: &mut SocketSet, - frame: &'frame T, - _fragments: &'frame mut FragmentsBuffer<'a>, - ) -> Option> { - let eth_frame = check!(EthernetFrame::new_checked(frame)); - - // Ignore any packets not directed to our hardware address or any of the multicast groups. - if !eth_frame.dst_addr().is_broadcast() - && !eth_frame.dst_addr().is_multicast() - && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr.unwrap() - { - return None; - } - - match eth_frame.ethertype() { - #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Arp => self.process_arp(self.now, ð_frame), - #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Ipv4 => { - let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - - #[cfg(feature = "proto-ipv4-fragmentation")] - { - self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - .map(EthernetPacket::Ip) - } - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - { - self.process_ipv4(sockets, &ipv4_packet, None) - .map(EthernetPacket::Ip) - } - } - #[cfg(feature = "proto-ipv6")] - EthernetProtocol::Ipv6 => { - let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload())); - self.process_ipv6(sockets, &ipv6_packet) - .map(EthernetPacket::Ip) - } - // Drop all other traffic. - _ => None, - } - } - - #[cfg(feature = "medium-ip")] - fn process_ip<'frame, T: AsRef<[u8]>>( - &mut self, - sockets: &mut SocketSet, - ip_payload: &'frame T, - _fragments: &'frame mut FragmentsBuffer<'a>, - ) -> Option> { - match IpVersion::of_packet(ip_payload.as_ref()) { - #[cfg(feature = "proto-ipv4")] - Ok(IpVersion::Ipv4) => { - let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - - #[cfg(feature = "proto-ipv4-fragmentation")] - { - self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - } - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - { - self.process_ipv4(sockets, &ipv4_packet, None) - } - } - #[cfg(feature = "proto-ipv6")] - Ok(IpVersion::Ipv6) => { - let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload)); - self.process_ipv6(sockets, &ipv6_packet) - } - // Drop all other traffic. - _ => None, - } - } - - #[cfg(feature = "medium-ieee802154")] - fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( - &mut self, - sockets: &mut SocketSet, - sixlowpan_payload: &'payload T, - _fragments: &'output mut FragmentsBuffer<'a>, - ) -> Option> { - let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); - let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); - - if ieee802154_repr.frame_type != Ieee802154FrameType::Data { - return None; - } - - // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this - // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. - // We always accept the broadcast PAN id. - if self.pan_id.is_some() - && ieee802154_repr.dst_pan_id != self.pan_id - && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) - { - net_debug!( - "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)", - ieee802154_repr - ); - return None; - } - - match ieee802154_frame.payload() { - Some(payload) => { - #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - self.process_sixlowpan( - sockets, - &ieee802154_repr, - payload, - Some(( - &mut _fragments.sixlowpan_fragments, - _fragments.sixlowpan_fragments_cache_timeout, - )), - ) - } - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - { - self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) - } - } - None => None, - } - } - - #[cfg(feature = "proto-sixlowpan")] - fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( - &mut self, - sockets: &mut SocketSet, - ieee802154_repr: &Ieee802154Repr, - payload: &'payload T, - _fragments: Option<( - &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, - Duration, - )>, - ) -> Option> { - let payload = match check!(SixlowpanPacket::dispatch(payload)) { - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - SixlowpanPacket::FragmentHeader => { - net_debug!("Fragmentation is not supported, use the `proto-sixlowpan-fragmentation` feature to add support."); - return None; - } - #[cfg(feature = "proto-sixlowpan-fragmentation")] - SixlowpanPacket::FragmentHeader => { - match self.process_sixlowpan_fragment(ieee802154_repr, payload, _fragments) { - Some(payload) => payload, - None => return None, - } - } - SixlowpanPacket::IphcHeader => payload.as_ref(), - }; - - // At this point we should have a valid 6LoWPAN packet. - // The first header needs to be an IPHC header. - let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload)); - let iphc_repr = check!(SixlowpanIphcRepr::parse( - &iphc_packet, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - self.sixlowpan_address_context, - )); - - let payload = iphc_packet.payload(); - let mut ipv6_repr = Ipv6Repr { - src_addr: iphc_repr.src_addr, - dst_addr: iphc_repr.dst_addr, - hop_limit: iphc_repr.hop_limit, - next_header: IpProtocol::Unknown(0), - payload_len: 40, - }; - - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(payload)) { - SixlowpanNhcPacket::ExtHeader => { - net_debug!("Extension headers are currently not supported for 6LoWPAN"); - None - } - #[cfg(not(feature = "socket-udp"))] - SixlowpanNhcPacket::UdpHeader => { - net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); - None - } - #[cfg(feature = "socket-udp")] - SixlowpanNhcPacket::UdpHeader => { - let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload)); - ipv6_repr.next_header = IpProtocol::Udp; - ipv6_repr.payload_len += 8 + udp_packet.payload().len(); - - let udp_repr = check!(SixlowpanUdpNhcRepr::parse( - &udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr - )); - - self.process_udp( - sockets, - IpRepr::Ipv6(ipv6_repr), - udp_repr.0, - false, - udp_packet.payload(), - payload, - ) - } - } - } - SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { - IpProtocol::Icmpv6 => { - ipv6_repr.next_header = IpProtocol::Icmpv6; - self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) - } - #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => { - ipv6_repr.next_header = nxt_hdr; - ipv6_repr.payload_len += payload.len(); - self.process_tcp(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) - } - proto => { - net_debug!("6LoWPAN: {} currently not supported", proto); - None - } - }, - } - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - fn process_sixlowpan_fragment<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( - &mut self, - ieee802154_repr: &Ieee802154Repr, - payload: &'payload T, - fragments: Option<( - &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, - Duration, - )>, - ) -> Option<&'output [u8]> { - let (fragments, timeout) = fragments.unwrap(); - - // We have a fragment header, which means we cannot process the 6LoWPAN packet, - // unless we have a complete one after processing this fragment. - let frag = check!(SixlowpanFragPacket::new_checked(payload)); - - // The key specifies to which 6LoWPAN fragment it belongs too. - // It is based on the link layer addresses, the tag and the size. - let key = frag.get_key(ieee802154_repr); - - // The offset of this fragment in increments of 8 octets. - let offset = frag.datagram_offset() as usize * 8; - - if frag.is_first_fragment() { - // The first fragment contains the total size of the IPv6 packet. - // However, we received a packet that is compressed following the 6LoWPAN - // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN - // packet size. The packet size can be different because of first the - // compression of the IP header and when UDP is used (because the UDP header - // can also be compressed). Other headers are not compressed by 6LoWPAN. - - let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); - let iphc_repr = check!(SixlowpanIphcRepr::parse( - &iphc, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - self.sixlowpan_address_context, - )); - - // The uncompressed header size always starts with 40, since this is the size - // of a IPv6 header. - let mut uncompressed_header_size = 40; - let mut compressed_header_size = iphc.header_len(); - - // We need to check if we have an UDP packet, since this header can also be - // compressed by 6LoWPAN. We currently don't support extension headers yet. - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { - SixlowpanNhcPacket::ExtHeader => { - net_debug!("6LoWPAN: extension headers not supported"); - return None; - } - SixlowpanNhcPacket::UdpHeader => { - let udp_packet = - check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); - - uncompressed_header_size += 8; - compressed_header_size += - 1 + udp_packet.ports_size() + udp_packet.checksum_size(); - } - } - } - SixlowpanNextHeader::Uncompressed(_) => (), - } - - // We reserve a spot in the packet assembler set and add the required - // information to the packet assembler. - // This information is the total size of the packet when it is fully assmbled. - // We also pass the header size, since this is needed when other fragments - // (other than the first one) are added. - let frag_slot = match fragments.reserve_with_key(&key) { - Ok(frag) => frag, - Err(Error::PacketAssemblerSetFull) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); - } - e => check!(e), - }; - - check!(frag_slot.start( - Some( - frag.datagram_size() as usize - uncompressed_header_size - + compressed_header_size - ), - self.now + timeout, - -((uncompressed_header_size - compressed_header_size) as isize), - )); - } - - let frags = check!(fragments.get_packet_assembler_mut(&key)); - - net_trace!("6LoWPAN: received packet fragment"); - - // Add the fragment to the packet assembler. - match frags.add(frag.payload(), offset) { - Ok(true) => { - net_trace!("6LoWPAN: fragmented packet now complete"); - match fragments.get_assembled_packet(&key) { - Ok(packet) => Some(packet), - _ => unreachable!(), - } - } - Ok(false) => None, - Err(Error::PacketAssemblerOverlap) => { - net_trace!("6LoWPAN: overlap in packet"); - frags.mark_discarded(); - None - } - Err(_) => None, - } - } - - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] - fn process_arp<'frame, T: AsRef<[u8]>>( - &mut self, - timestamp: Instant, - eth_frame: &EthernetFrame<&'frame T>, - ) -> Option> { - let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload())); - let arp_repr = check!(ArpRepr::parse(&arp_packet)); - - match arp_repr { - ArpRepr::EthernetIpv4 { - operation, - source_hardware_addr, - source_protocol_addr, - target_protocol_addr, - .. - } => { - // Only process ARP packets for us. - if !self.has_ip_addr(target_protocol_addr) { - return None; - } - - // Only process REQUEST and RESPONSE. - if let ArpOperation::Unknown(_) = operation { - net_debug!("arp: unknown operation code"); - return None; - } - - // Discard packets with non-unicast source addresses. - if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { - net_debug!("arp: non-unicast source address"); - return None; - } - - if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) { - net_debug!("arp: source IP address not in same network as us"); - return None; - } - - // Fill the ARP cache from any ARP packet aimed at us (both request or response). - // We fill from requests too because if someone is requesting our address they - // are probably going to talk to us, so we avoid having to request their address - // when we later reply to them. - self.neighbor_cache.as_mut().unwrap().fill( - source_protocol_addr.into(), - source_hardware_addr.into(), - timestamp, - ); - - if operation == ArpOperation::Request { - let src_hardware_addr = match self.hardware_addr { - Some(HardwareAddress::Ethernet(addr)) => addr, - _ => unreachable!(), - }; - - Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { - operation: ArpOperation::Reply, - source_hardware_addr: src_hardware_addr, - source_protocol_addr: target_protocol_addr, - target_hardware_addr: source_hardware_addr, - target_protocol_addr: source_protocol_addr, - })) - } else { - None - } - } - } - } - - #[cfg(feature = "socket-raw")] - fn raw_socket_filter<'frame>( - &mut self, - sockets: &mut SocketSet, - ip_repr: &IpRepr, - ip_payload: &'frame [u8], - ) -> bool { - let mut handled_by_raw_socket = false; - - // Pass every IP packet to all raw sockets we have registered. - for raw_socket in sockets - .items_mut() - .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket)) - { - if raw_socket.accepts(ip_repr) { - raw_socket.process(self, ip_repr, ip_payload); - handled_by_raw_socket = true; - } - } - handled_by_raw_socket - } - - #[cfg(feature = "proto-ipv6")] - fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( - &mut self, - sockets: &mut SocketSet, - ipv6_packet: &Ipv6Packet<&'frame T>, - ) -> Option> { - let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet)); - - if !ipv6_repr.src_addr.is_unicast() { - // Discard packets with non-unicast source addresses. - net_debug!("non-unicast source address"); - return None; - } - - 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); - #[cfg(not(feature = "socket-raw"))] - let handled_by_raw_socket = false; - - self.process_nxt_hdr( - sockets, - ipv6_repr, - ipv6_repr.next_header, - handled_by_raw_socket, - ip_payload, - ) - } - - /// Given the next header value forward the payload onto the correct process - /// function. - #[cfg(feature = "proto-ipv6")] - fn process_nxt_hdr<'frame>( - &mut self, - sockets: &mut SocketSet, - ipv6_repr: Ipv6Repr, - nxt_hdr: IpProtocol, - handled_by_raw_socket: bool, - ip_payload: &'frame [u8], - ) -> Option> { - match nxt_hdr { - IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload), - - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpProtocol::Udp => { - let udp_packet = check!(UdpPacket::new_checked(ip_payload)); - let udp_repr = check!(UdpRepr::parse( - &udp_packet, - &ipv6_repr.src_addr.into(), - &ipv6_repr.dst_addr.into(), - &self.checksum_caps(), - )); - - self.process_udp( - sockets, - ipv6_repr.into(), - udp_repr, - handled_by_raw_socket, - udp_packet.payload(), - ip_payload, - ) - } - - #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), - - IpProtocol::HopByHop => { - self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload) - } - - #[cfg(feature = "socket-raw")] - _ if handled_by_raw_socket => None, - - _ => { - // Send back as much of the original payload as we can. - let payload_len = - icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); - let icmp_reply_repr = Icmpv6Repr::ParamProblem { - reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, - // The offending packet is after the IPv6 header. - pointer: ipv6_repr.buffer_len() as u32, - header: ipv6_repr, - data: &ip_payload[0..payload_len], - }; - self.icmpv6_reply(ipv6_repr, icmp_reply_repr) - } - } - } - - #[cfg(feature = "proto-ipv4")] - fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( - &mut self, - sockets: &mut SocketSet, - ipv4_packet: &Ipv4Packet<&'payload T>, - _fragments: Option<&'output mut PacketAssemblerSet<'a, Ipv4FragKey>>, - ) -> Option> { - let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); - if !self.is_unicast_v4(ipv4_repr.src_addr) { - // Discard packets with non-unicast source addresses. - net_debug!("non-unicast source address"); - return None; - } - - #[cfg(feature = "proto-ipv4-fragmentation")] - let ip_payload = { - const REASSEMBLY_TIMEOUT: u64 = 90; - - let fragments = _fragments.unwrap(); - - if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { - let key = ipv4_packet.get_key(); - - let f = match fragments.get_packet_assembler_mut(&key) { - Ok(f) => f, - Err(_) => { - let p = match fragments.reserve_with_key(&key) { - Ok(p) => p, - Err(Error::PacketAssemblerSetFull) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); - } - e => check!(e), - }; - - check!(p.start( - None, - self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), - 0 - )); - - check!(fragments.get_packet_assembler_mut(&key)) - } - }; - - if !ipv4_packet.more_frags() { - // This is the last fragment, so we know the total size - check!(f.set_total_size( - ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize - + ipv4_packet.frag_offset() as usize, - )); - } - - match f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { - Ok(true) => { - // NOTE: according to the standard, the total length needs to be - // recomputed, as well as the checksum. However, we don't really use - // the IPv4 header after the packet is reassembled. - check!(fragments.get_assembled_packet(&key)) - } - Ok(false) => { - return None; - } - Err(Error::PacketAssemblerOverlap) => { - return None; - } - Err(e) => { - net_debug!("fragmentation error: {}", e); - return None; - } - } - } else { - ipv4_packet.payload() - } - }; - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - let ip_payload = ipv4_packet.payload(); - - let ip_repr = IpRepr::Ipv4(ipv4_repr); - - #[cfg(feature = "socket-raw")] - let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload); - #[cfg(not(feature = "socket-raw"))] - let handled_by_raw_socket = false; - - #[cfg(feature = "socket-dhcpv4")] - { - if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { - // First check for source and dest ports, then do `UdpRepr::parse` if they match. - // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) - let udp_packet = check!(UdpPacket::new_checked(ip_payload)); - if udp_packet.src_port() == DHCP_SERVER_PORT - && udp_packet.dst_port() == DHCP_CLIENT_PORT - { - if let Some(dhcp_socket) = sockets - .items_mut() - .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) - { - let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let udp_repr = check!(UdpRepr::parse( - &udp_packet, - &src_addr, - &dst_addr, - &self.caps.checksum - )); - let udp_payload = udp_packet.payload(); - - dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload); - return None; - } - } - } - } - - if !self.has_ip_addr(ipv4_repr.dst_addr) - && !self.has_multicast_group(ipv4_repr.dst_addr) - && !self.is_broadcast_v4(ipv4_repr.dst_addr) - { - // 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 - || !ipv4_repr.dst_addr.is_unicast() - || self - .routes - .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now) - .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) - { - return None; - } - } - - match ipv4_repr.next_header { - IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload), - - #[cfg(feature = "proto-igmp")] - IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload), - - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpProtocol::Udp => { - let udp_packet = check!(UdpPacket::new_checked(ip_payload)); - let udp_repr = check!(UdpRepr::parse( - &udp_packet, - &ipv4_repr.src_addr.into(), - &ipv4_repr.dst_addr.into(), - &self.checksum_caps(), - )); - - self.process_udp( - sockets, - ip_repr, - udp_repr, - handled_by_raw_socket, - udp_packet.payload(), - ip_payload, - ) - } - - #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload), - - _ if handled_by_raw_socket => None, - - _ => { - // Send back as much of the original payload as we can. - let payload_len = - icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); - let icmp_reply_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::ProtoUnreachable, - header: ipv4_repr, - data: &ip_payload[0..payload_len], - }; - self.icmpv4_reply(ipv4_repr, icmp_reply_repr) - } - } - } - - /// Checks if an incoming packet has a broadcast address for the interfaces - /// associated ipv4 addresses. - #[cfg(feature = "proto-ipv4")] - fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { - self.ip_addrs - .iter() - .filter_map(|own_cidr| match own_cidr { - IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None, - }) - .any(|broadcast_address| address == broadcast_address) - } - - /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses - #[cfg(feature = "proto-ipv4")] - fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { - address.is_broadcast() || self.is_subnet_broadcast(address) - } - - /// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses - #[cfg(feature = "proto-ipv4")] - fn is_unicast_v4(&self, address: Ipv4Address) -> bool { - address.is_unicast() && !self.is_subnet_broadcast(address) - } - - /// Host duties of the **IGMPv2** protocol. - /// - /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. - /// 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, - ipv4_repr: Ipv4Repr, - ip_payload: &'frame [u8], - ) -> Option> { - let igmp_packet = check!(IgmpPacket::new_checked(ip_payload)); - let igmp_repr = check!(IgmpRepr::parse(&igmp_packet)); - - // FIXME: report membership after a delay - match igmp_repr { - IgmpRepr::MembershipQuery { - group_addr, - version, - max_resp_time, - } => { - // General query - if group_addr.is_unspecified() - && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS - { - // Are we member in any groups? - if self.ipv4_multicast_groups.iter().next().is_some() { - let interval = match version { - IgmpVersion::Version1 => Duration::from_millis(100), - IgmpVersion::Version2 => { - // No dependence on a random generator - // (see [#24](https://github.com/m-labs/smoltcp/issues/24)) - // but at least spread reports evenly across max_resp_time. - let intervals = self.ipv4_multicast_groups.len() as u32 + 1; - max_resp_time / intervals - } - }; - self.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, - timeout: self.now + interval, - interval, - next_index: 0, - }; - } - } else { - // Group-specific query - if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr { - // Don't respond immediately - let timeout = max_resp_time / 4; - self.igmp_report_state = IgmpReportState::ToSpecificQuery { - version, - timeout: self.now + timeout, - group: group_addr, - }; - } - } - } - // Ignore membership reports - IgmpRepr::MembershipReport { .. } => (), - // Ignore hosts leaving groups - IgmpRepr::LeaveGroup { .. } => (), - } - - None - } - - #[cfg(feature = "proto-ipv6")] - fn process_icmpv6<'frame>( - &mut self, - _sockets: &mut SocketSet, - ip_repr: IpRepr, - ip_payload: &'frame [u8], - ) -> Option> { - let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload)); - let icmp_repr = check!(Icmpv6Repr::parse( - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - &icmp_packet, - &self.caps.checksum, - )); - - #[cfg(feature = "socket-icmp")] - let mut handled_by_icmp_socket = false; - - #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] - for icmp_socket in _sockets - .items_mut() - .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) - { - if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { - icmp_socket.process(self, &ip_repr, &icmp_repr.into()); - handled_by_icmp_socket = true; - } - } - - match icmp_repr { - // Respond to echo requests. - Icmpv6Repr::EchoRequest { - ident, - seq_no, - data, - } => match ip_repr { - IpRepr::Ipv6(ipv6_repr) => { - let icmp_reply_repr = Icmpv6Repr::EchoReply { - ident, - seq_no, - data, - }; - self.icmpv6_reply(ipv6_repr, icmp_reply_repr) - } - #[allow(unreachable_patterns)] - _ => unreachable!(), - }, - - // Ignore any echo replies. - Icmpv6Repr::EchoReply { .. } => None, - - // Forward any NDISC packets to the ndisc packet handler - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { - IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr), - #[allow(unreachable_patterns)] - _ => unreachable!(), - }, - - // Don't report an error if a packet with unknown type - // has been handled by an ICMP socket - #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => None, - - // FIXME: do something correct here? - _ => None, - } - } - - #[cfg(all( - any(feature = "medium-ethernet", feature = "medium-ieee802154"), - feature = "proto-ipv6" - ))] - fn process_ndisc<'frame>( - &mut self, - ip_repr: Ipv6Repr, - repr: NdiscRepr<'frame>, - ) -> Option> { - match repr { - NdiscRepr::NeighborAdvert { - lladdr, - target_addr, - flags, - } => { - let ip_addr = ip_repr.src_addr.into(); - if let Some(lladdr) = lladdr { - let lladdr = check!(lladdr.parse(self.caps.medium)); - if !lladdr.is_unicast() || !target_addr.is_unicast() { - return None; - } - if flags.contains(NdiscNeighborFlags::OVERRIDE) - || !self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&ip_addr, self.now) - .found() - { - self.neighbor_cache - .as_mut() - .unwrap() - .fill(ip_addr, lladdr, self.now) - } - } - None - } - NdiscRepr::NeighborSolicit { - target_addr, - lladdr, - .. - } => { - if let Some(lladdr) = lladdr { - let lladdr = check!(lladdr.parse(self.caps.medium)); - if !lladdr.is_unicast() || !target_addr.is_unicast() { - return None; - } - self.neighbor_cache.as_mut().unwrap().fill( - ip_repr.src_addr.into(), - lladdr, - self.now, - ); - } - - if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { - let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { - flags: NdiscNeighborFlags::SOLICITED, - target_addr, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - lladdr: Some(self.hardware_addr.unwrap().into()), - }); - let ip_repr = Ipv6Repr { - src_addr: target_addr, - dst_addr: ip_repr.src_addr, - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: advert.buffer_len(), - }; - Some(IpPacket::Icmpv6((ip_repr, advert))) - } else { - None - } - } - _ => None, - } - } - - #[cfg(feature = "proto-ipv6")] - fn process_hopbyhop<'frame>( - &mut self, - sockets: &mut SocketSet, - ipv6_repr: Ipv6Repr, - handled_by_raw_socket: bool, - ip_payload: &'frame [u8], - ) -> Option> { - let hbh_pkt = check!(Ipv6HopByHopHeader::new_checked(ip_payload)); - let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_pkt)); - for opt_repr in hbh_repr.options() { - let opt_repr = check!(opt_repr); - match opt_repr { - Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), - Ipv6OptionRepr::Unknown { type_, .. } => { - match Ipv6OptionFailureType::from(type_) { - Ipv6OptionFailureType::Skip => (), - Ipv6OptionFailureType::Discard => { - return None; - } - _ => { - // FIXME(dlrobertson): Send an ICMPv6 parameter problem message - // here. - return None; - } - } - } - } - } - self.process_nxt_hdr( - 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>( - &mut self, - _sockets: &mut SocketSet, - ip_repr: IpRepr, - ip_payload: &'frame [u8], - ) -> Option> { - let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload)); - let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)); - - #[cfg(feature = "socket-icmp")] - let mut handled_by_icmp_socket = false; - - #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] - for icmp_socket in _sockets - .items_mut() - .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) - { - if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { - icmp_socket.process(self, &ip_repr, &icmp_repr.into()); - handled_by_icmp_socket = true; - } - } - - match icmp_repr { - // Respond to echo requests. - #[cfg(feature = "proto-ipv4")] - Icmpv4Repr::EchoRequest { - ident, - seq_no, - data, - } => { - let icmp_reply_repr = Icmpv4Repr::EchoReply { - ident, - seq_no, - data, - }; - match ip_repr { - IpRepr::Ipv4(ipv4_repr) => self.icmpv4_reply(ipv4_repr, icmp_reply_repr), - #[allow(unreachable_patterns)] - _ => unreachable!(), - } - } - - // Ignore any echo replies. - Icmpv4Repr::EchoReply { .. } => None, - - // Don't report an error if a packet with unknown type - // has been handled by an ICMP socket - #[cfg(feature = "socket-icmp")] - _ if handled_by_icmp_socket => None, - - // FIXME: do something correct here? - _ => None, - } - } - - #[cfg(feature = "proto-ipv4")] - fn icmpv4_reply<'frame, 'icmp: 'frame>( - &self, - ipv4_repr: Ipv4Repr, - icmp_repr: Icmpv4Repr<'icmp>, - ) -> Option> { - if !self.is_unicast_v4(ipv4_repr.src_addr) { - // Do not send ICMP replies to non-unicast sources - None - } else if self.is_unicast_v4(ipv4_repr.dst_addr) { - // Reply as normal when src_addr and dst_addr are both unicast - let ipv4_reply_repr = Ipv4Repr { - src_addr: ipv4_repr.dst_addr, - dst_addr: ipv4_repr.src_addr, - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }; - Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) - } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { - // Only reply to broadcasts for echo replies and not other ICMP messages - match icmp_repr { - Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() { - Some(src_addr) => { - let ipv4_reply_repr = Ipv4Repr { - src_addr, - dst_addr: ipv4_repr.src_addr, - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }; - Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) - } - None => None, - }, - _ => None, - } - } else { - None - } - } - - #[cfg(feature = "proto-ipv6")] - fn icmpv6_reply<'frame, 'icmp: 'frame>( - &self, - ipv6_repr: Ipv6Repr, - icmp_repr: Icmpv6Repr<'icmp>, - ) -> Option> { - if ipv6_repr.dst_addr.is_unicast() { - let ipv6_reply_repr = Ipv6Repr { - src_addr: ipv6_repr.dst_addr, - dst_addr: ipv6_repr.src_addr, - next_header: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }; - Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) - } else { - // Do not send any ICMP replies to a broadcast destination address. - None - } - } - - #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - fn process_udp<'frame>( - &mut self, - sockets: &mut SocketSet, - ip_repr: IpRepr, - udp_repr: UdpRepr, - handled_by_raw_socket: bool, - udp_payload: &'frame [u8], - ip_payload: &'frame [u8], - ) -> Option> { - #[cfg(feature = "socket-udp")] - for udp_socket in sockets - .items_mut() - .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) - { - if udp_socket.accepts(self, &ip_repr, &udp_repr) { - udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); - return None; - } - } - - #[cfg(feature = "socket-dns")] - for dns_socket in sockets - .items_mut() - .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket)) - { - if dns_socket.accepts(&ip_repr, &udp_repr) { - dns_socket.process(self, &ip_repr, &udp_repr, udp_payload); - return None; - } - } - - // The packet wasn't handled by a socket, send an ICMP port unreachable packet. - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) if handled_by_raw_socket => None, - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) if handled_by_raw_socket => None, - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(ipv4_repr) => { - let payload_len = - icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); - let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::PortUnreachable, - header: ipv4_repr, - data: &ip_payload[0..payload_len], - }; - self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr) - } - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(ipv6_repr) => { - let payload_len = - icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); - let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ipv6_repr, - data: &ip_payload[0..payload_len], - }; - self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr) - } - } - } - - #[cfg(feature = "socket-tcp")] - fn process_tcp<'frame>( - &mut self, - sockets: &mut SocketSet, - ip_repr: IpRepr, - ip_payload: &'frame [u8], - ) -> Option> { - let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); - let tcp_packet = check!(TcpPacket::new_checked(ip_payload)); - let tcp_repr = check!(TcpRepr::parse( - &tcp_packet, - &src_addr, - &dst_addr, - &self.caps.checksum - )); - - for tcp_socket in sockets - .items_mut() - .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket)) - { - if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { - return tcp_socket - .process(self, &ip_repr, &tcp_repr) - .map(IpPacket::Tcp); - } - } - - if tcp_repr.control == TcpControl::Rst { - // Never reply to a TCP RST packet with another TCP RST packet. - None - } else { - // The packet wasn't handled by a socket, send a TCP RST packet. - Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr))) - } - } - - #[cfg(feature = "medium-ethernet")] - fn dispatch( - &mut self, - tx_token: Tx, - packet: EthernetPacket, - _out_packet: Option<&mut OutPackets<'_>>, - ) -> Result<()> - where - Tx: TxToken, - { - match packet { - #[cfg(feature = "proto-ipv4")] - EthernetPacket::Arp(arp_repr) => { - let dst_hardware_addr = match arp_repr { - ArpRepr::EthernetIpv4 { - target_hardware_addr, - .. - } => target_hardware_addr, - }; - - self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { - frame.set_dst_addr(dst_hardware_addr); - frame.set_ethertype(EthernetProtocol::Arp); - - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - arp_repr.emit(&mut packet); - }) - } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, _out_packet), - } - } - - #[cfg(feature = "medium-ethernet")] - fn dispatch_ethernet(&mut self, 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(self.now, tx_len, |tx_buffer| { - debug_assert!(tx_buffer.as_ref().len() == tx_len); - let mut frame = EthernetFrame::new_unchecked(tx_buffer); - - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - - frame.set_src_addr(src_addr); - - f(frame); - - Ok(()) - }) - } - - fn in_same_network(&self, addr: &IpAddress) -> bool { - self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr)) - } - - fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { - // Send directly. - if self.in_same_network(addr) || addr.is_broadcast() { - return Ok(*addr); - } - - // Route via a router. - match self.routes.lookup(addr, timestamp) { - Some(router_addr) => Ok(router_addr), - None => Err(Error::Unaddressable), - } - } - - fn has_neighbor(&self, addr: &IpAddress) -> bool { - match self.route(addr, self.now) { - Ok(_routed_addr) => match self.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => self - .neighbor_cache - .as_ref() - .unwrap() - .lookup(&_routed_addr, self.now) - .found(), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => self - .neighbor_cache - .as_ref() - .unwrap() - .lookup(&_routed_addr, self.now) - .found(), - #[cfg(feature = "medium-ip")] - Medium::Ip => true, - }, - Err(_) => false, - } - } - - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - fn lookup_hardware_addr( - &mut self, - tx_token: Tx, - src_addr: &IpAddress, - dst_addr: &IpAddress, - ) -> Result<(HardwareAddress, Tx)> - where - Tx: TxToken, - { - if dst_addr.is_broadcast() { - let hardware_addr = match self.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST), - #[cfg(feature = "medium-ip")] - Medium::Ip => unreachable!(), - }; - - return Ok((hardware_addr, tx_token)); - } - - if dst_addr.is_multicast() { - let b = dst_addr.as_bytes(); - let hardware_addr = match *dst_addr { - #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(_addr) => { - HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ - 0x01, - 0x00, - 0x5e, - b[1] & 0x7F, - b[2], - b[3], - ])) - } - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(_addr) => match self.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ - 0x33, 0x33, b[12], b[13], b[14], b[15], - ])), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - // Not sure if this is correct - HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST) - } - #[cfg(feature = "medium-ip")] - Medium::Ip => unreachable!(), - }, - }; - - return Ok((hardware_addr, tx_token)); - } - - let dst_addr = self.route(dst_addr, self.now)?; - - match self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&dst_addr, self.now) - { - NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), - NeighborAnswer::RateLimited => return Err(Error::Unaddressable), - _ => (), // XXX - } - - match (src_addr, dst_addr) { - #[cfg(feature = "proto-ipv4")] - (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => { - net_debug!( - "address {} not in neighbor cache, sending ARP request", - dst_addr - ); - let src_hardware_addr = - if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - - let arp_repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: src_hardware_addr, - source_protocol_addr: src_addr, - target_hardware_addr: EthernetAddress::BROADCAST, - target_protocol_addr: dst_addr, - }; - - self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_ethertype(EthernetProtocol::Arp); - - arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) - })?; - } - - #[cfg(feature = "proto-ipv6")] - (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => { - net_debug!( - "address {} not in neighbor cache, sending Neighbor Solicitation", - dst_addr - ); - - let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { - target_addr: dst_addr, - lladdr: Some(self.hardware_addr.unwrap().into()), - }); - - let packet = IpPacket::Icmpv6(( - Ipv6Repr { - src_addr, - dst_addr: dst_addr.solicited_node(), - next_header: IpProtocol::Icmpv6, - payload_len: solicit.buffer_len(), - hop_limit: 0xff, - }, - solicit, - )); - - self.dispatch_ip(tx_token, packet, None)?; - } - - #[allow(unreachable_patterns)] - _ => (), - } - // The request got dispatched, limit the rate on the cache. - self.neighbor_cache.as_mut().unwrap().limit_rate(self.now); - Err(Error::Unaddressable) - } - - fn flush_cache(&mut self) { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - if let Some(cache) = self.neighbor_cache.as_mut() { - cache.flush() - } - } - - fn dispatch_ip( - &mut self, - tx_token: Tx, - packet: IpPacket, - _out_packet: Option<&mut OutPackets<'_>>, - ) -> Result<()> { - let mut ip_repr = packet.ip_repr(); - assert!(!ip_repr.dst_addr().is_unspecified()); - - // Dispatch IEEE802.15.4: - - #[cfg(feature = "medium-ieee802154")] - if matches!(self.caps.medium, Medium::Ieee802154) { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), - _ => unreachable!(), - }; - - return self.dispatch_ieee802154( - dst_hardware_addr, - &ip_repr, - tx_token, - packet, - _out_packet, - ); - } - - // Dispatch IP/Ethernet: - - let caps = self.caps.clone(); - - #[cfg(feature = "proto-ipv4-fragmentation")] - let ipv4_id = self.get_ipv4_ident(); - - // First we calculate the total length that we will have to emit. - let mut total_len = ip_repr.buffer_len(); - - // Add the size of the Ethernet header if the medium is Ethernet. - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - total_len = EthernetFrame::<&[u8]>::buffer_len(total_len); - } - - // If the medium is Ethernet, then we need to retrieve the destination hardware address. - #[cfg(feature = "medium-ethernet")] - let (dst_hardware_addr, tx_token) = - match self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())? { - (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - #[cfg(feature = "medium-ieee802154")] - (HardwareAddress::Ieee802154(_), _) => unreachable!(), - }; - - // Emit function for the Ethernet header. - #[cfg(feature = "medium-ethernet")] - let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { - let mut frame = EthernetFrame::new_unchecked(tx_buffer); - - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - - frame.set_src_addr(src_addr); - frame.set_dst_addr(dst_hardware_addr); - - match repr.version() { - #[cfg(feature = "proto-ipv4")] - IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), - } - - Ok(()) - }; - - // Emit function for the IP header and payload. - let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| { - repr.emit(&mut tx_buffer, &self.caps.checksum); - - let payload = &mut tx_buffer[repr.header_len()..]; - packet.emit_payload(repr, payload, &caps); - }; - - let total_ip_len = ip_repr.buffer_len(); - - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(ref mut repr) => { - // If we have an IPv4 packet, then we need to check if we need to fragment it. - if total_ip_len > self.caps.max_transmission_unit { - #[cfg(feature = "proto-ipv4-fragmentation")] - { - net_debug!("start fragmentation"); - - let Ipv4OutPacket { - buffer, - packet_len, - sent_bytes, - repr: out_packet_repr, - frag_offset, - ident, - dst_hardware_addr: dst_address, - } = &mut _out_packet.unwrap().ipv4_out_packet; - - // Calculate how much we will send now (including the Ethernet header). - let tx_len = self.caps.max_transmission_unit; - - let ip_header_len = repr.buffer_len(); - let first_frag_ip_len = self.caps.ip_mtu(); - - if buffer.len() < first_frag_ip_len { - net_debug!("Fragmentation buffer is too small"); - return Err(Error::Exhausted); - } - - *dst_address = dst_hardware_addr; - - // Save the total packet len (without the Ethernet header, but with the first - // IP header). - *packet_len = total_ip_len; - - // Save the IP header for other fragments. - *out_packet_repr = *repr; - - // Save how much bytes we will send now. - *sent_bytes = first_frag_ip_len; - - // Modify the IP header - repr.payload_len = first_frag_ip_len - repr.buffer_len(); - - // Emit the IP header to the buffer. - emit_ip(&ip_repr, buffer); - let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); - *ident = ipv4_id; - ipv4_packet.set_ident(ipv4_id); - ipv4_packet.set_more_frags(true); - ipv4_packet.set_dont_frag(false); - ipv4_packet.set_frag_offset(0); - - if caps.checksum.ipv4.tx() { - ipv4_packet.fill_checksum(); - } - - // Transmit the first packet. - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&ip_repr, tx_buffer)?; - tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; - } - - // Change the offset for the next packet. - *frag_offset = (first_frag_ip_len - ip_header_len) as u16; - - // Copy the IP header and the payload. - tx_buffer[..first_frag_ip_len] - .copy_from_slice(&buffer[..first_frag_ip_len]); - - Ok(()) - }) - } - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - { - net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); - Ok(()) - } - } else { - // No fragmentation is required. - tx_token.consume(self.now, total_len, |mut tx_buffer| { - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&ip_repr, tx_buffer)?; - tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; - } - - emit_ip(&ip_repr, tx_buffer); - Ok(()) - }) - } - } - // We don't support IPv6 fragmentation yet. - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => tx_token.consume(self.now, total_len, |mut tx_buffer| { - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&ip_repr, tx_buffer)?; - tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; - } - - emit_ip(&ip_repr, tx_buffer); - Ok(()) - }), - } - } - - #[cfg(all(feature = "medium-ieee802154", feature = "proto-sixlowpan"))] - fn dispatch_ieee802154( - &mut self, - ll_dst_a: Ieee802154Address, - ip_repr: &IpRepr, - tx_token: Tx, - packet: IpPacket, - _out_packet: Option<&mut OutPackets>, - ) -> Result<()> { - // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. - // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to - // fragment it. - let ll_src_a = self.hardware_addr.map_or_else( - || Err(Error::Malformed), - |addr| match addr { - HardwareAddress::Ieee802154(addr) => Ok(addr), - _ => Err(Error::Malformed), - }, - )?; - - let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { - (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), - #[allow(unreachable_patterns)] - _ => return Err(Error::Unaddressable), - }; - - // Create the IEEE802.15.4 header. - let ieee_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(self.get_sequence_number()), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: self.pan_id, - dst_addr: Some(ll_dst_a), - src_pan_id: self.pan_id, - src_addr: Some(ll_src_a), - }; - - // Create the 6LoWPAN IPHC header. - let iphc_repr = SixlowpanIphcRepr { - src_addr, - ll_src_addr: Some(ll_src_a), - dst_addr, - ll_dst_addr: Some(ll_dst_a), - next_header: match &packet { - IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp), - #[cfg(feature = "socket-udp")] - IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, - #[allow(unreachable_patterns)] - _ => return Err(Error::Unrecognized), - }, - hop_limit: ip_repr.hop_limit(), - ecn: None, - dscp: None, - flow_label: None, - }; - - // Now we calculate the total size of the packet. - // We need to know this, such that we know when to do the fragmentation. - let mut total_size = 0; - total_size += iphc_repr.buffer_len(); - let mut _compressed_headers_len = iphc_repr.buffer_len(); - let mut _uncompressed_headers_len = ip_repr.header_len(); - - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - _compressed_headers_len += udp_repr.header_len(); - _uncompressed_headers_len += udpv6_repr.header_len(); - total_size += udp_repr.header_len() + payload.len(); - } - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, tcp_repr)) => { - total_size += tcp_repr.buffer_len(); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - total_size += icmp_repr.buffer_len(); - } - _ => return Err(Error::Unrecognized), - } - - let ieee_len = ieee_repr.buffer_len(); - - if total_size + ieee_len > 125 { - #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - // The packet does not fit in one Ieee802154 frame, so we need fragmentation. - // We do this by emitting everything in the `out_packet.buffer` from the interface. - // After emitting everything into that buffer, we send the first fragment heere. - // When `poll` is called again, we check if out_packet was fully sent, otherwise we - // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. - - // `dispatch_ieee802154_out_packet` requires some information about the total packet size, - // the link local source and destination address... - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - datagram_offset, - .. - } = &mut _out_packet.unwrap().sixlowpan_out_packet; - - if buffer.len() < total_size { - net_debug!("6LoWPAN: Fragmentation buffer is too small"); - return Err(Error::Exhausted); - } - - *ll_dst_addr = ll_dst_a; - *ll_src_addr = ll_src_a; - - let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); - iphc_repr.emit(&mut iphc_packet); - - let b = &mut buffer[iphc_repr.buffer_len()..]; - - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( - &mut b[..udp_repr.header_len() + payload.len()], - ); - udp_repr.emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - } - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = - TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); - tcp_repr.emit( - &mut tcp_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &self.caps.checksum, - ); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - let mut icmp_packet = - Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - } - _ => return Err(Error::Unrecognized), - } - - *packet_len = total_size; - - // The datagram size that we need to set in the first fragment header is equal to the - // IPv6 payload length + 40. - *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; - - // We generate a random tag. - let tag = self.get_sixlowpan_fragment_tag(); - // We save the tag for the other fragments that will be created when calling `poll` - // multiple times. - *datagram_tag = tag; - - let frag1 = SixlowpanFragRepr::FirstFragment { - size: *datagram_size, - tag, - }; - let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, - tag, - offset: 0, - }; - - // We calculate how much data we can send in the first fragment and the other - // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight - // (except for the last fragment) since the offset field in the fragment is an offset - // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. - // - // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 - - let header_diff = _uncompressed_headers_len - _compressed_headers_len; - let frag1_size = - (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); - - *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; - - *sent_bytes = frag1_size; - *datagram_offset = frag1_size + header_diff; - - tx_token.consume( - self.now, - ieee_len + frag1.buffer_len() + frag1_size, - |mut tx_buf| { - // Add the IEEE header. - let mut ieee_packet = - Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - // Add the first fragment header - let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); - frag1.emit(&mut frag1_packet); - tx_buf = &mut tx_buf[frag1.buffer_len()..]; - - // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); - - Ok(()) - }, - ) - } - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - { - net_debug!( - "Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support." - ); - Ok(()) - } - } else { - // We don't need fragmentation, so we emit everything to the TX token. - tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut tx_buf[..iphc_repr.buffer_len()]); - iphc_repr.emit(&mut iphc_packet); - tx_buf = &mut tx_buf[iphc_repr.buffer_len()..]; - - #[allow(unreachable_patterns)] - match packet { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { - let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); - let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( - &mut tx_buf[..udp_repr.header_len() + payload.len()], - ); - udp_repr.emit( - &mut udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - payload.len(), - |buf| buf.copy_from_slice(payload), - ); - } - #[cfg(feature = "socket-tcp")] - IpPacket::Tcp((_, tcp_repr)) => { - let mut tcp_packet = - TcpPacket::new_unchecked(&mut tx_buf[..tcp_repr.buffer_len()]); - tcp_repr.emit( - &mut tcp_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &self.caps.checksum, - ); - } - #[cfg(feature = "proto-ipv6")] - IpPacket::Icmpv6((_, icmp_repr)) => { - let mut icmp_packet = - Icmpv6Packet::new_unchecked(&mut tx_buf[..icmp_repr.buffer_len()]); - icmp_repr.emit( - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - &mut icmp_packet, - &self.caps.checksum, - ); - } - _ => return Err(Error::Unrecognized), - } - Ok(()) - }) - } - } - - #[cfg(all( - feature = "medium-ieee802154", - feature = "proto-sixlowpan-fragmentation" - ))] - fn dispatch_ieee802154_out_packet( - &mut self, - tx_token: Tx, - out_packet: &mut SixlowpanOutPacket, - ) -> Result<()> { - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - datagram_offset, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - .. - } = out_packet; - - // Create the IEEE802.15.4 header. - let ieee_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(self.get_sequence_number()), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: self.pan_id, - dst_addr: Some(*ll_dst_addr), - src_pan_id: self.pan_id, - src_addr: Some(*ll_src_addr), - }; - - // Create the FRAG_N header. - let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, - tag: *datagram_tag, - offset: (*datagram_offset / 8) as u8, - }; - - let ieee_len = ieee_repr.buffer_len(); - let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); - - tx_token.consume( - self.now, - ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, - |mut tx_buf| { - let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - let mut frag_packet = - SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); - fragn.emit(&mut frag_packet); - tx_buf = &mut tx_buf[fragn.buffer_len()..]; - - // Add the buffer part - tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); - - *sent_bytes += frag_size; - *datagram_offset += frag_size; - - Ok(()) - }, - ) - } - - #[cfg(feature = "proto-ipv4-fragmentation")] - fn dispatch_ipv4_out_packet( - &mut self, - tx_token: Tx, - out_packet: &mut Ipv4OutPacket, - ) -> Result<()> { - let Ipv4OutPacket { - buffer, - packet_len, - sent_bytes, - repr, - dst_hardware_addr, - frag_offset, - ident, - .. - } = out_packet; - - let caps = self.caps.clone(); - - let mtu_max = self.ip_mtu(); - let ip_len = (*packet_len - *sent_bytes + repr.buffer_len()).min(mtu_max); - let payload_len = ip_len - repr.buffer_len(); - - let more_frags = (*packet_len - *sent_bytes) != payload_len; - repr.payload_len = payload_len; - *sent_bytes += payload_len; - - let mut tx_len = ip_len; - #[cfg(feature = "medium-ethernet")] - if matches!(caps.medium, Medium::Ethernet) { - tx_len += EthernetFrame::<&[u8]>::header_len(); - } - - // Emit function for the Ethernet header. - let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { - let mut frame = EthernetFrame::new_unchecked(tx_buffer); - - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - - frame.set_src_addr(src_addr); - frame.set_dst_addr(*dst_hardware_addr); - - match repr.version() { - #[cfg(feature = "proto-ipv4")] - IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), - } - - Ok(()) - }; - - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - #[cfg(feature = "medium-ethernet")] - if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; - tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; - } - - let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..repr.buffer_len()]); - repr.emit(&mut packet, &caps.checksum); - packet.set_ident(*ident); - packet.set_more_frags(more_frags); - packet.set_dont_frag(false); - packet.set_frag_offset(*frag_offset); - - if caps.checksum.ipv4.tx() { - packet.fill_checksum(); - } - - tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( - &buffer[*frag_offset as usize + repr.buffer_len() as usize..][..payload_len], - ); - - // Update the frag offset for the next fragment. - *frag_offset += payload_len as u16; - - Ok(()) - }) - } - - #[cfg(feature = "proto-igmp")] - fn igmp_report_packet<'any>( - &self, - version: IgmpVersion, - group_addr: Ipv4Address, - ) -> Option> { - let iface_addr = self.ipv4_address()?; - let igmp_repr = IgmpRepr::MembershipReport { - group_addr, - version, - }; - let pkt = IpPacket::Igmp(( - Ipv4Repr { - src_addr: iface_addr, - // Send to the group being reported - dst_addr: group_addr, - next_header: IpProtocol::Igmp, - payload_len: igmp_repr.buffer_len(), - hop_limit: 1, - // [#183](https://github.com/m-labs/smoltcp/issues/183). - }, - igmp_repr, - )); - Some(pkt) - } - - #[cfg(feature = "proto-igmp")] - fn igmp_leave_packet<'any>(&self, group_addr: Ipv4Address) -> Option> { - self.ipv4_address().map(|iface_addr| { - let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; - IpPacket::Igmp(( - Ipv4Repr { - src_addr: iface_addr, - dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, - next_header: IpProtocol::Igmp, - payload_len: igmp_repr.buffer_len(), - hop_limit: 1, - }, - igmp_repr, - )) - }) - } -} - -#[cfg(test)] -mod test { - use std::collections::BTreeMap; - #[cfg(feature = "proto-igmp")] - use std::vec::Vec; - - use super::*; - - use crate::iface::Interface; - #[cfg(feature = "medium-ethernet")] - use crate::iface::NeighborCache; - use crate::phy::{ChecksumCapabilities, Loopback}; - #[cfg(feature = "proto-igmp")] - use crate::time::Instant; - use crate::{Error, Result}; - - #[allow(unused)] - fn fill_slice(s: &mut [u8], val: u8) { - for x in s.iter_mut() { - *x = val - } - } - - fn create<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { - #[cfg(feature = "medium-ethernet")] - return create_ethernet(); - #[cfg(not(feature = "medium-ethernet"))] - return create_ip(); - } - - #[cfg(all(feature = "medium-ip"))] - #[allow(unused)] - fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { - // Create a basic device - let mut device = Loopback::new(Medium::Ip); - let ip_addrs = [ - #[cfg(feature = "proto-ipv4")] - IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), - ]; - - let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - - #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder - .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_fragmentation_buffer(vec![]); - - #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(&mut device); - - (iface, SocketSet::new(vec![]), device) - } - - #[cfg(all(feature = "medium-ethernet"))] - fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { - // Create a basic device - let mut device = Loopback::new(Medium::Ethernet); - let ip_addrs = [ - #[cfg(feature = "proto-ipv4")] - IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), - ]; - - let iface_builder = InterfaceBuilder::new() - .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .ip_addrs(ip_addrs); - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let iface_builder = iface_builder - .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .sixlowpan_fragmentation_buffer(vec![]); - - #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder - .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_fragmentation_buffer(vec![]); - - #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); - let iface = iface_builder.finalize(&mut device); - - (iface, SocketSet::new(vec![]), device) - } - - #[cfg(feature = "proto-igmp")] - fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { - let mut pkts = Vec::new(); - while let Some((rx, _tx)) = device.receive() { - rx.consume(timestamp, |pkt| { - pkts.push(pkt.to_vec()); - Ok(()) - }) - .unwrap(); - } - pkts - } - - #[derive(Debug, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - struct MockTxToken; - - impl TxToken for MockTxToken { - fn consume(self, _: Instant, _: usize, _: F) -> Result - where - F: FnOnce(&mut [u8]) -> Result, - { - Err(Error::Unaddressable) - } - } - - #[test] - #[should_panic(expected = "hardware_addr required option was not set")] - #[cfg(all(feature = "medium-ethernet"))] - fn test_builder_initialization_panic() { - let mut device = Loopback::new(Medium::Ethernet); - InterfaceBuilder::new().finalize(&mut device); - } - - #[test] - #[cfg(feature = "proto-ipv4")] - fn test_no_icmp_no_unicast_ipv4() { - let (mut iface, mut sockets, _device) = create(); - - // Unknown Ipv4 Protocol - // - // Because the destination is the broadcast address - // this should not trigger and Destination Unreachable - // response. See RFC 1122 § 3.2.2. - let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 54]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv4Packet::new_unchecked(&bytes); - - // Ensure that the unknown protocol frame does not trigger an - // ICMP error response when the destination address is a - // broadcast address - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - Some(&mut iface.fragments.ipv4_fragments) - ), - None - ); - } - - #[test] - #[cfg(feature = "proto-ipv6")] - fn test_no_icmp_no_unicast_ipv6() { - let (mut iface, mut sockets, _device) = create(); - - // Unknown Ipv6 Protocol - // - // Because the destination is the broadcast address - // this should not trigger and Destination Unreachable - // response. See RFC 1122 § 3.2.2. - let repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 54]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv6Packet::new_unchecked(&bytes); - - // 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 sockets, &frame), None); - } - - #[test] - #[cfg(feature = "proto-ipv4")] - fn test_icmp_error_no_payload() { - static NO_BYTES: [u8; 0] = []; - let (mut iface, mut sockets, _device) = create(); - - // Unknown Ipv4 Protocol with no payload - let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 34]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv4Packet::new_unchecked(&bytes); - - // The expected Destination Unreachable response due to the - // unknown protocol - let icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::ProtoUnreachable, - header: Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Unknown(12), - payload_len: 0, - hop_limit: 64, - }, - data: &NO_BYTES, - }; - - let expected_repr = IpPacket::Icmpv4(( - Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }, - icmp_repr, - )); - - // Ensure that the unknown protocol triggers an error response. - // And we correctly handle no payload. - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame, None), - Some(expected_repr) - ); - - #[cfg(feature = "proto-ipv4-fragmentation")] - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - Some(&mut iface.fragments.ipv4_fragments) - ), - Some(expected_repr) - ); - } - - #[test] - #[cfg(feature = "proto-ipv4")] - fn test_local_subnet_broadcasts() { - let (mut iface, _, _device) = create(); - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); - }); - }); - - assert!(iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])),); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])),); - - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); - }); - }); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])),); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])),); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])),); - assert!(iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])),); - - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); - }); - }); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])),); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])),); - assert!(!iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])),); - assert!(iface - .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])),); - } - - #[test] - #[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] - fn test_icmp_error_port_unreachable() { - static UDP_PAYLOAD: [u8; 12] = [ - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, - ]; - let (mut iface, mut sockets, _device) = create(); - - let mut udp_bytes_unicast = vec![0u8; 20]; - let mut udp_bytes_broadcast = vec![0u8; 20]; - let mut packet_unicast = UdpPacket::new_unchecked(&mut udp_bytes_unicast); - let mut packet_broadcast = UdpPacket::new_unchecked(&mut udp_bytes_broadcast); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }); - - // Emit the representations to a packet - udp_repr.emit( - &mut packet_unicast, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - let data = packet_unicast.into_inner(); - - // The expected Destination Unreachable ICMPv4 error response due - // to no sockets listening on the destination port. - let icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::PortUnreachable, - header: Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }, - data, - }; - let expected_repr = IpPacket::Icmpv4(( - Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }, - icmp_repr, - )); - - // Ensure that the unknown protocol triggers an error response. - // And we correctly handle no payload. - assert_eq!( - iface - .inner - .process_udp(&mut sockets, ip_repr, udp_repr, false, &UDP_PAYLOAD, data), - Some(expected_repr) - ); - - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }); - - // Emit the representations to a packet - udp_repr.emit( - &mut packet_broadcast, - &ip_repr.src_addr(), - &IpAddress::Ipv4(Ipv4Address::BROADCAST), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - // 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 sockets, - ip_repr, - udp_repr, - false, - &UDP_PAYLOAD, - packet_broadcast.into_inner(), - ), - None - ); - } - - #[test] - #[cfg(feature = "socket-udp")] - fn test_handle_udp_broadcast() { - use crate::wire::IpEndpoint; - - static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - - let (mut iface, mut sockets, _device) = create(); - - let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - - let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); - - let mut udp_bytes = vec![0u8; 13]; - let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); - - let socket_handle = sockets.add(udp_socket); - - #[cfg(feature = "proto-ipv6")] - let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] - let src_ip = Ipv4Address::new(0x7f, 0x00, 0x00, 0x02); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - - #[cfg(feature = "proto-ipv6")] - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_ip, - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40, - }); - #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: src_ip, - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40, - }); - - // Bind the socket to port 68 - let socket = sockets.get_mut::(socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - - udp_repr.emit( - &mut packet, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - // Packet should be handled by bound UDP socket - assert_eq!( - iface.inner.process_udp( - &mut sockets, - ip_repr, - udp_repr, - false, - &UDP_PAYLOAD, - packet.into_inner(), - ), - None - ); - - // Make sure the payload to the UDP packet processed by process_udp is - // appended to the bound sockets rx_buffer - let socket = sockets.get_mut::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) - ); - } - - #[test] - #[cfg(feature = "proto-ipv4")] - fn test_handle_ipv4_broadcast() { - use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - - let (mut iface, mut sockets, _device) = create(); - - let our_ipv4_addr = iface.ipv4_address().unwrap(); - let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); - - // ICMPv4 echo request - let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; - let icmpv4_repr = Icmpv4Repr::EchoRequest { - ident: 0x1234, - seq_no: 0xabcd, - data: &icmpv4_data, - }; - - // Send to IPv4 broadcast address - let ipv4_repr = Ipv4Repr { - src_addr: src_ipv4_addr, - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: icmpv4_repr.buffer_len(), - }; - - // Emit to ip frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - icmpv4_repr.emit( - &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - // Expected ICMPv4 echo reply - let expected_icmpv4_repr = Icmpv4Repr::EchoReply { - ident: 0x1234, - seq_no: 0xabcd, - data: &icmpv4_data, - }; - let expected_ipv4_repr = Ipv4Repr { - src_addr: our_ipv4_addr, - dst_addr: src_ipv4_addr, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: expected_icmpv4_repr.buffer_len(), - }; - let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame, None), - Some(expected_packet) - ); - - #[cfg(feature = "proto-ipv4-fragmentation")] - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - Some(&mut iface.fragments.ipv4_fragments) - ), - Some(expected_packet) - ); - } - - #[test] - #[cfg(feature = "socket-udp")] - fn test_icmp_reply_size() { - #[cfg(feature = "proto-ipv6")] - use crate::wire::Icmpv6DstUnreachable; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - use crate::wire::IPV4_MIN_MTU as MIN_MTU; - #[cfg(feature = "proto-ipv6")] - use crate::wire::IPV6_MIN_MTU as MIN_MTU; - - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - const MAX_PAYLOAD_LEN: usize = 528; - #[cfg(feature = "proto-ipv6")] - const MAX_PAYLOAD_LEN: usize = 1192; - - let (mut iface, mut sockets, _device) = create(); - - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let src_addr = Ipv4Address([192, 168, 1, 1]); - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let dst_addr = Ipv4Address([192, 168, 1, 2]); - #[cfg(feature = "proto-ipv6")] - let src_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - #[cfg(feature = "proto-ipv6")] - let dst_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2); - - // UDP packet that if not tructated will cause a icmp port unreachable reply - // to exeed the minimum mtu bytes in length. - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - MAX_PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let ip_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, - }; - #[cfg(feature = "proto-ipv6")] - let ip_repr = Ipv6Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, - }; - let payload = packet.into_inner(); - - // Expected packets - #[cfg(feature = "proto-ipv6")] - let expected_icmp_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN], - }; - #[cfg(feature = "proto-ipv6")] - let expected_ip_repr = Ipv6Repr { - src_addr: dst_addr, - dst_addr: src_addr, - next_header: IpProtocol::Icmpv6, - hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(), - }; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let expected_icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::PortUnreachable, - header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN], - }; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let expected_ip_repr = Ipv4Repr { - src_addr: dst_addr, - dst_addr: src_addr, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(), - }; - - // The expected packet does not exceed the IPV4_MIN_MTU - #[cfg(feature = "proto-ipv6")] - assert_eq!( - expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), - MIN_MTU - ); - // The expected packet does not exceed the IPV4_MIN_MTU - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - 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 sockets, - ip_repr.into(), - udp_repr, - false, - &vec![0x2a; MAX_PAYLOAD_LEN], - payload, - ), - Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) - ); - #[cfg(feature = "proto-ipv6")] - assert_eq!( - iface.inner.process_udp( - &mut sockets, - ip_repr.into(), - udp_repr, - false, - &vec![0x2a; MAX_PAYLOAD_LEN], - payload, - ), - Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) - ); - } - - #[test] - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] - fn test_handle_valid_arp_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: local_ip_addr, - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - - // Ensure an ARP Request for us triggers an ARP Reply - assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), - Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { - operation: ArpOperation::Reply, - source_hardware_addr: local_hw_addr, - source_protocol_addr: local_ip_addr, - target_hardware_addr: remote_hw_addr, - target_protocol_addr: remote_ip_addr - })) - ); - - // Ensure the address of the requestor was entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr) - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); - } - - #[test] - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] - fn test_handle_valid_ndisc_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 86]; - - let local_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1); - let remote_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { - target_addr: local_ip_addr, - lladdr: Some(remote_hw_addr.into()), - }); - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: remote_ip_addr, - dst_addr: local_ip_addr.solicited_node(), - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: solicit.buffer_len(), - }); - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Ipv6); - ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - solicit.emit( - &remote_ip_addr.into(), - &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), - &ChecksumCapabilities::default(), - ); - - let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { - flags: NdiscNeighborFlags::SOLICITED, - target_addr: local_ip_addr, - lladdr: Some(local_hw_addr.into()), - }); - - let ipv6_expected = Ipv6Repr { - src_addr: local_ip_addr, - dst_addr: remote_ip_addr, - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: icmpv6_expected.buffer_len(), - }; - - // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement - assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), - 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, - &IpAddress::Ipv6(local_ip_addr), - &IpAddress::Ipv6(remote_ip_addr) - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); - } - - #[test] - #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] - fn test_handle_other_arp_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x03]), - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - - // Ensure an ARP Request for someone else does not trigger an ARP Reply - assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), - None - ); - - // Ensure the address of the requestor was NOT entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr) - ), - Err(Error::Unaddressable) - ); - } - - #[test] - #[cfg(all( - feature = "medium-ethernet", - feature = "proto-ipv4", - not(feature = "medium-ieee802154") - ))] - fn test_arp_flush_after_update_ip() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - { - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - } - - // Ensure an ARP Request for us triggers an ARP Reply - assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), - Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { - operation: ArpOperation::Reply, - source_hardware_addr: local_hw_addr, - source_protocol_addr: local_ip_addr, - target_hardware_addr: remote_hw_addr, - target_protocol_addr: remote_ip_addr - })) - ); - - // Ensure the address of the requestor was entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr) - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); - - // Update IP addrs to trigger ARP cache flush - let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); - }); - }); - - // ARP cache flush after address change - assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); - } - - #[test] - #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] - fn test_icmpv4_socket() { - use crate::wire::Icmpv4Packet; - - let (mut iface, mut sockets, _device) = create(); - - let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); - let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); - - let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); - - let socket_handle = sockets.add(icmpv4_socket); - - let ident = 0x1234; - let seq_no = 0x5432; - let echo_data = &[0xff; 16]; - - let socket = sockets.get_mut::(socket_handle); - // Bind to the ID 0x1234 - assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); - - // Ensure the ident we bound to and the ident of the packet are the same. - let mut bytes = [0xff; 24]; - let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); - let echo_repr = Icmpv4Repr::EchoRequest { - ident, - seq_no, - data: echo_data, - }; - echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); - let icmp_data = &*packet.into_inner(); - - let ipv4_repr = Ipv4Repr { - src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), - dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), - next_header: IpProtocol::Icmp, - payload_len: 24, - hop_limit: 64, - }; - let ip_repr = IpRepr::Ipv4(ipv4_repr); - - // Open a socket and ensure the packet is handled due to the listening - // socket. - assert!(!sockets.get_mut::(socket_handle).can_recv()); - - // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening - let echo_reply = Icmpv4Repr::EchoReply { - ident, - seq_no, - data: echo_data, - }; - let ipv4_reply = Ipv4Repr { - src_addr: ipv4_repr.dst_addr, - dst_addr: ipv4_repr.src_addr, - ..ipv4_repr - }; - assert_eq!( - iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), - Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) - ); - - let socket = sockets.get_mut::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok(( - icmp_data, - IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) - )) - ); - } - - #[test] - #[cfg(feature = "proto-ipv6")] - fn test_solicited_node_addrs() { - let (mut iface, _, _device) = create(); - let mut new_addrs = vec![ - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), - ]; - iface.update_ip_addrs(|addrs| { - new_addrs.extend(addrs.to_vec()); - *addrs = From::from(new_addrs); - }); - assert!(iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); - assert!(iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); - assert!(!iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); - } - - #[test] - #[cfg(feature = "proto-ipv6")] - fn test_icmpv6_nxthdr_unknown() { - let (mut iface, mut sockets, _device) = create(); - - let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - - let payload = [0x12, 0x34, 0x56, 0x78]; - - let ipv6_repr = Ipv6Repr { - src_addr: remote_ip_addr, - dst_addr: Ipv6Address::LOOPBACK, - next_header: IpProtocol::HopByHop, - payload_len: 12, - hop_limit: 0x40, - }; - - let mut bytes = vec![0; 52]; - let frame = { - let ip_repr = IpRepr::Ipv6(ipv6_repr); - ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let mut offset = ipv6_repr.buffer_len(); - { - let mut hbh_pkt = Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); - hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); - hbh_pkt.set_header_len(0); - offset += 8; - { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut *hbh_pkt.options_mut()); - Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt); - } - { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.options_mut()[5..]); - Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); - } - } - bytes[offset..].copy_from_slice(&payload); - Ipv6Packet::new_unchecked(&bytes) - }; - - let reply_icmp_repr = Icmpv6Repr::ParamProblem { - reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, - pointer: 40, - header: ipv6_repr, - data: &payload[..], - }; - - let reply_ipv6_repr = Ipv6Repr { - src_addr: Ipv6Address::LOOPBACK, - dst_addr: remote_ip_addr, - next_header: IpProtocol::Icmpv6, - payload_len: reply_icmp_repr.buffer_len(), - hop_limit: 0x40, - }; - - // 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 sockets, &frame), - Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) - ); - } - - #[test] - #[cfg(feature = "proto-igmp")] - fn test_handle_igmp() { - fn recv_igmp(device: &mut Loopback, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { - let caps = device.capabilities(); - let checksum_caps = &caps.checksum; - recv_all(device, timestamp) - .iter() - .filter_map(|frame| { - let ipv4_packet = match caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - let eth_frame = EthernetFrame::new_checked(frame).ok()?; - Ipv4Packet::new_checked(eth_frame.payload()).ok()? - } - #[cfg(feature = "medium-ip")] - Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => todo!(), - }; - let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; - let ip_payload = ipv4_packet.payload(); - let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; - let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; - Some((ipv4_repr, igmp_repr)) - }) - .collect::>() - } - - let groups = [ - Ipv4Address::new(224, 0, 0, 22), - Ipv4Address::new(224, 0, 0, 56), - ]; - - let (mut iface, mut sockets, mut device) = create(); - - // Join multicast groups - let timestamp = Instant::now(); - for group in &groups { - iface - .join_multicast_group(&mut device, *group, timestamp) - .unwrap(); - } - - let reports = recv_igmp(&mut device, timestamp); - assert_eq!(reports.len(), 2); - for (i, group_addr) in groups.iter().enumerate() { - assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); - assert_eq!(reports[i].0.dst_addr, *group_addr); - assert_eq!( - reports[i].1, - IgmpRepr::MembershipReport { - group_addr: *group_addr, - version: IgmpVersion::Version2, - } - ); - } - - // General query - let timestamp = Instant::now(); - const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, - 0x63, 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]; - { - // Transmit GENERAL_QUERY_BYTES into loopback - let tx_token = device.transmit().unwrap(); - tx_token - .consume(timestamp, GENERAL_QUERY_BYTES.len(), |buffer| { - buffer.copy_from_slice(GENERAL_QUERY_BYTES); - Ok(()) - }) - .unwrap(); - } - // Trigger processing until all packets received through the - // 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 device, &mut sockets); - - // Leave multicast groups - let timestamp = Instant::now(); - for group in &groups { - iface - .leave_multicast_group(&mut device, *group, timestamp) - .unwrap(); - } - - let leaves = recv_igmp(&mut device, timestamp); - assert_eq!(leaves.len(), 2); - for (i, group_addr) in groups.iter().cloned().enumerate() { - assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); - assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); - assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); - } - } - - #[test] - #[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] - fn test_raw_socket_no_reply() { - use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - - let (mut iface, mut sockets, _device) = create(); - - let packets = 1; - let rx_buffer = - raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = raw::PacketBuffer::new( - vec![raw::PacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ); - let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - sockets.add(raw_socket); - - let src_addr = Ipv4Address([127, 0, 0, 2]); - let dst_addr = Ipv4Address([127, 0, 0, 1]); - - const PAYLOAD_LEN: usize = 10; - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - let ipv4_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + PAYLOAD_LEN, - }; - - // Emit to frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - udp_repr.emit( - &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - Some(&mut iface.fragments.ipv4_fragments) - ), - None - ); - } - - #[test] - #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] - fn test_raw_socket_with_udp_socket() { - use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - - static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - - let (mut iface, mut sockets, _device) = create(); - - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let udp_socket_handle = sockets.add(udp_socket); - - // Bind the socket to port 68 - let socket = sockets.get_mut::(udp_socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - - let packets = 1; - let raw_rx_buffer = - raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let raw_tx_buffer = raw::PacketBuffer::new( - vec![raw::PacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ); - let raw_socket = raw::Socket::new( - IpVersion::Ipv4, - IpProtocol::Udp, - raw_rx_buffer, - raw_tx_buffer, - ); - sockets.add(raw_socket); - - let src_addr = Ipv4Address([127, 0, 0, 2]); - let dst_addr = Ipv4Address([127, 0, 0, 1]); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - let ipv4_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - }; - - // Emit to frame - let mut bytes = - vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - udp_repr.emit( - &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &src_addr.into(), - &dst_addr.into(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - Some(&mut iface.fragments.ipv4_fragments) - ), - None - ); - - // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = sockets.get_mut::(udp_socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) - ); - } -} diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs new file mode 100644 index 000000000..4a9f86967 --- /dev/null +++ b/src/iface/interface/ethernet.rs @@ -0,0 +1,89 @@ +use super::check; +use super::EthernetPacket; +use super::FragmentsBuffer; +use super::InterfaceInner; +use super::SocketSet; + +use crate::phy::TxToken; +use crate::wire::*; +use crate::Error; +use crate::Result; + +impl<'i> InterfaceInner<'i> { + #[cfg(feature = "medium-ethernet")] + pub(super) fn process_ethernet<'frame, T: AsRef<[u8]>>( + &mut self, + sockets: &mut SocketSet, + frame: &'frame T, + _fragments: &'frame mut FragmentsBuffer<'i>, + ) -> Option> { + let eth_frame = check!(EthernetFrame::new_checked(frame)); + + // Ignore any packets not directed to our hardware address or any of the multicast groups. + if !eth_frame.dst_addr().is_broadcast() + && !eth_frame.dst_addr().is_multicast() + && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr.unwrap() + { + return None; + } + + match eth_frame.ethertype() { + #[cfg(feature = "proto-ipv4")] + EthernetProtocol::Arp => self.process_arp(self.now, ð_frame), + #[cfg(feature = "proto-ipv4")] + EthernetProtocol::Ipv4 => { + let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) + .map(EthernetPacket::Ip) + } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + self.process_ipv4(sockets, &ipv4_packet, None) + .map(EthernetPacket::Ip) + } + } + #[cfg(feature = "proto-ipv6")] + EthernetProtocol::Ipv6 => { + let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload())); + self.process_ipv6(sockets, &ipv6_packet) + .map(EthernetPacket::Ip) + } + // Drop all other traffic. + _ => None, + } + } + + #[cfg(feature = "medium-ethernet")] + pub(super) fn dispatch_ethernet( + &mut self, + 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(self.now, tx_len, |tx_buffer| { + debug_assert!(tx_buffer.as_ref().len() == tx_len); + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + + f(frame); + + Ok(()) + }) + } +} diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs new file mode 100644 index 000000000..37e7f860e --- /dev/null +++ b/src/iface/interface/ipv4.rs @@ -0,0 +1,565 @@ +use super::check; +use super::icmp_reply_payload_len; +use super::InterfaceInner; +use super::IpPacket; +use super::PacketAssemblerSet; +use super::SocketSet; + +#[cfg(feature = "proto-igmp")] +use super::IgmpReportState; + +#[cfg(feature = "medium-ethernet")] +use super::EthernetPacket; + +#[cfg(feature = "proto-ipv4-fragmentation")] +use super::Ipv4OutPacket; + +#[cfg(feature = "socket-dhcpv4")] +use crate::socket::dhcpv4; +#[cfg(feature = "socket-icmp")] +use crate::socket::icmp; +use crate::socket::AnySocket; + +use crate::phy::{Medium, TxToken}; +use crate::{time::*, wire::*, Error, Result}; + +impl<'a> InterfaceInner<'a> { + pub(super) fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + sockets: &mut SocketSet, + ipv4_packet: &Ipv4Packet<&'payload T>, + _fragments: Option<&'output mut PacketAssemblerSet<'a, Ipv4FragKey>>, + ) -> Option> { + let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); + if !self.is_unicast_v4(ipv4_repr.src_addr) { + // Discard packets with non-unicast source addresses. + net_debug!("non-unicast source address"); + return None; + } + + #[cfg(feature = "proto-ipv4-fragmentation")] + let ip_payload = { + const REASSEMBLY_TIMEOUT: u64 = 90; + + let fragments = _fragments.unwrap(); + + if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { + let key = ipv4_packet.get_key(); + + let f = match fragments.get_packet_assembler_mut(&key) { + Ok(f) => f, + Err(_) => { + let p = match fragments.reserve_with_key(&key) { + Ok(p) => p, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(p.start( + None, + self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), + 0 + )); + + check!(fragments.get_packet_assembler_mut(&key)) + } + }; + + if !ipv4_packet.more_frags() { + // This is the last fragment, so we know the total size + check!(f.set_total_size( + ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize + + ipv4_packet.frag_offset() as usize, + )); + } + + match f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { + Ok(true) => { + // NOTE: according to the standard, the total length needs to be + // recomputed, as well as the checksum. However, we don't really use + // the IPv4 header after the packet is reassembled. + check!(fragments.get_assembled_packet(&key)) + } + Ok(false) => { + return None; + } + Err(Error::PacketAssemblerOverlap) => { + return None; + } + Err(e) => { + net_debug!("fragmentation error: {}", e); + return None; + } + } + } else { + ipv4_packet.payload() + } + }; + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + let ip_payload = ipv4_packet.payload(); + + let ip_repr = IpRepr::Ipv4(ipv4_repr); + + #[cfg(feature = "socket-raw")] + let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload); + #[cfg(not(feature = "socket-raw"))] + let handled_by_raw_socket = false; + + #[cfg(feature = "socket-dhcpv4")] + { + if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { + // First check for source and dest ports, then do `UdpRepr::parse` if they match. + // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + if udp_packet.src_port() == DHCP_SERVER_PORT + && udp_packet.dst_port() == DHCP_CLIENT_PORT + { + if let Some(dhcp_socket) = sockets + .items_mut() + .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) + { + let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &src_addr, + &dst_addr, + &self.caps.checksum + )); + let udp_payload = udp_packet.payload(); + + dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload); + return None; + } + } + } + } + + if !self.has_ip_addr(ipv4_repr.dst_addr) + && !self.has_multicast_group(ipv4_repr.dst_addr) + && !self.is_broadcast_v4(ipv4_repr.dst_addr) + { + // 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 + || !ipv4_repr.dst_addr.is_unicast() + || self + .routes + .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now) + .map_or(true, |router_addr| !self.has_ip_addr(router_addr)) + { + return None; + } + } + + match ipv4_repr.next_header { + IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload), + + #[cfg(feature = "proto-igmp")] + IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload), + + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + IpProtocol::Udp => { + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &ipv4_repr.src_addr.into(), + &ipv4_repr.dst_addr.into(), + &self.checksum_caps(), + )); + + self.process_udp( + sockets, + ip_repr, + udp_repr, + handled_by_raw_socket, + udp_packet.payload(), + ip_payload, + ) + } + + #[cfg(feature = "socket-tcp")] + IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload), + + _ if handled_by_raw_socket => None, + + _ => { + // Send back as much of the original payload as we can. + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); + let icmp_reply_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::ProtoUnreachable, + header: ipv4_repr, + data: &ip_payload[0..payload_len], + }; + self.icmpv4_reply(ipv4_repr, icmp_reply_repr) + } + } + } + + #[cfg(feature = "medium-ethernet")] + pub(super) fn process_arp<'frame, T: AsRef<[u8]>>( + &mut self, + timestamp: Instant, + eth_frame: &EthernetFrame<&'frame T>, + ) -> Option> { + let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload())); + let arp_repr = check!(ArpRepr::parse(&arp_packet)); + + match arp_repr { + ArpRepr::EthernetIpv4 { + operation, + source_hardware_addr, + source_protocol_addr, + target_protocol_addr, + .. + } => { + // Only process ARP packets for us. + if !self.has_ip_addr(target_protocol_addr) { + return None; + } + + // Only process REQUEST and RESPONSE. + if let ArpOperation::Unknown(_) = operation { + net_debug!("arp: unknown operation code"); + return None; + } + + // Discard packets with non-unicast source addresses. + if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() { + net_debug!("arp: non-unicast source address"); + return None; + } + + if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) { + net_debug!("arp: source IP address not in same network as us"); + return None; + } + + // Fill the ARP cache from any ARP packet aimed at us (both request or response). + // We fill from requests too because if someone is requesting our address they + // are probably going to talk to us, so we avoid having to request their address + // when we later reply to them. + self.neighbor_cache.as_mut().unwrap().fill( + source_protocol_addr.into(), + source_hardware_addr.into(), + timestamp, + ); + + if operation == ArpOperation::Request { + let src_hardware_addr = match self.hardware_addr { + Some(HardwareAddress::Ethernet(addr)) => addr, + _ => unreachable!(), + }; + + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: src_hardware_addr, + source_protocol_addr: target_protocol_addr, + target_hardware_addr: source_hardware_addr, + target_protocol_addr: source_protocol_addr, + })) + } else { + None + } + } + } + } + + /// Host duties of the **IGMPv2** protocol. + /// + /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. + /// 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")] + pub(super) fn process_igmp<'frame>( + &mut self, + ipv4_repr: Ipv4Repr, + ip_payload: &'frame [u8], + ) -> Option> { + let igmp_packet = check!(IgmpPacket::new_checked(ip_payload)); + let igmp_repr = check!(IgmpRepr::parse(&igmp_packet)); + + // FIXME: report membership after a delay + match igmp_repr { + IgmpRepr::MembershipQuery { + group_addr, + version, + max_resp_time, + } => { + // General query + if group_addr.is_unspecified() + && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS + { + // Are we member in any groups? + if self.ipv4_multicast_groups.iter().next().is_some() { + let interval = match version { + IgmpVersion::Version1 => Duration::from_millis(100), + IgmpVersion::Version2 => { + // No dependence on a random generator + // (see [#24](https://github.com/m-labs/smoltcp/issues/24)) + // but at least spread reports evenly across max_resp_time. + let intervals = self.ipv4_multicast_groups.len() as u32 + 1; + max_resp_time / intervals + } + }; + self.igmp_report_state = IgmpReportState::ToGeneralQuery { + version, + timeout: self.now + interval, + interval, + next_index: 0, + }; + } + } else { + // Group-specific query + if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr { + // Don't respond immediately + let timeout = max_resp_time / 4; + self.igmp_report_state = IgmpReportState::ToSpecificQuery { + version, + timeout: self.now + timeout, + group: group_addr, + }; + } + } + } + // Ignore membership reports + IgmpRepr::MembershipReport { .. } => (), + // Ignore hosts leaving groups + IgmpRepr::LeaveGroup { .. } => (), + } + + None + } + + pub(super) fn process_icmpv4<'frame>( + &mut self, + _sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Option> { + let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload)); + let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum)); + + #[cfg(feature = "socket-icmp")] + let mut handled_by_icmp_socket = false; + + #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] + for icmp_socket in _sockets + .items_mut() + .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) + { + if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { + icmp_socket.process(self, &ip_repr, &icmp_repr.into()); + handled_by_icmp_socket = true; + } + } + + match icmp_repr { + // Respond to echo requests. + #[cfg(feature = "proto-ipv4")] + Icmpv4Repr::EchoRequest { + ident, + seq_no, + data, + } => { + let icmp_reply_repr = Icmpv4Repr::EchoReply { + ident, + seq_no, + data, + }; + match ip_repr { + IpRepr::Ipv4(ipv4_repr) => self.icmpv4_reply(ipv4_repr, icmp_reply_repr), + #[allow(unreachable_patterns)] + _ => unreachable!(), + } + } + + // Ignore any echo replies. + Icmpv4Repr::EchoReply { .. } => None, + + // Don't report an error if a packet with unknown type + // has been handled by an ICMP socket + #[cfg(feature = "socket-icmp")] + _ if handled_by_icmp_socket => None, + + // FIXME: do something correct here? + _ => None, + } + } + + pub(super) fn icmpv4_reply<'frame, 'icmp: 'frame>( + &self, + ipv4_repr: Ipv4Repr, + icmp_repr: Icmpv4Repr<'icmp>, + ) -> Option> { + if !self.is_unicast_v4(ipv4_repr.src_addr) { + // Do not send ICMP replies to non-unicast sources + None + } else if self.is_unicast_v4(ipv4_repr.dst_addr) { + // Reply as normal when src_addr and dst_addr are both unicast + let ipv4_reply_repr = Ipv4Repr { + src_addr: ipv4_repr.dst_addr, + dst_addr: ipv4_repr.src_addr, + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }; + Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) + } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { + // Only reply to broadcasts for echo replies and not other ICMP messages + match icmp_repr { + Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() { + Some(src_addr) => { + let ipv4_reply_repr = Ipv4Repr { + src_addr, + dst_addr: ipv4_repr.src_addr, + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }; + Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr))) + } + None => None, + }, + _ => None, + } + } else { + None + } + } + + #[cfg(feature = "proto-ipv4-fragmentation")] + pub(super) fn dispatch_ipv4_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut Ipv4OutPacket, + ) -> Result<()> { + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr, + dst_hardware_addr, + frag_offset, + ident, + .. + } = out_packet; + + let caps = self.caps.clone(); + + let mtu_max = self.ip_mtu(); + let ip_len = (*packet_len - *sent_bytes + repr.buffer_len()).min(mtu_max); + let payload_len = ip_len - repr.buffer_len(); + + let more_frags = (*packet_len - *sent_bytes) != payload_len; + repr.payload_len = payload_len; + *sent_bytes += payload_len; + + let mut tx_len = ip_len; + #[cfg(feature = "medium-ethernet")] + if matches!(caps.medium, Medium::Ethernet) { + tx_len += EthernetFrame::<&[u8]>::header_len(); + } + + // Emit function for the Ethernet header. + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(*dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), + } + + Ok(()) + }; + + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..repr.buffer_len()]); + repr.emit(&mut packet, &caps.checksum); + packet.set_ident(*ident); + packet.set_more_frags(more_frags); + packet.set_dont_frag(false); + packet.set_frag_offset(*frag_offset); + + if caps.checksum.ipv4.tx() { + packet.fill_checksum(); + } + + tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( + &buffer[*frag_offset as usize + repr.buffer_len() as usize..][..payload_len], + ); + + // Update the frag offset for the next fragment. + *frag_offset += payload_len as u16; + + Ok(()) + }) + } + + #[cfg(feature = "proto-igmp")] + pub(super) fn igmp_report_packet<'any>( + &self, + version: IgmpVersion, + group_addr: Ipv4Address, + ) -> Option> { + let iface_addr = self.ipv4_address()?; + let igmp_repr = IgmpRepr::MembershipReport { + group_addr, + version, + }; + let pkt = IpPacket::Igmp(( + Ipv4Repr { + src_addr: iface_addr, + // Send to the group being reported + dst_addr: group_addr, + next_header: IpProtocol::Igmp, + payload_len: igmp_repr.buffer_len(), + hop_limit: 1, + // [#183](https://github.com/m-labs/smoltcp/issues/183). + }, + igmp_repr, + )); + Some(pkt) + } + + #[cfg(feature = "proto-igmp")] + pub(super) fn igmp_leave_packet<'any>( + &self, + group_addr: Ipv4Address, + ) -> Option> { + self.ipv4_address().map(|iface_addr| { + let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; + IpPacket::Igmp(( + Ipv4Repr { + src_addr: iface_addr, + dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS, + next_header: IpProtocol::Igmp, + payload_len: igmp_repr.buffer_len(), + hop_limit: 1, + }, + igmp_repr, + )) + }) + } +} diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs new file mode 100644 index 000000000..529db9878 --- /dev/null +++ b/src/iface/interface/ipv6.rs @@ -0,0 +1,308 @@ +use super::check; +use super::icmp_reply_payload_len; +use super::InterfaceInner; +use super::IpPacket; +use super::SocketSet; + +#[cfg(feature = "socket-icmp")] +use crate::socket::icmp; +use crate::socket::AnySocket; + +use crate::wire::*; + +impl<'a> InterfaceInner<'a> { + #[cfg(feature = "proto-ipv6")] + pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( + &mut self, + sockets: &mut SocketSet, + ipv6_packet: &Ipv6Packet<&'frame T>, + ) -> Option> { + let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet)); + + if !ipv6_repr.src_addr.is_unicast() { + // Discard packets with non-unicast source addresses. + net_debug!("non-unicast source address"); + return None; + } + + 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); + #[cfg(not(feature = "socket-raw"))] + let handled_by_raw_socket = false; + + self.process_nxt_hdr( + sockets, + ipv6_repr, + ipv6_repr.next_header, + handled_by_raw_socket, + ip_payload, + ) + } + + /// Given the next header value forward the payload onto the correct process + /// function. + #[cfg(feature = "proto-ipv6")] + pub(super) fn process_nxt_hdr<'frame>( + &mut self, + sockets: &mut SocketSet, + ipv6_repr: Ipv6Repr, + nxt_hdr: IpProtocol, + handled_by_raw_socket: bool, + ip_payload: &'frame [u8], + ) -> Option> { + match nxt_hdr { + IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr.into(), ip_payload), + + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + IpProtocol::Udp => { + let udp_packet = check!(UdpPacket::new_checked(ip_payload)); + let udp_repr = check!(UdpRepr::parse( + &udp_packet, + &ipv6_repr.src_addr.into(), + &ipv6_repr.dst_addr.into(), + &self.checksum_caps(), + )); + + self.process_udp( + sockets, + ipv6_repr.into(), + udp_repr, + handled_by_raw_socket, + udp_packet.payload(), + ip_payload, + ) + } + + #[cfg(feature = "socket-tcp")] + IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), + + IpProtocol::HopByHop => { + self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload) + } + + #[cfg(feature = "socket-raw")] + _ if handled_by_raw_socket => None, + + _ => { + // Send back as much of the original payload as we can. + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); + let icmp_reply_repr = Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, + // The offending packet is after the IPv6 header. + pointer: ipv6_repr.buffer_len() as u32, + header: ipv6_repr, + data: &ip_payload[0..payload_len], + }; + self.icmpv6_reply(ipv6_repr, icmp_reply_repr) + } + } + } + + #[cfg(feature = "proto-ipv6")] + pub(super) fn process_icmpv6<'frame>( + &mut self, + _sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Option> { + let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload)); + let icmp_repr = check!(Icmpv6Repr::parse( + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + &icmp_packet, + &self.caps.checksum, + )); + + #[cfg(feature = "socket-icmp")] + let mut handled_by_icmp_socket = false; + + #[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))] + for icmp_socket in _sockets + .items_mut() + .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket)) + { + if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) { + icmp_socket.process(self, &ip_repr, &icmp_repr.into()); + handled_by_icmp_socket = true; + } + } + + match icmp_repr { + // Respond to echo requests. + Icmpv6Repr::EchoRequest { + ident, + seq_no, + data, + } => match ip_repr { + IpRepr::Ipv6(ipv6_repr) => { + let icmp_reply_repr = Icmpv6Repr::EchoReply { + ident, + seq_no, + data, + }; + self.icmpv6_reply(ipv6_repr, icmp_reply_repr) + } + #[allow(unreachable_patterns)] + _ => unreachable!(), + }, + + // Ignore any echo replies. + Icmpv6Repr::EchoReply { .. } => None, + + // Forward any NDISC packets to the ndisc packet handler + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { + IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(ipv6_repr, repr), + #[allow(unreachable_patterns)] + _ => unreachable!(), + }, + + // Don't report an error if a packet with unknown type + // has been handled by an ICMP socket + #[cfg(feature = "socket-icmp")] + _ if handled_by_icmp_socket => None, + + // FIXME: do something correct here? + _ => None, + } + } + + #[cfg(all( + any(feature = "medium-ethernet", feature = "medium-ieee802154"), + feature = "proto-ipv6" + ))] + pub(super) fn process_ndisc<'frame>( + &mut self, + ip_repr: Ipv6Repr, + repr: NdiscRepr<'frame>, + ) -> Option> { + match repr { + NdiscRepr::NeighborAdvert { + lladdr, + target_addr, + flags, + } => { + let ip_addr = ip_repr.src_addr.into(); + if let Some(lladdr) = lladdr { + let lladdr = check!(lladdr.parse(self.caps.medium)); + if !lladdr.is_unicast() || !target_addr.is_unicast() { + return None; + } + if flags.contains(NdiscNeighborFlags::OVERRIDE) + || !self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&ip_addr, self.now) + .found() + { + self.neighbor_cache + .as_mut() + .unwrap() + .fill(ip_addr, lladdr, self.now) + } + } + None + } + NdiscRepr::NeighborSolicit { + target_addr, + lladdr, + .. + } => { + if let Some(lladdr) = lladdr { + let lladdr = check!(lladdr.parse(self.caps.medium)); + if !lladdr.is_unicast() || !target_addr.is_unicast() { + return None; + } + self.neighbor_cache.as_mut().unwrap().fill( + ip_repr.src_addr.into(), + lladdr, + self.now, + ); + } + + if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { + let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + lladdr: Some(self.hardware_addr.unwrap().into()), + }); + let ip_repr = Ipv6Repr { + src_addr: target_addr, + dst_addr: ip_repr.src_addr, + next_header: IpProtocol::Icmpv6, + hop_limit: 0xff, + payload_len: advert.buffer_len(), + }; + Some(IpPacket::Icmpv6((ip_repr, advert))) + } else { + None + } + } + _ => None, + } + } + + #[cfg(feature = "proto-ipv6")] + pub(super) fn process_hopbyhop<'frame>( + &mut self, + sockets: &mut SocketSet, + ipv6_repr: Ipv6Repr, + handled_by_raw_socket: bool, + ip_payload: &'frame [u8], + ) -> Option> { + let hbh_pkt = check!(Ipv6HopByHopHeader::new_checked(ip_payload)); + let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_pkt)); + for opt_repr in hbh_repr.options() { + let opt_repr = check!(opt_repr); + match opt_repr { + Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), + Ipv6OptionRepr::Unknown { type_, .. } => { + match Ipv6OptionFailureType::from(type_) { + Ipv6OptionFailureType::Skip => (), + Ipv6OptionFailureType::Discard => { + return None; + } + _ => { + // FIXME(dlrobertson): Send an ICMPv6 parameter problem message + // here. + return None; + } + } + } + } + } + self.process_nxt_hdr( + sockets, + ipv6_repr, + hbh_repr.next_header, + handled_by_raw_socket, + &ip_payload[hbh_repr.buffer_len()..], + ) + } + + #[cfg(feature = "proto-ipv6")] + pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>( + &self, + ipv6_repr: Ipv6Repr, + icmp_repr: Icmpv6Repr<'icmp>, + ) -> Option> { + if ipv6_repr.dst_addr.is_unicast() { + let ipv6_reply_repr = Ipv6Repr { + src_addr: ipv6_repr.dst_addr, + dst_addr: ipv6_repr.src_addr, + next_header: IpProtocol::Icmpv6, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }; + Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) + } else { + // Do not send any ICMP replies to a broadcast destination address. + None + } + } +} diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs new file mode 100644 index 000000000..b604c2397 --- /dev/null +++ b/src/iface/interface/mod.rs @@ -0,0 +1,2279 @@ +// Heads up! Before working on this file you should read the parts +// of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work +// and RFCs 8200 and 4861 for any IPv6 and NDISC work. + +#[cfg(test)] +mod tests; + +#[cfg(feature = "medium-ethernet")] +mod ethernet; +#[cfg(feature = "proto-sixlowpan")] +mod sixlowpan; + +#[cfg(feature = "proto-ipv4")] +mod ipv4; +#[cfg(feature = "proto-ipv6")] +mod ipv6; + +use core::cmp; +use managed::{ManagedMap, ManagedSlice}; + +#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] +use super::fragmentation::PacketAssemblerSet; +use super::socket_set::SocketSet; +use crate::iface::Routes; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +use crate::iface::{NeighborAnswer, NeighborCache}; +use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; +use crate::rand::Rand; +#[cfg(feature = "socket-dns")] +use crate::socket::dns; +use crate::socket::*; +use crate::time::{Duration, Instant}; +use crate::wire::*; +use crate::{Error, Result}; + +pub(crate) struct FragmentsBuffer<'a> { + #[cfg(feature = "proto-ipv4-fragmentation")] + pub(crate) ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: Duration, + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] + _lifetime: core::marker::PhantomData<&'a ()>, +} + +pub(crate) struct OutPackets<'a> { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket<'a>, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_out_packet: SixlowpanOutPacket<'a>, + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + _lifetime: core::marker::PhantomData<&'a ()>, +} + +impl<'a> OutPackets<'a> { + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] + /// Returns `true` when all the data of the outgoing buffers are transmitted. + fn all_transmitted(&self) -> bool { + #[cfg(feature = "proto-ipv4-fragmentation")] + if !self.ipv4_out_packet.is_empty() { + return false; + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if !self.sixlowpan_out_packet.is_empty() { + return false; + } + + true + } +} + +#[allow(unused)] +#[cfg(feature = "proto-ipv4")] +pub(crate) struct Ipv4OutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The IPv4 representation. + repr: Ipv4Repr, + /// The destination hardware address. + dst_hardware_addr: EthernetAddress, + /// The offset of the next fragment. + frag_offset: u16, + /// The identifier of the stream. + ident: u16, +} + +#[cfg(feature = "proto-ipv4-fragmentation")] +impl<'a> Ipv4OutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + sent_bytes: 0, + repr: Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }, + dst_hardware_addr: EthernetAddress::default(), + frag_offset: 0, + ident: 0, + } + } + + /// Return `true` when everything is transmitted. + #[inline] + fn finished(&self) -> bool { + self.packet_len == self.sent_bytes + } + + /// Returns `true` when there is nothing to transmit. + #[inline] + fn is_empty(&self) -> bool { + self.packet_len == 0 + } + + // Reset the buffer. + fn reset(&mut self) { + self.packet_len = 0; + self.sent_bytes = 0; + self.repr = Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }; + self.dst_hardware_addr = EthernetAddress::default(); + } +} + +#[allow(unused)] +#[cfg(feature = "proto-sixlowpan")] +pub(crate) struct SixlowpanOutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The datagram size that is used for the fragmentation headers. + datagram_size: u16, + /// The datagram tag that is used for the fragmentation headers. + datagram_tag: u16, + datagram_offset: usize, + + /// The size of the FRAG_N packets. + fragn_size: usize, + + /// The link layer IEEE802.15.4 source address. + ll_dst_addr: Ieee802154Address, + /// The link layer IEEE802.15.4 source address. + ll_src_addr: Ieee802154Address, +} + +#[cfg(feature = "proto-sixlowpan-fragmentation")] +impl<'a> SixlowpanOutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + datagram_size: 0, + datagram_tag: 0, + datagram_offset: 0, + sent_bytes: 0, + fragn_size: 0, + ll_dst_addr: Ieee802154Address::Absent, + ll_src_addr: Ieee802154Address::Absent, + } + } + + /// Return `true` when everything is transmitted. + #[inline] + fn finished(&self) -> bool { + self.packet_len == self.sent_bytes + } + + /// Returns `true` when there is nothing to transmit. + #[inline] + fn is_empty(&self) -> bool { + self.packet_len == 0 + } + + // Reset the buffer. + fn reset(&mut self) { + self.packet_len = 0; + self.datagram_size = 0; + self.datagram_tag = 0; + self.sent_bytes = 0; + self.fragn_size = 0; + self.ll_dst_addr = Ieee802154Address::Absent; + self.ll_src_addr = Ieee802154Address::Absent; + } +} + +macro_rules! check { + ($e:expr) => { + match $e { + Ok(x) => x, + Err(_) => { + // concat!/stringify! doesn't work with defmt macros + #[cfg(not(feature = "defmt"))] + net_trace!(concat!("iface: malformed ", stringify!($e))); + #[cfg(feature = "defmt")] + net_trace!("iface: malformed"); + return Default::default(); + } + } + }; +} +use check; + +/// A network interface. +/// +/// The network interface logically owns a number of other data structures; to avoid +/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be +/// a `&mut [T]`, or `Vec` if a heap is available. +pub struct Interface<'a> { + inner: InterfaceInner<'a>, + fragments: FragmentsBuffer<'a>, + out_packets: OutPackets<'a>, +} + +/// The device independent part of an Ethernet network interface. +/// +/// Separating the device from the data required for processing and dispatching makes +/// it possible to borrow them independently. For example, the tx and rx tokens borrow +/// the `device` mutably until they're used, which makes it impossible to call other +/// methods on the `Interface` in this time (since its `device` field is borrowed +/// exclusively). However, it is still possible to call methods on its `inner` field. +pub struct InterfaceInner<'a> { + caps: DeviceCapabilities, + now: Instant, + rand: Rand, + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: Option>, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Option, + #[cfg(feature = "medium-ieee802154")] + sequence_no: u8, + #[cfg(feature = "medium-ieee802154")] + pan_id: Option, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: u16, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + #[cfg(feature = "proto-sixlowpan-fragmentation")] + tag: u16, + ip_addrs: ManagedSlice<'a, IpCidr>, + #[cfg(feature = "proto-ipv4")] + any_ip: bool, + routes: Routes<'a>, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + /// When to report for (all or) the next multicast group membership via IGMP + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState, +} + +/// A builder structure used for creating a network interface. +pub struct InterfaceBuilder<'a> { + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Option, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: Option>, + #[cfg(feature = "medium-ieee802154")] + pan_id: Option, + ip_addrs: ManagedSlice<'a, IpCidr>, + #[cfg(feature = "proto-ipv4")] + any_ip: bool, + routes: Routes<'a>, + /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + random_seed: u64, + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice<'a, u8>, + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_reassembly_buffer_timeout: Duration, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_out_buffer: ManagedSlice<'a, u8>, + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], +} + +impl<'a> InterfaceBuilder<'a> { + /// Create a builder used for creating a network interface using the + /// given device and address. + #[cfg_attr( + all(feature = "medium-ethernet", not(feature = "proto-sixlowpan")), + doc = r##" +# Examples + +``` +# use std::collections::BTreeMap; +#[cfg(feature = "proto-ipv4-fragmentation")] +use smoltcp::iface::FragmentsCache; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +# use smoltcp::phy::{Loopback, Medium}; +use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; + +let mut device = // ... +# Loopback::new(Medium::Ethernet); +let hw_addr = // ... +# EthernetAddress::default(); +let neighbor_cache = // ... +# NeighborCache::new(BTreeMap::new()); +# #[cfg(feature = "proto-ipv4-fragmentation")] +# let ipv4_frag_cache = // ... +# FragmentsCache::new(vec![], BTreeMap::new()); +let ip_addrs = // ... +# []; +let builder = InterfaceBuilder::new() + .hardware_addr(hw_addr.into()) + .neighbor_cache(neighbor_cache) + .ip_addrs(ip_addrs); + +# #[cfg(feature = "proto-ipv4-fragmentation")] +let builder = builder + .ipv4_reassembly_buffer(ipv4_frag_cache) + .ipv4_fragmentation_buffer(vec![]); + +let iface = builder.finalize(&mut device); +``` + "## + )] + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + InterfaceBuilder { + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: None, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: None, + + #[cfg(feature = "medium-ieee802154")] + pan_id: None, + + ip_addrs: ManagedSlice::Borrowed(&mut []), + #[cfg(feature = "proto-ipv4")] + any_ip: false, + routes: Routes::new(ManagedMap::Borrowed(&mut [])), + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + random_seed: 0, + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], + } + } + + /// Set the random seed for this interface. + /// + /// It is strongly recommended that the random seed is different on each boot, + /// to avoid problems with TCP port/sequence collisions. + /// + /// The seed doesn't have to be cryptographically secure. + pub fn random_seed(mut self, random_seed: u64) -> Self { + self.random_seed = random_seed; + self + } + + /// Set the Hardware address the interface will use. See also + /// [hardware_addr]. + /// + /// # Panics + /// This function panics if the address is not unicast. + /// + /// [hardware_addr]: struct.Interface.html#method.hardware_addr + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn hardware_addr(mut self, addr: HardwareAddress) -> Self { + InterfaceInner::check_hardware_addr(&addr); + self.hardware_addr = Some(addr); + self + } + + /// Set the IEEE802.15.4 PAN ID the interface will use. + /// + /// **NOTE**: we use the same PAN ID for destination and source. + #[cfg(feature = "medium-ieee802154")] + pub fn pan_id(mut self, pan_id: Ieee802154Pan) -> Self { + self.pan_id = Some(pan_id); + self + } + + /// Set the IP addresses the interface will use. See also + /// [ip_addrs]. + /// + /// # Panics + /// This function panics if any of the addresses are not unicast. + /// + /// [ip_addrs]: struct.Interface.html#method.ip_addrs + pub fn ip_addrs(mut self, ip_addrs: T) -> Self + where + T: Into>, + { + let ip_addrs = ip_addrs.into(); + InterfaceInner::check_ip_addrs(&ip_addrs); + self.ip_addrs = ip_addrs; + self + } + + /// Enable or disable the AnyIP capability, allowing packets to be received + /// locally on IPv4 addresses other than the interface's configured [ip_addrs]. + /// When AnyIP is enabled and a route prefix in [routes] specifies one of + /// the interface's [ip_addrs] as its gateway, the interface will accept + /// packets addressed to that prefix. + /// + /// # IPv6 + /// + /// This option is not available or required for IPv6 as packets sent to + /// the interface are not filtered by IPv6 address. + /// + /// [routes]: struct.Interface.html#method.routes + /// [ip_addrs]: struct.Interface.html#method.ip_addrs + #[cfg(feature = "proto-ipv4")] + pub fn any_ip(mut self, enabled: bool) -> Self { + self.any_ip = enabled; + self + } + + /// Set the IP routes the interface will use. See also + /// [routes]. + /// + /// [routes]: struct.Interface.html#method.routes + pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a> + where + T: Into>, + { + self.routes = routes.into(); + self + } + + /// Provide storage for multicast groups. + /// + /// Join multicast groups by calling [`join_multicast_group()`] on an `Interface`. + /// Using [`join_multicast_group()`] will send initial membership reports. + /// + /// A previously destroyed interface can be recreated by reusing the multicast group + /// storage, i.e. providing a non-empty storage to `ipv4_multicast_groups()`. + /// Note that this way initial membership reports are **not** sent. + /// + /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group + #[cfg(feature = "proto-igmp")] + pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self + where + T: Into>, + { + self.ipv4_multicast_groups = ipv4_multicast_groups.into(); + self + } + + /// Set the Neighbor Cache the interface will use. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { + self.neighbor_cache = Some(neighbor_cache); + self + } + + /// Set the IPv4 reassembly buffer the interface will use. + #[cfg(feature = "proto-ipv4-fragmentation")] + pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { + self.ipv4_fragments = storage; + self + } + + /// Set the IPv4 fragments buffer the interface will use. + #[cfg(feature = "proto-ipv4-fragmentation")] + pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self + where + T: Into>, + { + self.ipv4_out_buffer = storage.into(); + self + } + + /// Set the address contexts the interface will use. + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_address_context( + mut self, + sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + ) -> Self { + self.sixlowpan_address_context = sixlowpan_address_context; + self + } + + /// Set the 6LoWPAN reassembly buffer the interface will use. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn sixlowpan_reassembly_buffer( + mut self, + storage: PacketAssemblerSet<'a, SixlowpanFragKey>, + ) -> Self { + self.sixlowpan_fragments = storage; + self + } + + /// Set the timeout value the 6LoWPAN reassembly buffer will use. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { + if timeout > Duration::from_secs(60) { + net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); + } + self.sixlowpan_reassembly_buffer_timeout = timeout; + self + } + + /// Set the 6LoWPAN fragments buffer the interface will use. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self + where + T: Into>, + { + self.sixlowpan_out_buffer = storage.into(); + self + } + + /// Create a network interface using the previously provided configuration. + /// + /// # Panics + /// If a required option is not provided, this function will panic. Required + /// options are: + /// + /// - [ethernet_addr] + /// - [neighbor_cache] + /// + /// [ethernet_addr]: #method.ethernet_addr + /// [neighbor_cache]: #method.neighbor_cache + pub fn finalize(self, device: &mut D) -> Interface<'a> + where + D: for<'d> Device<'d> + ?Sized, + { + let caps = device.capabilities(); + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + let (hardware_addr, neighbor_cache) = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => ( + Some( + self.hardware_addr + .expect("hardware_addr required option was not set"), + ), + Some( + self.neighbor_cache + .expect("neighbor_cache required option was not set"), + ), + ), + #[cfg(feature = "medium-ip")] + Medium::Ip => { + assert!( + self.hardware_addr.is_none(), + "hardware_addr is set, but device medium is IP" + ); + assert!( + self.neighbor_cache.is_none(), + "neighbor_cache is set, but device medium is IP" + ); + (None, None) + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => ( + Some( + self.hardware_addr + .expect("hardware_addr required option was not set"), + ), + Some( + self.neighbor_cache + .expect("neighbor_cache required option was not set"), + ), + ), + }; + + let mut rand = Rand::new(self.random_seed); + + #[cfg(feature = "medium-ieee802154")] + let mut sequence_no; + #[cfg(feature = "medium-ieee802154")] + loop { + sequence_no = (rand.rand_u32() & 0xff) as u8; + if sequence_no != 0 { + break; + } + } + + #[cfg(feature = "proto-sixlowpan")] + let mut tag; + + #[cfg(feature = "proto-sixlowpan")] + loop { + tag = rand.rand_u16(); + if tag != 0 { + break; + } + } + + #[cfg(feature = "proto-ipv4")] + let mut ipv4_id; + + #[cfg(feature = "proto-ipv4")] + loop { + ipv4_id = rand.rand_u16(); + if ipv4_id != 0 { + break; + } + } + + Interface { + fragments: FragmentsBuffer { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: self.ipv4_fragments, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments: self.sixlowpan_fragments, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, + + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] + _lifetime: core::marker::PhantomData, + }, + out_packets: OutPackets { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket::new(self.ipv4_out_buffer), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_out_packet: SixlowpanOutPacket::new(self.sixlowpan_out_buffer), + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + _lifetime: core::marker::PhantomData, + }, + inner: InterfaceInner { + now: Instant::from_secs(0), + caps, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr, + ip_addrs: self.ip_addrs, + #[cfg(feature = "proto-ipv4")] + any_ip: self.any_ip, + routes: self.routes, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: self.ipv4_multicast_groups, + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + #[cfg(feature = "medium-ieee802154")] + sequence_no, + #[cfg(feature = "medium-ieee802154")] + pan_id: self.pan_id, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + tag, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], + rand, + }, + } + } +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg(feature = "medium-ethernet")] +enum EthernetPacket<'a> { + #[cfg(feature = "proto-ipv4")] + Arp(ArpRepr), + Ip(IpPacket<'a>), +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) enum IpPacket<'a> { + #[cfg(feature = "proto-ipv4")] + Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)), + #[cfg(feature = "proto-igmp")] + Igmp((Ipv4Repr, IgmpRepr)), + #[cfg(feature = "proto-ipv6")] + Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)), + #[cfg(feature = "socket-raw")] + Raw((IpRepr, &'a [u8])), + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + Udp((IpRepr, UdpRepr, &'a [u8])), + #[cfg(feature = "socket-tcp")] + Tcp((IpRepr, TcpRepr<'a>)), + #[cfg(feature = "socket-dhcpv4")] + Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)), +} + +impl<'a> IpPacket<'a> { + pub(crate) fn ip_repr(&self) -> IpRepr { + match self { + #[cfg(feature = "proto-ipv4")] + IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), + #[cfg(feature = "proto-igmp")] + IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr), + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr), + #[cfg(feature = "socket-raw")] + IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), + #[cfg(feature = "socket-dhcpv4")] + IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr), + } + } + + pub(crate) fn emit_payload( + &self, + _ip_repr: &IpRepr, + payload: &mut [u8], + caps: &DeviceCapabilities, + ) { + match self { + #[cfg(feature = "proto-ipv4")] + IpPacket::Icmpv4((_, icmpv4_repr)) => { + icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum) + } + #[cfg(feature = "proto-igmp")] + IpPacket::Igmp((_, igmp_repr)) => { + igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)) + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit( + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + &mut Icmpv6Packet::new_unchecked(payload), + &caps.checksum, + ), + #[cfg(feature = "socket-raw")] + IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit( + &mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + inner_payload.len(), + |buf| buf.copy_from_slice(inner_payload), + &caps.checksum, + ), + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, mut tcp_repr)) => { + // This is a terrible hack to make TCP performance more acceptable on systems + // where the TCP buffers are significantly larger than network buffers, + // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window) + // together with four 1500 B Ethernet receive buffers. If left untreated, + // this would result in our peer pushing our window and sever packet loss. + // + // I'm really not happy about this "solution" but I don't know what else to do. + if let Some(max_burst_size) = caps.max_burst_size { + let mut max_segment_size = caps.max_transmission_unit; + max_segment_size -= _ip_repr.header_len(); + max_segment_size -= tcp_repr.header_len(); + + let max_window_size = max_burst_size * max_segment_size; + if tcp_repr.window_len as usize > max_window_size { + tcp_repr.window_len = max_window_size as u16; + } + } + + tcp_repr.emit( + &mut TcpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + &caps.checksum, + ); + } + #[cfg(feature = "socket-dhcpv4")] + IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit( + &mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + dhcp_repr.buffer_len(), + |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), + &caps.checksum, + ), + } + } +} + +#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] +fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize { + // Send back as much of the original payload as will fit within + // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for + // more details. + // + // Since the entire network layer packet must fit within the minimum + // MTU supported, the payload must not exceed the following: + // + // - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size + cmp::min(len, mtu - header_len * 2 - 8) +} + +#[cfg(feature = "proto-igmp")] +enum IgmpReportState { + Inactive, + ToGeneralQuery { + version: IgmpVersion, + timeout: Instant, + interval: Duration, + next_index: usize, + }, + ToSpecificQuery { + version: IgmpVersion, + timeout: Instant, + group: Ipv4Address, + }, +} + +impl<'a> Interface<'a> { + /// Get the socket context. + /// + /// The context is needed for some socket methods. + pub fn context(&mut self) -> &mut InterfaceInner<'a> { + &mut self.inner + } + + /// Get the HardwareAddress address of the interface. + /// + /// # Panics + /// This function panics if the medium is not Ethernet or Ieee802154. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn hardware_addr(&self) -> HardwareAddress { + #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] + assert!(self.inner.caps.medium == Medium::Ethernet); + #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] + assert!(self.inner.caps.medium == Medium::Ieee802154); + + #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] + assert!( + self.inner.caps.medium == Medium::Ethernet + || self.inner.caps.medium == Medium::Ieee802154 + ); + + self.inner.hardware_addr.unwrap() + } + + /// Set the HardwareAddress address of the interface. + /// + /// # Panics + /// This function panics if the address is not unicast, and if the medium is not Ethernet or + /// Ieee802154. + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + pub fn set_hardware_addr(&mut self, addr: HardwareAddress) { + #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))] + assert!(self.inner.caps.medium == Medium::Ethernet); + #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))] + assert!(self.inner.caps.medium == Medium::Ieee802154); + + #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))] + assert!( + self.inner.caps.medium == Medium::Ethernet + || self.inner.caps.medium == Medium::Ieee802154 + ); + + InterfaceInner::check_hardware_addr(&addr); + self.inner.hardware_addr = Some(addr); + } + + /// Add an address to a list of subscribed multicast IP addresses. + /// + /// 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>( + &mut self, + device: &mut D, + addr: T, + timestamp: Instant, + ) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + self.inner.now = timestamp; + + match addr.into() { + #[cfg(feature = "proto-igmp")] + IpAddress::Ipv4(addr) => { + let is_not_new = self + .inner + .ipv4_multicast_groups + .insert(addr, ()) + .map_err(|_| Error::Exhausted)? + .is_some(); + if is_not_new { + Ok(false) + } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) + { + // Send initial membership report + let tx_token = device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + Ok(true) + } else { + Ok(false) + } + } + // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] + _ => Err(Error::Unaddressable), + } + } + + /// Remove an address from the subscribed multicast IP addresses. + /// + /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` + /// indicates whether an immediate leave packet has been sent. + pub fn leave_multicast_group>( + &mut self, + device: &mut D, + addr: T, + timestamp: Instant, + ) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + self.inner.now = timestamp; + + match addr.into() { + #[cfg(feature = "proto-igmp")] + IpAddress::Ipv4(addr) => { + let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none(); + if was_not_present { + Ok(false) + } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { + // Send group leave packet + let tx_token = device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + Ok(true) + } else { + Ok(false) + } + } + // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] + _ => Err(Error::Unaddressable), + } + } + + /// Check whether the interface listens to given destination multicast IP address. + pub fn has_multicast_group>(&self, addr: T) -> bool { + self.inner.has_multicast_group(addr) + } + + /// Get the IP addresses of the interface. + pub fn ip_addrs(&self) -> &[IpCidr] { + self.inner.ip_addrs.as_ref() + } + + /// Get the first IPv4 address if present. + #[cfg(feature = "proto-ipv4")] + pub fn ipv4_addr(&self) -> Option { + self.ip_addrs() + .iter() + .find_map(|cidr| match cidr.address() { + IpAddress::Ipv4(addr) => Some(addr), + #[allow(unreachable_patterns)] + _ => None, + }) + } + + /// Update the IP addresses of the interface. + /// + /// # Panics + /// This function panics if any of the addresses are not unicast. + pub fn update_ip_addrs)>(&mut self, f: F) { + f(&mut self.inner.ip_addrs); + InterfaceInner::flush_cache(&mut self.inner); + InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) + } + + /// Check whether the interface has the given IP address assigned. + pub fn has_ip_addr>(&self, addr: T) -> bool { + self.inner.has_ip_addr(addr) + } + + /// Get the first IPv4 address of the interface. + #[cfg(feature = "proto-ipv4")] + pub fn ipv4_address(&self) -> Option { + self.inner.ipv4_address() + } + + pub fn routes(&self) -> &Routes<'a> { + &self.inner.routes + } + + pub fn routes_mut(&mut self) -> &mut Routes<'a> { + &mut self.inner.routes + } + + /// Transmit packets queued in the given sockets, and receive packets queued + /// in the device. + /// + /// This function returns a boolean value indicating whether any packets were + /// processed or emitted, and thus, whether the readiness of any socket might + /// have changed. + /// + /// # Errors + /// This method will routinely return errors in response to normal network + /// activity as well as certain boundary conditions such as buffer exhaustion. + /// These errors are provided as an aid for troubleshooting, and are meant + /// to be logged and ignored. + /// + /// As a special case, `Err(Error::Unrecognized)` is returned in response to + /// packets containing any unsupported protocol, option, or form, which is + /// a very common occurrence and on a production system it should not even + /// be logged. + pub fn poll( + &mut self, + timestamp: Instant, + device: &mut D, + sockets: &mut SocketSet<'_>, + ) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + self.inner.now = timestamp; + + #[cfg(feature = "proto-ipv4-fragmentation")] + self.fragments + .ipv4_fragments + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + self.fragments + .sixlowpan_fragments + .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; + + #[cfg(feature = "proto-ipv4-fragmentation")] + match self.ipv4_egress(device) { + Ok(true) => return Ok(true), + Err(e) => { + net_debug!("failed to transmit: {}", e); + return Err(e); + } + _ => (), + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + match self.sixlowpan_egress(device) { + Ok(true) => return Ok(true), + Err(e) => { + net_debug!("failed to transmit: {}", e); + return Err(e); + } + _ => (), + } + + let mut readiness_may_have_changed = false; + + loop { + let processed_any = self.socket_ingress(device, sockets); + let emitted_any = self.socket_egress(device, sockets); + + #[cfg(feature = "proto-igmp")] + self.igmp_egress(device)?; + + if processed_any || emitted_any { + readiness_may_have_changed = true; + } else { + break; + } + } + + Ok(readiness_may_have_changed) + } + + /// Return a _soft deadline_ for calling [poll] the next time. + /// The [Instant] returned is the time at which you should call [poll] next. + /// It is harmless (but wastes energy) to call it before the [Instant], and + /// potentially harmful (impacting quality of service) to call it after the + /// [Instant] + /// + /// [poll]: #method.poll + /// [Instant]: struct.Instant.html + pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { + self.inner.now = timestamp; + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if !self.out_packets.all_transmitted() { + return Some(Instant::from_millis(0)); + } + + let inner = &mut self.inner; + + sockets + .items() + .filter_map(move |item| { + let socket_poll_at = item.socket.poll_at(inner); + match item + .meta + .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr)) + { + PollAt::Ingress => None, + PollAt::Time(instant) => Some(instant), + PollAt::Now => Some(Instant::from_millis(0)), + } + }) + .min() + } + + /// Return an _advisory wait time_ for calling [poll] the next time. + /// The [Duration] returned is the time left to wait before calling [poll] next. + /// It is harmless (but wastes energy) to call it before the [Duration] has passed, + /// and potentially harmful (impacting quality of service) to call it after the + /// [Duration] has passed. + /// + /// [poll]: #method.poll + /// [Duration]: struct.Duration.html + pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { + match self.poll_at(timestamp, sockets) { + Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp), + Some(_) => Some(Duration::from_millis(0)), + _ => None, + } + } + + fn socket_ingress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool + where + D: for<'d> Device<'d> + ?Sized, + { + let mut processed_any = false; + let Self { + inner, + fragments: ref mut _fragments, + out_packets: _out_packets, + } = self; + + while let Some((rx_token, tx_token)) = device.receive() { + let res = rx_token.consume(inner.now, |frame| { + match inner.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { + if let Err(err) = inner.dispatch(tx_token, packet, Some(_out_packets)) { + net_debug!("Failed to send response: {}", err); + } + } + } + #[cfg(feature = "medium-ip")] + Medium::Ip => { + if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { + if let Err(err) = + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) + { + net_debug!("Failed to send response: {}", err); + } + } + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + if let Some(packet) = inner.process_ieee802154(sockets, &frame, _fragments) + { + if let Err(err) = + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) + { + net_debug!("Failed to send response: {}", err); + } + } + } + } + processed_any = true; + Ok(()) + }); + + if let Err(err) = res { + net_debug!("Failed to consume RX token: {}", err); + } + } + + processed_any + } + + fn socket_egress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool + where + D: for<'d> Device<'d> + ?Sized, + { + let Self { + inner, + out_packets: _out_packets, + .. + } = self; + let _caps = device.capabilities(); + + let mut emitted_any = false; + for item in sockets.items_mut() { + if !item + .meta + .egress_permitted(inner.now, |ip_addr| inner.has_neighbor(&ip_addr)) + { + continue; + } + + let mut neighbor_addr = None; + let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { + neighbor_addr = Some(response.ip_repr().dst_addr()); + let t = device.transmit().ok_or_else(|| { + net_debug!("failed to transmit IP: {}", Error::Exhausted); + Error::Exhausted + })?; + + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] + inner.dispatch_ip(t, response, Some(_out_packets))?; + + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] + inner.dispatch_ip(t, response, None)?; + + emitted_any = true; + + Ok(()) + }; + + let result = match &mut item.socket { + #[cfg(feature = "socket-raw")] + Socket::Raw(socket) => socket.dispatch(inner, |inner, response| { + respond(inner, IpPacket::Raw(response)) + }), + #[cfg(feature = "socket-icmp")] + Socket::Icmp(socket) => socket.dispatch(inner, |inner, response| match response { + #[cfg(feature = "proto-ipv4")] + (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { + respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) + } + #[cfg(feature = "proto-ipv6")] + (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { + respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) + } + #[allow(unreachable_patterns)] + _ => unreachable!(), + }), + #[cfg(feature = "socket-udp")] + Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { + respond(inner, IpPacket::Udp(response)) + }), + #[cfg(feature = "socket-tcp")] + Socket::Tcp(socket) => socket.dispatch(inner, |inner, response| { + respond(inner, IpPacket::Tcp(response)) + }), + #[cfg(feature = "socket-dhcpv4")] + Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { + respond(inner, IpPacket::Dhcpv4(response)) + }), + #[cfg(feature = "socket-dns")] + Socket::Dns(ref mut socket) => socket.dispatch(inner, |inner, response| { + respond(inner, IpPacket::Udp(response)) + }), + }; + + match result { + Err(Error::Exhausted) => break, // Device buffer full. + Err(Error::Unaddressable) => { + // `NeighborCache` already takes care of rate limiting the neighbor discovery + // requests from the socket. However, without an additional rate limiting + // mechanism, we would spin on every socket that has yet to discover its + // neighbor. + item.meta.neighbor_missing( + inner.now, + neighbor_addr.expect("non-IP response packet"), + ); + break; + } + Err(err) => { + net_debug!( + "{}: cannot dispatch egress packet: {}", + item.meta.handle, + err + ); + } + Ok(()) => {} + } + } + emitted_any + } + + /// Depending on `igmp_report_state` and the therein contained + /// timeouts, send IGMP membership reports. + #[cfg(feature = "proto-igmp")] + fn igmp_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + match self.inner.igmp_report_state { + IgmpReportState::ToSpecificQuery { + version, + timeout, + group, + } if self.inner.now >= timeout => { + if let Some(pkt) = self.inner.igmp_report_packet(version, group) { + // Send initial membership report + let tx_token = device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + } + + self.inner.igmp_report_state = IgmpReportState::Inactive; + Ok(true) + } + IgmpReportState::ToGeneralQuery { + version, + timeout, + interval, + next_index, + } if self.inner.now >= timeout => { + let addr = self + .inner + .ipv4_multicast_groups + .iter() + .nth(next_index) + .map(|(addr, ())| *addr); + + match addr { + Some(addr) => { + if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { + // Send initial membership report + let tx_token = device.transmit().ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + } + + let next_timeout = (timeout + interval).max(self.inner.now); + self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { + version, + timeout: next_timeout, + interval, + next_index: next_index + 1, + }; + Ok(true) + } + + None => { + self.inner.igmp_report_state = IgmpReportState::Inactive; + Ok(false) + } + } + } + _ => Ok(false), + } + } + + /// Process fragments that still need to be sent for IPv4 packets. + /// + /// This function returns a boolean value indicating whether any packets were + /// processed or emitted, and thus, whether the readiness of any socket might + /// have changed. + #[cfg(feature = "proto-ipv4-fragmentation")] + fn ipv4_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + // Reset the buffer when we transmitted everything. + if self.out_packets.ipv4_out_packet.finished() { + self.out_packets.ipv4_out_packet.reset(); + } + + if self.out_packets.ipv4_out_packet.is_empty() { + return Ok(false); + } + + let Ipv4OutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.ipv4_out_packet; + + if *packet_len > *sent_bytes { + match device.transmit() { + Some(tx_token) => self + .inner + .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet), + None => Err(Error::Exhausted), + } + .map(|_| true) + } else { + Ok(false) + } + } + + /// Process fragments that still need to be sent for 6LoWPAN packets. + /// + /// This function returns a boolean value indicating whether any packets were + /// processed or emitted, and thus, whether the readiness of any socket might + /// have changed. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + fn sixlowpan_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + // Reset the buffer when we transmitted everything. + if self.out_packets.sixlowpan_out_packet.finished() { + self.out_packets.sixlowpan_out_packet.reset(); + } + + if self.out_packets.sixlowpan_out_packet.is_empty() { + return Ok(false); + } + + let SixlowpanOutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.sixlowpan_out_packet; + + if *packet_len > *sent_bytes { + match device.transmit() { + Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( + tx_token, + &mut self.out_packets.sixlowpan_out_packet, + ), + None => Err(Error::Exhausted), + } + .map(|_| true) + } else { + Ok(false) + } + } +} + +impl<'a> InterfaceInner<'a> { + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn now(&self) -> Instant { + self.now + } + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn hardware_addr(&self) -> Option { + self.hardware_addr + } + + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities { + self.caps.checksum.clone() + } + + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn ip_mtu(&self) -> usize { + self.caps.ip_mtu() + } + + #[allow(unused)] // unused depending on which sockets are enabled, and in tests + pub(crate) fn rand(&mut self) -> &mut Rand { + &mut self.rand + } + + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { + let v = dst_addr.version(); + for cidr in self.ip_addrs.iter() { + let addr = cidr.address(); + if addr.version() == v { + return Some(addr); + } + } + None + } + + #[cfg(feature = "proto-ipv4")] + #[allow(unused)] + pub(crate) fn get_source_address_ipv4( + &mut self, + _dst_addr: Ipv4Address, + ) -> Option { + for cidr in self.ip_addrs.iter() { + #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled + if let IpCidr::Ipv4(cidr) = cidr { + return Some(cidr.address()); + } + } + None + } + + #[cfg(feature = "proto-ipv6")] + #[allow(unused)] + pub(crate) fn get_source_address_ipv6( + &mut self, + _dst_addr: Ipv6Address, + ) -> Option { + for cidr in self.ip_addrs.iter() { + #[allow(irrefutable_let_patterns)] // if only ipv6 is enabled + if let IpCidr::Ipv6(cidr) = cidr { + return Some(cidr.address()); + } + } + None + } + + #[cfg(test)] + pub(crate) fn mock() -> Self { + Self { + 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, + }, + now: Instant::from_millis_const(0), + + ip_addrs: ManagedSlice::Owned(vec![ + #[cfg(feature = "proto-ipv4")] + IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(Ipv6Cidr::new( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), + 64, + )), + ]), + rand: Rand::new(1234), + routes: Routes::new(&mut [][..]), + + #[cfg(feature = "proto-ipv4")] + any_ip: false, + + #[cfg(feature = "medium-ieee802154")] + pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)), + #[cfg(feature = "medium-ieee802154")] + sequence_no: 1, + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + tag: 1, + + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: &[], + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: 1, + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( + crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), + )), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: None, + + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + } + } + + #[cfg(test)] + #[allow(unused)] // unused depending on which sockets are enabled + pub(crate) fn set_now(&mut self, now: Instant) { + self.now = now + } + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + fn check_hardware_addr(addr: &HardwareAddress) { + if !addr.is_unicast() { + panic!("Ethernet address {} is not unicast", addr) + } + } + + fn check_ip_addrs(addrs: &[IpCidr]) { + for cidr in addrs { + if !cidr.address().is_unicast() && !cidr.address().is_unspecified() { + panic!("IP address {} is not unicast", cidr.address()) + } + } + } + + #[cfg(feature = "medium-ieee802154")] + fn get_sequence_number(&mut self) -> u8 { + let no = self.sequence_no; + self.sequence_no = self.sequence_no.wrapping_add(1); + no + } + + #[cfg(feature = "proto-ipv4-fragmentation")] + fn get_ipv4_ident(&mut self) -> u16 { + let ipv4_id = self.ipv4_id; + self.ipv4_id = self.ipv4_id.wrapping_add(1); + ipv4_id + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + fn get_sixlowpan_fragment_tag(&mut self) -> u16 { + let tag = self.tag; + self.tag = self.tag.wrapping_add(1); + tag + } + + /// Determine if the given `Ipv6Address` is the solicited node + /// multicast address for a IPv6 addresses assigned to the interface. + /// See [RFC 4291 § 2.7.1] for more details. + /// + /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1 + #[cfg(feature = "proto-ipv6")] + pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool { + self.ip_addrs.iter().any(|cidr| { + match *cidr { + IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => { + // Take the lower order 24 bits of the IPv6 address and + // append those bits to FF02:0:0:0:0:1:FF00::/104. + addr.as_bytes()[14..] == cidr.address().as_bytes()[14..] + } + _ => false, + } + }) + } + + /// Check whether the interface has the given IP address assigned. + fn has_ip_addr>(&self, addr: T) -> bool { + let addr = addr.into(); + self.ip_addrs.iter().any(|probe| probe.address() == addr) + } + + /// Get the first IPv4 address of the interface. + #[cfg(feature = "proto-ipv4")] + pub fn ipv4_address(&self) -> Option { + self.ip_addrs.iter().find_map(|addr| match *addr { + IpCidr::Ipv4(cidr) => Some(cidr.address()), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None, + }) + } + + /// Check whether the interface listens to given destination multicast IP address. + /// + /// If built without feature `proto-igmp` this function will + /// always return `false`. + pub fn has_multicast_group>(&self, addr: T) -> bool { + match addr.into() { + #[cfg(feature = "proto-igmp")] + IpAddress::Ipv4(key) => { + key == Ipv4Address::MULTICAST_ALL_SYSTEMS + || self.ipv4_multicast_groups.get(&key).is_some() + } + #[allow(unreachable_patterns)] + _ => false, + } + } + + #[cfg(feature = "medium-ip")] + fn process_ip<'frame, T: AsRef<[u8]>>( + &mut self, + sockets: &mut SocketSet, + ip_payload: &'frame T, + _fragments: &'frame mut FragmentsBuffer<'a>, + ) -> Option> { + match IpVersion::of_packet(ip_payload.as_ref()) { + #[cfg(feature = "proto-ipv4")] + Ok(IpVersion::Ipv4) => { + let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) + } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + self.process_ipv4(sockets, &ipv4_packet, None) + } + } + #[cfg(feature = "proto-ipv6")] + Ok(IpVersion::Ipv6) => { + let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload)); + self.process_ipv6(sockets, &ipv6_packet) + } + // Drop all other traffic. + _ => None, + } + } + + #[cfg(feature = "socket-raw")] + fn raw_socket_filter<'frame>( + &mut self, + sockets: &mut SocketSet, + ip_repr: &IpRepr, + ip_payload: &'frame [u8], + ) -> bool { + let mut handled_by_raw_socket = false; + + // Pass every IP packet to all raw sockets we have registered. + for raw_socket in sockets + .items_mut() + .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket)) + { + if raw_socket.accepts(ip_repr) { + raw_socket.process(self, ip_repr, ip_payload); + handled_by_raw_socket = true; + } + } + handled_by_raw_socket + } + + /// Checks if an incoming packet has a broadcast address for the interfaces + /// associated ipv4 addresses. + #[cfg(feature = "proto-ipv4")] + fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { + self.ip_addrs + .iter() + .filter_map(|own_cidr| match own_cidr { + IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None, + }) + .any(|broadcast_address| address == broadcast_address) + } + + /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses + #[cfg(feature = "proto-ipv4")] + fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { + address.is_broadcast() || self.is_subnet_broadcast(address) + } + + /// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses + #[cfg(feature = "proto-ipv4")] + fn is_unicast_v4(&self, address: Ipv4Address) -> bool { + address.is_unicast() && !self.is_subnet_broadcast(address) + } + + #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + fn process_udp<'frame>( + &mut self, + sockets: &mut SocketSet, + ip_repr: IpRepr, + udp_repr: UdpRepr, + handled_by_raw_socket: bool, + udp_payload: &'frame [u8], + ip_payload: &'frame [u8], + ) -> Option> { + #[cfg(feature = "socket-udp")] + for udp_socket in sockets + .items_mut() + .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) + { + if udp_socket.accepts(self, &ip_repr, &udp_repr) { + udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); + return None; + } + } + + #[cfg(feature = "socket-dns")] + for dns_socket in sockets + .items_mut() + .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket)) + { + if dns_socket.accepts(&ip_repr, &udp_repr) { + dns_socket.process(self, &ip_repr, &udp_repr, udp_payload); + return None; + } + } + + // The packet wasn't handled by a socket, send an ICMP port unreachable packet. + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(_) if handled_by_raw_socket => None, + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) if handled_by_raw_socket => None, + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(ipv4_repr) => { + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len()); + let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::PortUnreachable, + header: ipv4_repr, + data: &ip_payload[0..payload_len], + }; + self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr) + } + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(ipv6_repr) => { + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); + let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ipv6_repr, + data: &ip_payload[0..payload_len], + }; + self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr) + } + } + } + + #[cfg(feature = "socket-tcp")] + pub(crate) fn process_tcp<'frame>( + &mut self, + sockets: &mut SocketSet, + ip_repr: IpRepr, + ip_payload: &'frame [u8], + ) -> Option> { + let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); + let tcp_packet = check!(TcpPacket::new_checked(ip_payload)); + let tcp_repr = check!(TcpRepr::parse( + &tcp_packet, + &src_addr, + &dst_addr, + &self.caps.checksum + )); + + for tcp_socket in sockets + .items_mut() + .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket)) + { + if tcp_socket.accepts(self, &ip_repr, &tcp_repr) { + return tcp_socket + .process(self, &ip_repr, &tcp_repr) + .map(IpPacket::Tcp); + } + } + + if tcp_repr.control == TcpControl::Rst { + // Never reply to a TCP RST packet with another TCP RST packet. + None + } else { + // The packet wasn't handled by a socket, send a TCP RST packet. + Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr))) + } + } + + #[cfg(feature = "medium-ethernet")] + fn dispatch( + &mut self, + tx_token: Tx, + packet: EthernetPacket, + _out_packet: Option<&mut OutPackets<'_>>, + ) -> Result<()> + where + Tx: TxToken, + { + match packet { + #[cfg(feature = "proto-ipv4")] + EthernetPacket::Arp(arp_repr) => { + let dst_hardware_addr = match arp_repr { + ArpRepr::EthernetIpv4 { + target_hardware_addr, + .. + } => target_hardware_addr, + }; + + self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { + frame.set_dst_addr(dst_hardware_addr); + frame.set_ethertype(EthernetProtocol::Arp); + + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + arp_repr.emit(&mut packet); + }) + } + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, _out_packet), + } + } + + fn in_same_network(&self, addr: &IpAddress) -> bool { + self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr)) + } + + fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { + // Send directly. + if self.in_same_network(addr) || addr.is_broadcast() { + return Ok(*addr); + } + + // Route via a router. + match self.routes.lookup(addr, timestamp) { + Some(router_addr) => Ok(router_addr), + None => Err(Error::Unaddressable), + } + } + + fn has_neighbor(&self, addr: &IpAddress) -> bool { + match self.route(addr, self.now) { + Ok(_routed_addr) => match self.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => self + .neighbor_cache + .as_ref() + .unwrap() + .lookup(&_routed_addr, self.now) + .found(), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => self + .neighbor_cache + .as_ref() + .unwrap() + .lookup(&_routed_addr, self.now) + .found(), + #[cfg(feature = "medium-ip")] + Medium::Ip => true, + }, + Err(_) => false, + } + } + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + fn lookup_hardware_addr( + &mut self, + tx_token: Tx, + src_addr: &IpAddress, + dst_addr: &IpAddress, + ) -> Result<(HardwareAddress, Tx)> + where + Tx: TxToken, + { + if dst_addr.is_broadcast() { + let hardware_addr = match self.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST), + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + }; + + return Ok((hardware_addr, tx_token)); + } + + if dst_addr.is_multicast() { + let b = dst_addr.as_bytes(); + let hardware_addr = match *dst_addr { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(_addr) => { + HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0x01, + 0x00, + 0x5e, + b[1] & 0x7F, + b[2], + b[3], + ])) + } + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(_addr) => match self.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0x33, 0x33, b[12], b[13], b[14], b[15], + ])), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => { + // Not sure if this is correct + HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST) + } + #[cfg(feature = "medium-ip")] + Medium::Ip => unreachable!(), + }, + }; + + return Ok((hardware_addr, tx_token)); + } + + let dst_addr = self.route(dst_addr, self.now)?; + + match self + .neighbor_cache + .as_mut() + .unwrap() + .lookup(&dst_addr, self.now) + { + NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), + NeighborAnswer::RateLimited => return Err(Error::Unaddressable), + _ => (), // XXX + } + + match (src_addr, dst_addr) { + #[cfg(feature = "proto-ipv4")] + (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => { + net_debug!( + "address {} not in neighbor cache, sending ARP request", + dst_addr + ); + let src_hardware_addr = + if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + let arp_repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: src_hardware_addr, + source_protocol_addr: src_addr, + target_hardware_addr: EthernetAddress::BROADCAST, + target_protocol_addr: dst_addr, + }; + + self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_ethertype(EthernetProtocol::Arp); + + arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) + })?; + } + + #[cfg(feature = "proto-ipv6")] + (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => { + net_debug!( + "address {} not in neighbor cache, sending Neighbor Solicitation", + dst_addr + ); + + let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { + target_addr: dst_addr, + lladdr: Some(self.hardware_addr.unwrap().into()), + }); + + let packet = IpPacket::Icmpv6(( + Ipv6Repr { + src_addr, + dst_addr: dst_addr.solicited_node(), + next_header: IpProtocol::Icmpv6, + payload_len: solicit.buffer_len(), + hop_limit: 0xff, + }, + solicit, + )); + + self.dispatch_ip(tx_token, packet, None)?; + } + + #[allow(unreachable_patterns)] + _ => (), + } + // The request got dispatched, limit the rate on the cache. + self.neighbor_cache.as_mut().unwrap().limit_rate(self.now); + Err(Error::Unaddressable) + } + + fn flush_cache(&mut self) { + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + if let Some(cache) = self.neighbor_cache.as_mut() { + cache.flush() + } + } + + fn dispatch_ip( + &mut self, + tx_token: Tx, + packet: IpPacket, + _out_packet: Option<&mut OutPackets<'_>>, + ) -> Result<()> { + let mut ip_repr = packet.ip_repr(); + assert!(!ip_repr.dst_addr().is_unspecified()); + + // Dispatch IEEE802.15.4: + + #[cfg(feature = "medium-ieee802154")] + if matches!(self.caps.medium, Medium::Ieee802154) { + let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )? { + (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), + _ => unreachable!(), + }; + + return self.dispatch_ieee802154( + dst_hardware_addr, + &ip_repr, + tx_token, + packet, + _out_packet, + ); + } + + // Dispatch IP/Ethernet: + + let caps = self.caps.clone(); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let ipv4_id = self.get_ipv4_ident(); + + // First we calculate the total length that we will have to emit. + let mut total_len = ip_repr.buffer_len(); + + // Add the size of the Ethernet header if the medium is Ethernet. + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + total_len = EthernetFrame::<&[u8]>::buffer_len(total_len); + } + + // If the medium is Ethernet, then we need to retrieve the destination hardware address. + #[cfg(feature = "medium-ethernet")] + let (dst_hardware_addr, tx_token) = + match self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())? { + (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), + #[cfg(feature = "medium-ieee802154")] + (HardwareAddress::Ieee802154(_), _) => unreachable!(), + }; + + // Emit function for the Ethernet header. + #[cfg(feature = "medium-ethernet")] + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), + } + + Ok(()) + }; + + // Emit function for the IP header and payload. + let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| { + repr.emit(&mut tx_buffer, &self.caps.checksum); + + let payload = &mut tx_buffer[repr.header_len()..]; + packet.emit_payload(repr, payload, &caps); + }; + + let total_ip_len = ip_repr.buffer_len(); + + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(ref mut repr) => { + // If we have an IPv4 packet, then we need to check if we need to fragment it. + if total_ip_len > self.caps.max_transmission_unit { + #[cfg(feature = "proto-ipv4-fragmentation")] + { + net_debug!("start fragmentation"); + + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr: out_packet_repr, + frag_offset, + ident, + dst_hardware_addr: dst_address, + } = &mut _out_packet.unwrap().ipv4_out_packet; + + // Calculate how much we will send now (including the Ethernet header). + let tx_len = self.caps.max_transmission_unit; + + let ip_header_len = repr.buffer_len(); + let first_frag_ip_len = self.caps.ip_mtu(); + + if buffer.len() < first_frag_ip_len { + net_debug!("Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + + *dst_address = dst_hardware_addr; + + // Save the total packet len (without the Ethernet header, but with the first + // IP header). + *packet_len = total_ip_len; + + // Save the IP header for other fragments. + *out_packet_repr = *repr; + + // Save how much bytes we will send now. + *sent_bytes = first_frag_ip_len; + + // Modify the IP header + repr.payload_len = first_frag_ip_len - repr.buffer_len(); + + // Emit the IP header to the buffer. + emit_ip(&ip_repr, buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); + *ident = ipv4_id; + ipv4_packet.set_ident(ipv4_id); + ipv4_packet.set_more_frags(true); + ipv4_packet.set_dont_frag(false); + ipv4_packet.set_frag_offset(0); + + if caps.checksum.ipv4.tx() { + ipv4_packet.fill_checksum(); + } + + // Transmit the first packet. + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + // Change the offset for the next packet. + *frag_offset = (first_frag_ip_len - ip_header_len) as u16; + + // Copy the IP header and the payload. + tx_buffer[..first_frag_ip_len] + .copy_from_slice(&buffer[..first_frag_ip_len]); + + Ok(()) + }) + } + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + { + net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); + Ok(()) + } + } else { + // No fragmentation is required. + tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }) + } + } + // We don't support IPv6 fragmentation yet. + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) => tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }), + } + } +} diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs new file mode 100644 index 000000000..70c030999 --- /dev/null +++ b/src/iface/interface/sixlowpan.rs @@ -0,0 +1,648 @@ +use super::check; +use super::FragmentsBuffer; +use super::InterfaceInner; +use super::IpPacket; +use super::OutPackets; +use super::PacketAssemblerSet; +use super::SocketSet; + +#[cfg(feature = "proto-sixlowpan-fragmentation")] +use super::SixlowpanOutPacket; + +use crate::phy::TxToken; +use crate::time::*; +use crate::wire::*; +use crate::Error; +use crate::Result; + +impl<'a> InterfaceInner<'a> { + #[cfg(feature = "medium-ieee802154")] + pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + sockets: &mut SocketSet, + sixlowpan_payload: &'payload T, + _fragments: &'output mut FragmentsBuffer<'a>, + ) -> Option> { + let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); + let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); + + if ieee802154_repr.frame_type != Ieee802154FrameType::Data { + return None; + } + + // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this + // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. + // We always accept the broadcast PAN id. + if self.pan_id.is_some() + && ieee802154_repr.dst_pan_id != self.pan_id + && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) + { + net_debug!( + "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)", + ieee802154_repr + ); + return None; + } + + match ieee802154_frame.payload() { + Some(payload) => { + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + self.process_sixlowpan( + sockets, + &ieee802154_repr, + payload, + Some(( + &mut _fragments.sixlowpan_fragments, + _fragments.sixlowpan_fragments_cache_timeout, + )), + ) + } + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + { + self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) + } + } + None => None, + } + } + + pub(super) fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + sockets: &mut SocketSet, + ieee802154_repr: &Ieee802154Repr, + payload: &'payload T, + _fragments: Option<( + &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + Duration, + )>, + ) -> Option> { + let payload = match check!(SixlowpanPacket::dispatch(payload)) { + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + SixlowpanPacket::FragmentHeader => { + net_debug!("Fragmentation is not supported, use the `proto-sixlowpan-fragmentation` feature to add support."); + return None; + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] + SixlowpanPacket::FragmentHeader => { + match self.process_sixlowpan_fragment(ieee802154_repr, payload, _fragments) { + Some(payload) => payload, + None => return None, + } + } + SixlowpanPacket::IphcHeader => payload.as_ref(), + }; + + // At this point we should have a valid 6LoWPAN packet. + // The first header needs to be an IPHC header. + let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload)); + let iphc_repr = check!(SixlowpanIphcRepr::parse( + &iphc_packet, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + self.sixlowpan_address_context, + )); + + let payload = iphc_packet.payload(); + let mut ipv6_repr = Ipv6Repr { + src_addr: iphc_repr.src_addr, + dst_addr: iphc_repr.dst_addr, + hop_limit: iphc_repr.hop_limit, + next_header: IpProtocol::Unknown(0), + payload_len: 40, + }; + + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(payload)) { + SixlowpanNhcPacket::ExtHeader => { + net_debug!("Extension headers are currently not supported for 6LoWPAN"); + None + } + #[cfg(not(feature = "socket-udp"))] + SixlowpanNhcPacket::UdpHeader => { + net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); + None + } + #[cfg(feature = "socket-udp")] + SixlowpanNhcPacket::UdpHeader => { + let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload)); + ipv6_repr.next_header = IpProtocol::Udp; + ipv6_repr.payload_len += 8 + udp_packet.payload().len(); + + let udp_repr = check!(SixlowpanUdpNhcRepr::parse( + &udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr + )); + + self.process_udp( + sockets, + IpRepr::Ipv6(ipv6_repr), + udp_repr.0, + false, + udp_packet.payload(), + payload, + ) + } + } + } + SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { + IpProtocol::Icmpv6 => { + ipv6_repr.next_header = IpProtocol::Icmpv6; + self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) + } + #[cfg(feature = "socket-tcp")] + IpProtocol::Tcp => { + ipv6_repr.next_header = nxt_hdr; + ipv6_repr.payload_len += payload.len(); + self.process_tcp(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) + } + proto => { + net_debug!("6LoWPAN: {} currently not supported", proto); + None + } + }, + } + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + fn process_sixlowpan_fragment<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + ieee802154_repr: &Ieee802154Repr, + payload: &'payload T, + fragments: Option<( + &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, + Duration, + )>, + ) -> Option<&'output [u8]> { + let (fragments, timeout) = fragments.unwrap(); + + // We have a fragment header, which means we cannot process the 6LoWPAN packet, + // unless we have a complete one after processing this fragment. + let frag = check!(SixlowpanFragPacket::new_checked(payload)); + + // The key specifies to which 6LoWPAN fragment it belongs too. + // It is based on the link layer addresses, the tag and the size. + let key = frag.get_key(ieee802154_repr); + + // The offset of this fragment in increments of 8 octets. + let offset = frag.datagram_offset() as usize * 8; + + if frag.is_first_fragment() { + // The first fragment contains the total size of the IPv6 packet. + // However, we received a packet that is compressed following the 6LoWPAN + // standard. This means we need to convert the IPv6 packet size to a 6LoWPAN + // packet size. The packet size can be different because of first the + // compression of the IP header and when UDP is used (because the UDP header + // can also be compressed). Other headers are not compressed by 6LoWPAN. + + let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); + let iphc_repr = check!(SixlowpanIphcRepr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + self.sixlowpan_address_context, + )); + + // The uncompressed header size always starts with 40, since this is the size + // of a IPv6 header. + let mut uncompressed_header_size = 40; + let mut compressed_header_size = iphc.header_len(); + + // We need to check if we have an UDP packet, since this header can also be + // compressed by 6LoWPAN. We currently don't support extension headers yet. + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + SixlowpanNhcPacket::ExtHeader => { + net_debug!("6LoWPAN: extension headers not supported"); + return None; + } + SixlowpanNhcPacket::UdpHeader => { + let udp_packet = + check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); + + uncompressed_header_size += 8; + compressed_header_size += + 1 + udp_packet.ports_size() + udp_packet.checksum_size(); + } + } + } + SixlowpanNextHeader::Uncompressed(_) => (), + } + + // We reserve a spot in the packet assembler set and add the required + // information to the packet assembler. + // This information is the total size of the packet when it is fully assmbled. + // We also pass the header size, since this is needed when other fragments + // (other than the first one) are added. + let frag_slot = match fragments.reserve_with_key(&key) { + Ok(frag) => frag, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(frag_slot.start( + Some( + frag.datagram_size() as usize - uncompressed_header_size + + compressed_header_size + ), + self.now + timeout, + -((uncompressed_header_size - compressed_header_size) as isize), + )); + } + + let frags = check!(fragments.get_packet_assembler_mut(&key)); + + net_trace!("6LoWPAN: received packet fragment"); + + // Add the fragment to the packet assembler. + match frags.add(frag.payload(), offset) { + Ok(true) => { + net_trace!("6LoWPAN: fragmented packet now complete"); + match fragments.get_assembled_packet(&key) { + Ok(packet) => Some(packet), + _ => unreachable!(), + } + } + Ok(false) => None, + Err(Error::PacketAssemblerOverlap) => { + net_trace!("6LoWPAN: overlap in packet"); + frags.mark_discarded(); + None + } + Err(_) => None, + } + } + + #[cfg(feature = "medium-ieee802154")] + pub(super) fn dispatch_ieee802154( + &mut self, + ll_dst_a: Ieee802154Address, + ip_repr: &IpRepr, + tx_token: Tx, + packet: IpPacket, + _out_packet: Option<&mut OutPackets>, + ) -> Result<()> { + // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. + // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to + // fragment it. + let ll_src_a = self.hardware_addr.map_or_else( + || Err(Error::Malformed), + |addr| match addr { + HardwareAddress::Ieee802154(addr) => Ok(addr), + _ => Err(Error::Malformed), + }, + )?; + + let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { + (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), + #[allow(unreachable_patterns)] + _ => return Err(Error::Unaddressable), + }; + + // Create the IEEE802.15.4 header. + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(ll_dst_a), + src_pan_id: self.pan_id, + src_addr: Some(ll_src_a), + }; + + // Create the 6LoWPAN IPHC header. + let iphc_repr = SixlowpanIphcRepr { + src_addr, + ll_src_addr: Some(ll_src_a), + dst_addr, + ll_dst_addr: Some(ll_dst_a), + next_header: match &packet { + IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp), + #[cfg(feature = "socket-udp")] + IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, + #[allow(unreachable_patterns)] + _ => return Err(Error::Unrecognized), + }, + hop_limit: ip_repr.hop_limit(), + ecn: None, + dscp: None, + flow_label: None, + }; + + // Now we calculate the total size of the packet. + // We need to know this, such that we know when to do the fragmentation. + let mut total_size = 0; + total_size += iphc_repr.buffer_len(); + let mut _compressed_headers_len = iphc_repr.buffer_len(); + let mut _uncompressed_headers_len = ip_repr.header_len(); + + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + _compressed_headers_len += udp_repr.header_len(); + _uncompressed_headers_len += udpv6_repr.header_len(); + total_size += udp_repr.header_len() + payload.len(); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + total_size += tcp_repr.buffer_len(); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + total_size += icmp_repr.buffer_len(); + } + _ => return Err(Error::Unrecognized), + } + + let ieee_len = ieee_repr.buffer_len(); + + if total_size + ieee_len > 125 { + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + // The packet does not fit in one Ieee802154 frame, so we need fragmentation. + // We do this by emitting everything in the `out_packet.buffer` from the interface. + // After emitting everything into that buffer, we send the first fragment heere. + // When `poll` is called again, we check if out_packet was fully sent, otherwise we + // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + + // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // the link local source and destination address... + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + datagram_offset, + .. + } = &mut _out_packet.unwrap().sixlowpan_out_packet; + + if buffer.len() < total_size { + net_debug!("6LoWPAN: Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + + *ll_dst_addr = ll_dst_a; + *ll_src_addr = ll_src_a; + + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); + + let b = &mut buffer[iphc_repr.buffer_len()..]; + + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut b[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut b[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut b[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } + + *packet_len = total_size; + + // The datagram size that we need to set in the first fragment header is equal to the + // IPv6 payload length + 40. + *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + + // We generate a random tag. + let tag = self.get_sixlowpan_fragment_tag(); + // We save the tag for the other fragments that will be created when calling `poll` + // multiple times. + *datagram_tag = tag; + + let frag1 = SixlowpanFragRepr::FirstFragment { + size: *datagram_size, + tag, + }; + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag, + offset: 0, + }; + + // We calculate how much data we can send in the first fragment and the other + // fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight + // (except for the last fragment) since the offset field in the fragment is an offset + // in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. + // + // [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 + + let header_diff = _uncompressed_headers_len - _compressed_headers_len; + let frag1_size = + (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); + + *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; + + *sent_bytes = frag1_size; + *datagram_offset = frag1_size + header_diff; + + tx_token.consume( + self.now, + ieee_len + frag1.buffer_len() + frag1_size, + |mut tx_buf| { + // Add the IEEE header. + let mut ieee_packet = + Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the first fragment header + let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); + frag1.emit(&mut frag1_packet); + tx_buf = &mut tx_buf[frag1.buffer_len()..]; + + // Add the buffer part. + tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + + Ok(()) + }, + ) + } + + #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + { + net_debug!( + "Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support." + ); + Ok(()) + } + } else { + // We don't need fragmentation, so we emit everything to the TX token. + tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + let mut iphc_packet = + SixlowpanIphcPacket::new_unchecked(&mut tx_buf[..iphc_repr.buffer_len()]); + iphc_repr.emit(&mut iphc_packet); + tx_buf = &mut tx_buf[iphc_repr.buffer_len()..]; + + #[allow(unreachable_patterns)] + match packet { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, udpv6_repr, payload)) => { + let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); + let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( + &mut tx_buf[..udp_repr.header_len() + payload.len()], + ); + udp_repr.emit( + &mut udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + payload.len(), + |buf| buf.copy_from_slice(payload), + ); + } + #[cfg(feature = "socket-tcp")] + IpPacket::Tcp((_, tcp_repr)) => { + let mut tcp_packet = + TcpPacket::new_unchecked(&mut tx_buf[..tcp_repr.buffer_len()]); + tcp_repr.emit( + &mut tcp_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &self.caps.checksum, + ); + } + #[cfg(feature = "proto-ipv6")] + IpPacket::Icmpv6((_, icmp_repr)) => { + let mut icmp_packet = + Icmpv6Packet::new_unchecked(&mut tx_buf[..icmp_repr.buffer_len()]); + icmp_repr.emit( + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + &mut icmp_packet, + &self.caps.checksum, + ); + } + _ => return Err(Error::Unrecognized), + } + Ok(()) + }) + } + } + + #[cfg(all( + feature = "medium-ieee802154", + feature = "proto-sixlowpan-fragmentation" + ))] + pub(super) fn dispatch_ieee802154_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut SixlowpanOutPacket, + ) -> Result<()> { + let SixlowpanOutPacket { + buffer, + packet_len, + datagram_size, + datagram_tag, + datagram_offset, + sent_bytes, + fragn_size, + ll_dst_addr, + ll_src_addr, + .. + } = out_packet; + + // Create the IEEE802.15.4 header. + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(*ll_dst_addr), + src_pan_id: self.pan_id, + src_addr: Some(*ll_src_addr), + }; + + // Create the FRAG_N header. + let fragn = SixlowpanFragRepr::Fragment { + size: *datagram_size, + tag: *datagram_tag, + offset: (*datagram_offset / 8) as u8, + }; + + let ieee_len = ieee_repr.buffer_len(); + let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); + + tx_token.consume( + self.now, + ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, + |mut tx_buf| { + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + let mut frag_packet = + SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]); + fragn.emit(&mut frag_packet); + tx_buf = &mut tx_buf[fragn.buffer_len()..]; + + // Add the buffer part + tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); + + *sent_bytes += frag_size; + *datagram_offset += frag_size; + + Ok(()) + }, + ) + } +} diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs new file mode 100644 index 000000000..8da4a77fb --- /dev/null +++ b/src/iface/interface/tests.rs @@ -0,0 +1,1369 @@ +use std::collections::BTreeMap; +#[cfg(feature = "proto-igmp")] +use std::vec::Vec; + +use super::*; + +use crate::iface::Interface; +#[cfg(feature = "medium-ethernet")] +use crate::iface::NeighborCache; +use crate::phy::{ChecksumCapabilities, Loopback}; +#[cfg(feature = "proto-igmp")] +use crate::time::Instant; +use crate::{Error, Result}; + +#[allow(unused)] +fn fill_slice(s: &mut [u8], val: u8) { + for x in s.iter_mut() { + *x = val + } +} + +fn create<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { + #[cfg(feature = "medium-ethernet")] + return create_ethernet(); + #[cfg(not(feature = "medium-ethernet"))] + return create_ip(); +} + +#[cfg(all(feature = "medium-ip"))] +#[allow(unused)] +fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { + // Create a basic device + let mut device = Loopback::new(Medium::Ip); + let ip_addrs = [ + #[cfg(feature = "proto-ipv4")] + IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + + let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let iface_builder = iface_builder + .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_fragmentation_buffer(vec![]); + + #[cfg(feature = "proto-igmp")] + let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder.finalize(&mut device); + + (iface, SocketSet::new(vec![]), device) +} + +#[cfg(all(feature = "medium-ethernet"))] +fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { + // Create a basic device + let mut device = Loopback::new(Medium::Ethernet); + let ip_addrs = [ + #[cfg(feature = "proto-ipv4")] + IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + + let iface_builder = InterfaceBuilder::new() + .hardware_addr(EthernetAddress::default().into()) + .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .ip_addrs(ip_addrs); + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let iface_builder = iface_builder + .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .sixlowpan_fragmentation_buffer(vec![]); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let iface_builder = iface_builder + .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_fragmentation_buffer(vec![]); + + #[cfg(feature = "proto-igmp")] + let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder.finalize(&mut device); + + (iface, SocketSet::new(vec![]), device) +} + +#[cfg(feature = "proto-igmp")] +fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { + let mut pkts = Vec::new(); + while let Some((rx, _tx)) = device.receive() { + rx.consume(timestamp, |pkt| { + pkts.push(pkt.to_vec()); + Ok(()) + }) + .unwrap(); + } + pkts +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct MockTxToken; + +impl TxToken for MockTxToken { + fn consume(self, _: Instant, _: usize, _: F) -> Result + where + F: FnOnce(&mut [u8]) -> Result, + { + Err(Error::Unaddressable) + } +} + +#[test] +#[should_panic(expected = "hardware_addr required option was not set")] +#[cfg(all(feature = "medium-ethernet"))] +fn test_builder_initialization_panic() { + let mut device = Loopback::new(Medium::Ethernet); + InterfaceBuilder::new().finalize(&mut device); +} + +#[test] +#[cfg(feature = "proto-ipv4")] +fn test_no_icmp_no_unicast_ipv4() { + let (mut iface, mut sockets, _device) = create(); + + // Unknown Ipv4 Protocol + // + // Because the destination is the broadcast address + // this should not trigger and Destination Unreachable + // response. See RFC 1122 § 3.2.2. + let repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + hop_limit: 0x40, + }); + + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // Ensure that the unknown protocol frame does not trigger an + // ICMP error response when the destination address is a + // broadcast address + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); +} + +#[test] +#[cfg(feature = "proto-ipv6")] +fn test_no_icmp_no_unicast_ipv6() { + let (mut iface, mut sockets, _device) = create(); + + // Unknown Ipv6 Protocol + // + // Because the destination is the broadcast address + // this should not trigger and Destination Unreachable + // response. See RFC 1122 § 3.2.2. + let repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), + dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + hop_limit: 0x40, + }); + + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv6Packet::new_unchecked(&bytes); + + // 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 sockets, &frame), None); +} + +#[test] +#[cfg(feature = "proto-ipv4")] +fn test_icmp_error_no_payload() { + static NO_BYTES: [u8; 0] = []; + let (mut iface, mut sockets, _device) = create(); + + // Unknown Ipv4 Protocol with no payload + let repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + hop_limit: 0x40, + }); + + let mut bytes = vec![0u8; 34]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // The expected Destination Unreachable response due to the + // unknown protocol + let icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::ProtoUnreachable, + header: Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Unknown(12), + payload_len: 0, + hop_limit: 64, + }, + data: &NO_BYTES, + }; + + let expected_repr = IpPacket::Icmpv4(( + Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }, + icmp_repr, + )); + + // Ensure that the unknown protocol triggers an error response. + // And we correctly handle no payload. + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!( + iface.inner.process_ipv4(&mut sockets, &frame, None), + Some(expected_repr) + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + Some(expected_repr) + ); +} + +#[test] +#[cfg(feature = "proto-ipv4")] +fn test_local_subnet_broadcasts() { + let (mut iface, _, _device) = create(); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); + }); + }); + + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])),); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); + }); + }); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])),); + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])),); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); + }); + }); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])),); + assert!(!iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])),); + assert!(iface + .inner + .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])),); +} + +#[test] +#[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] +fn test_icmp_error_port_unreachable() { + static UDP_PAYLOAD: [u8; 12] = [ + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, + ]; + let (mut iface, mut sockets, _device) = create(); + + let mut udp_bytes_unicast = vec![0u8; 20]; + let mut udp_bytes_broadcast = vec![0u8; 20]; + let mut packet_unicast = UdpPacket::new_unchecked(&mut udp_bytes_unicast); + let mut packet_broadcast = UdpPacket::new_unchecked(&mut udp_bytes_broadcast); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }); + + // Emit the representations to a packet + udp_repr.emit( + &mut packet_unicast, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + let data = packet_unicast.into_inner(); + + // The expected Destination Unreachable ICMPv4 error response due + // to no sockets listening on the destination port. + let icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::PortUnreachable, + header: Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }, + data, + }; + let expected_repr = IpPacket::Icmpv4(( + Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }, + icmp_repr, + )); + + // Ensure that the unknown protocol triggers an error response. + // And we correctly handle no payload. + assert_eq!( + iface + .inner + .process_udp(&mut sockets, ip_repr, udp_repr, false, &UDP_PAYLOAD, data), + Some(expected_repr) + ); + + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }); + + // Emit the representations to a packet + udp_repr.emit( + &mut packet_broadcast, + &ip_repr.src_addr(), + &IpAddress::Ipv4(Ipv4Address::BROADCAST), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + // 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 sockets, + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet_broadcast.into_inner(), + ), + None + ); +} + +#[test] +#[cfg(feature = "socket-udp")] +fn test_handle_udp_broadcast() { + use crate::wire::IpEndpoint; + + static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; + + let (mut iface, mut sockets, _device) = create(); + + let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + + let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); + + let mut udp_bytes = vec![0u8; 13]; + let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); + + let socket_handle = sockets.add(udp_socket); + + #[cfg(feature = "proto-ipv6")] + let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] + let src_ip = Ipv4Address::new(0x7f, 0x00, 0x00, 0x02); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + + #[cfg(feature = "proto-ipv6")] + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: src_ip, + dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 0x40, + }); + #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: src_ip, + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 0x40, + }); + + // Bind the socket to port 68 + let socket = sockets.get_mut::(socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + + udp_repr.emit( + &mut packet, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + // Packet should be handled by bound UDP socket + assert_eq!( + iface.inner.process_udp( + &mut sockets, + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet.into_inner(), + ), + None + ); + + // Make sure the payload to the UDP packet processed by process_udp is + // appended to the bound sockets rx_buffer + let socket = sockets.get_mut::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) + ); +} + +#[test] +#[cfg(feature = "proto-ipv4")] +fn test_handle_ipv4_broadcast() { + use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; + + let (mut iface, mut sockets, _device) = create(); + + let our_ipv4_addr = iface.ipv4_address().unwrap(); + let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); + + // ICMPv4 echo request + let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; + let icmpv4_repr = Icmpv4Repr::EchoRequest { + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, + }; + + // Send to IPv4 broadcast address + let ipv4_repr = Ipv4Repr { + src_addr: src_ipv4_addr, + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: icmpv4_repr.buffer_len(), + }; + + // Emit to ip frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + icmpv4_repr.emit( + &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + // Expected ICMPv4 echo reply + let expected_icmpv4_repr = Icmpv4Repr::EchoReply { + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, + }; + let expected_ipv4_repr = Ipv4Repr { + src_addr: our_ipv4_addr, + dst_addr: src_ipv4_addr, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: expected_icmpv4_repr.buffer_len(), + }; + let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!( + iface.inner.process_ipv4(&mut sockets, &frame, None), + Some(expected_packet) + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + Some(expected_packet) + ); +} + +#[test] +#[cfg(feature = "socket-udp")] +fn test_icmp_reply_size() { + #[cfg(feature = "proto-ipv6")] + use crate::wire::Icmpv6DstUnreachable; + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + use crate::wire::IPV4_MIN_MTU as MIN_MTU; + #[cfg(feature = "proto-ipv6")] + use crate::wire::IPV6_MIN_MTU as MIN_MTU; + + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + const MAX_PAYLOAD_LEN: usize = 528; + #[cfg(feature = "proto-ipv6")] + const MAX_PAYLOAD_LEN: usize = 1192; + + let (mut iface, mut sockets, _device) = create(); + + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + let src_addr = Ipv4Address([192, 168, 1, 1]); + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + let dst_addr = Ipv4Address([192, 168, 1, 2]); + #[cfg(feature = "proto-ipv6")] + let src_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + #[cfg(feature = "proto-ipv6")] + let dst_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2); + + // UDP packet that if not tructated will cause a icmp port unreachable reply + // to exeed the minimum mtu bytes in length. + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + MAX_PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + let ip_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, + }; + #[cfg(feature = "proto-ipv6")] + let ip_repr = Ipv6Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, + }; + let payload = packet.into_inner(); + + // Expected packets + #[cfg(feature = "proto-ipv6")] + let expected_icmp_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ip_repr, + data: &payload[..MAX_PAYLOAD_LEN], + }; + #[cfg(feature = "proto-ipv6")] + let expected_ip_repr = Ipv6Repr { + src_addr: dst_addr, + dst_addr: src_addr, + next_header: IpProtocol::Icmpv6, + hop_limit: 64, + payload_len: expected_icmp_repr.buffer_len(), + }; + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + let expected_icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::PortUnreachable, + header: ip_repr, + data: &payload[..MAX_PAYLOAD_LEN], + }; + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + let expected_ip_repr = Ipv4Repr { + src_addr: dst_addr, + dst_addr: src_addr, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: expected_icmp_repr.buffer_len(), + }; + + // The expected packet does not exceed the IPV4_MIN_MTU + #[cfg(feature = "proto-ipv6")] + assert_eq!( + expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), + MIN_MTU + ); + // The expected packet does not exceed the IPV4_MIN_MTU + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + 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 sockets, + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), + Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) + ); + #[cfg(feature = "proto-ipv6")] + assert_eq!( + iface.inner.process_udp( + &mut sockets, + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), + Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) + ); +} + +#[test] +#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] +fn test_handle_valid_arp_request() { + let (mut iface, mut sockets, _device) = create_ethernet(); + + let mut eth_bytes = vec![0u8; 42]; + + let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: local_ip_addr, + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + + // Ensure an ARP Request for us triggers an ARP Reply + assert_eq!( + iface + .inner + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: local_hw_addr, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + })) + ); + + // Ensure the address of the requestor was entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr) + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); +} + +#[test] +#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] +fn test_handle_valid_ndisc_request() { + let (mut iface, mut sockets, _device) = create_ethernet(); + + let mut eth_bytes = vec![0u8; 86]; + + let local_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1); + let remote_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { + target_addr: local_ip_addr, + lladdr: Some(remote_hw_addr.into()), + }); + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: remote_ip_addr, + dst_addr: local_ip_addr.solicited_node(), + next_header: IpProtocol::Icmpv6, + hop_limit: 0xff, + payload_len: solicit.buffer_len(), + }); + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00])); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Ipv6); + ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); + solicit.emit( + &remote_ip_addr.into(), + &local_ip_addr.solicited_node().into(), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), + &ChecksumCapabilities::default(), + ); + + let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr: local_ip_addr, + lladdr: Some(local_hw_addr.into()), + }); + + let ipv6_expected = Ipv6Repr { + src_addr: local_ip_addr, + dst_addr: remote_ip_addr, + next_header: IpProtocol::Icmpv6, + hop_limit: 0xff, + payload_len: icmpv6_expected.buffer_len(), + }; + + // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement + assert_eq!( + iface + .inner + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + 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, + &IpAddress::Ipv6(local_ip_addr), + &IpAddress::Ipv6(remote_ip_addr) + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); +} + +#[test] +#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] +fn test_handle_other_arp_request() { + let (mut iface, mut sockets, _device) = create_ethernet(); + + let mut eth_bytes = vec![0u8; 42]; + + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x03]), + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + + // Ensure an ARP Request for someone else does not trigger an ARP Reply + assert_eq!( + iface + .inner + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + None + ); + + // Ensure the address of the requestor was NOT entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), + &IpAddress::Ipv4(remote_ip_addr) + ), + Err(Error::Unaddressable) + ); +} + +#[test] +#[cfg(all( + feature = "medium-ethernet", + feature = "proto-ipv4", + not(feature = "medium-ieee802154") +))] +fn test_arp_flush_after_update_ip() { + let (mut iface, mut sockets, _device) = create_ethernet(); + + let mut eth_bytes = vec![0u8; 42]; + + let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + { + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + } + + // Ensure an ARP Request for us triggers an ARP Reply + assert_eq!( + iface + .inner + .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: local_hw_addr, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + })) + ); + + // Ensure the address of the requestor was entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr) + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); + + // Update IP addrs to trigger ARP cache flush + let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); + }); + }); + + // ARP cache flush after address change + assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); +} + +#[test] +#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] +fn test_icmpv4_socket() { + use crate::wire::Icmpv4Packet; + + let (mut iface, mut sockets, _device) = create(); + + let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); + let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); + + let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); + + let socket_handle = sockets.add(icmpv4_socket); + + let ident = 0x1234; + let seq_no = 0x5432; + let echo_data = &[0xff; 16]; + + let socket = sockets.get_mut::(socket_handle); + // Bind to the ID 0x1234 + assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); + + // Ensure the ident we bound to and the ident of the packet are the same. + let mut bytes = [0xff; 24]; + let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); + let echo_repr = Icmpv4Repr::EchoRequest { + ident, + seq_no, + data: echo_data, + }; + echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); + let icmp_data = &*packet.into_inner(); + + let ipv4_repr = Ipv4Repr { + src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), + dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), + next_header: IpProtocol::Icmp, + payload_len: 24, + hop_limit: 64, + }; + let ip_repr = IpRepr::Ipv4(ipv4_repr); + + // Open a socket and ensure the packet is handled due to the listening + // socket. + assert!(!sockets.get_mut::(socket_handle).can_recv()); + + // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening + let echo_reply = Icmpv4Repr::EchoReply { + ident, + seq_no, + data: echo_data, + }; + let ipv4_reply = Ipv4Repr { + src_addr: ipv4_repr.dst_addr, + dst_addr: ipv4_repr.src_addr, + ..ipv4_repr + }; + assert_eq!( + iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), + Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) + ); + + let socket = sockets.get_mut::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok(( + icmp_data, + IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) + )) + ); +} + +#[test] +#[cfg(feature = "proto-ipv6")] +fn test_solicited_node_addrs() { + let (mut iface, _, _device) = create(); + let mut new_addrs = vec![ + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), + IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), + ]; + iface.update_ip_addrs(|addrs| { + new_addrs.extend(addrs.to_vec()); + *addrs = From::from(new_addrs); + }); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); + assert!(!iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); +} + +#[test] +#[cfg(feature = "proto-ipv6")] +fn test_icmpv6_nxthdr_unknown() { + let (mut iface, mut sockets, _device) = create(); + + let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + + let payload = [0x12, 0x34, 0x56, 0x78]; + + let ipv6_repr = Ipv6Repr { + src_addr: remote_ip_addr, + dst_addr: Ipv6Address::LOOPBACK, + next_header: IpProtocol::HopByHop, + payload_len: 12, + hop_limit: 0x40, + }; + + let mut bytes = vec![0; 52]; + let frame = { + let ip_repr = IpRepr::Ipv6(ipv6_repr); + ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let mut offset = ipv6_repr.buffer_len(); + { + let mut hbh_pkt = Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); + hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); + hbh_pkt.set_header_len(0); + offset += 8; + { + let mut pad_pkt = Ipv6Option::new_unchecked(&mut *hbh_pkt.options_mut()); + Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt); + } + { + let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.options_mut()[5..]); + Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); + } + } + bytes[offset..].copy_from_slice(&payload); + Ipv6Packet::new_unchecked(&bytes) + }; + + let reply_icmp_repr = Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, + pointer: 40, + header: ipv6_repr, + data: &payload[..], + }; + + let reply_ipv6_repr = Ipv6Repr { + src_addr: Ipv6Address::LOOPBACK, + dst_addr: remote_ip_addr, + next_header: IpProtocol::Icmpv6, + payload_len: reply_icmp_repr.buffer_len(), + hop_limit: 0x40, + }; + + // 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 sockets, &frame), + Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) + ); +} + +#[test] +#[cfg(feature = "proto-igmp")] +fn test_handle_igmp() { + fn recv_igmp(device: &mut Loopback, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + let caps = device.capabilities(); + let checksum_caps = &caps.checksum; + recv_all(device, timestamp) + .iter() + .filter_map(|frame| { + let ipv4_packet = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + let eth_frame = EthernetFrame::new_checked(frame).ok()?; + Ipv4Packet::new_checked(eth_frame.payload()).ok()? + } + #[cfg(feature = "medium-ip")] + Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => todo!(), + }; + let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; + let ip_payload = ipv4_packet.payload(); + let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; + let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; + Some((ipv4_repr, igmp_repr)) + }) + .collect::>() + } + + let groups = [ + Ipv4Address::new(224, 0, 0, 22), + Ipv4Address::new(224, 0, 0, 56), + ]; + + let (mut iface, mut sockets, mut device) = create(); + + // Join multicast groups + let timestamp = Instant::now(); + for group in &groups { + iface + .join_multicast_group(&mut device, *group, timestamp) + .unwrap(); + } + + let reports = recv_igmp(&mut device, timestamp); + assert_eq!(reports.len(), 2); + for (i, group_addr) in groups.iter().enumerate() { + assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); + assert_eq!(reports[i].0.dst_addr, *group_addr); + assert_eq!( + reports[i].1, + IgmpRepr::MembershipReport { + group_addr: *group_addr, + version: IgmpVersion::Version2, + } + ); + } + + // General query + let timestamp = Instant::now(); + const GENERAL_QUERY_BYTES: &[u8] = &[ + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, + 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + ]; + { + // Transmit GENERAL_QUERY_BYTES into loopback + let tx_token = device.transmit().unwrap(); + tx_token + .consume(timestamp, GENERAL_QUERY_BYTES.len(), |buffer| { + buffer.copy_from_slice(GENERAL_QUERY_BYTES); + Ok(()) + }) + .unwrap(); + } + // Trigger processing until all packets received through the + // 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 device, &mut sockets); + + // Leave multicast groups + let timestamp = Instant::now(); + for group in &groups { + iface + .leave_multicast_group(&mut device, *group, timestamp) + .unwrap(); + } + + let leaves = recv_igmp(&mut device, timestamp); + assert_eq!(leaves.len(), 2); + for (i, group_addr) in groups.iter().cloned().enumerate() { + assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); + assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); + assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); + } +} + +#[test] +#[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] +fn test_raw_socket_no_reply() { + use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + + let (mut iface, mut sockets, _device) = create(); + + let packets = 1; + let rx_buffer = + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); + let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); + sockets.add(raw_socket); + + let src_addr = Ipv4Address([127, 0, 0, 2]); + let dst_addr = Ipv4Address([127, 0, 0, 1]); + + const PAYLOAD_LEN: usize = 10; + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + let ipv4_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + PAYLOAD_LEN, + }; + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + udp_repr.emit( + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); +} + +#[test] +#[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] +fn test_raw_socket_with_udp_socket() { + use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + + static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; + + let (mut iface, mut sockets, _device) = create(); + + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + let udp_socket_handle = sockets.add(udp_socket); + + // Bind the socket to port 68 + let socket = sockets.get_mut::(udp_socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + + let packets = 1; + let raw_rx_buffer = + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let raw_tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); + let raw_socket = raw::Socket::new( + IpVersion::Ipv4, + IpProtocol::Udp, + raw_rx_buffer, + raw_tx_buffer, + ); + sockets.add(raw_socket); + + let src_addr = Ipv4Address([127, 0, 0, 2]); + let dst_addr = Ipv4Address([127, 0, 0, 1]); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + let ipv4_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + }; + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + udp_repr.emit( + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &src_addr.into(), + &dst_addr.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + #[cfg(not(feature = "proto-ipv4-fragmentation"))] + assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); + #[cfg(feature = "proto-ipv4-fragmentation")] + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + &frame, + Some(&mut iface.fragments.ipv4_fragments) + ), + None + ); + + // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP + let socket = sockets.get_mut::(udp_socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) + ); +} From 5451ecc2d4aae76f29cab66c97d8aeee5dd0e966 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 23 Oct 2022 23:18:49 +0200 Subject: [PATCH 431/566] Fix missing `#[test]` --- src/wire/ip.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 519203a4f..3f8a53c32 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -958,6 +958,7 @@ pub(crate) mod test { test_eq(32, Ipv4Address::new(255, 255, 255, 255)); } + #[test] #[cfg(feature = "proto-ipv4")] fn to_prefix_len_ipv4_error() { assert_eq!( @@ -982,6 +983,7 @@ pub(crate) mod test { ); } + #[test] #[cfg(feature = "proto-ipv6")] fn to_prefix_len_ipv6_error() { assert_eq!( From a55c00fab7f56c08504b693ed1792a43d2849433 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 24 Oct 2022 01:12:09 +0200 Subject: [PATCH 432/566] assembler: do not return whether there was an overlap. This functionality is completely unused, and was panicky with overflow checking (see #694) Fixes #694 --- src/iface/fragmentation.rs | 6 +---- src/socket/tcp.rs | 2 +- src/storage/assembler.rs | 54 ++++++++++++++++++++------------------ 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 74b09a1db..2ec35270f 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -182,12 +182,8 @@ impl<'a> PacketAssembler<'a> { ); match assembler.add(offset, data.len()) { - Ok(overlap) => { + Ok(()) => { net_debug!("assembler: {}", self.assembler); - - if overlap { - net_debug!("packet was added, but there was an overlap."); - } self.is_complete() } // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 6b05bce44..242de2702 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1764,7 +1764,7 @@ impl<'a> Socket<'a> { // Try adding payload octets to the assembler. match self.assembler.add(payload_offset, payload_len) { - Ok(_) => { + Ok(()) => { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. tcp_trace!( diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 8a52d53c3..4770a246e 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -194,10 +194,8 @@ impl Assembler { /// Add a new contiguous range to the assembler, and return `Ok(bool)`, /// or return `Err(TooManyHolesError)` if too many discontiguities are already recorded. - /// Returns `Ok(true)` when there was an overlap. - pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result { + pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result<(), TooManyHolesError> { let mut index = 0; - let mut overlap = size; while index != self.contigs.len() && size != 0 { let contig = self.contigs[index]; @@ -208,7 +206,6 @@ impl Assembler { // The range being added covers the entire hole in this contig, merge it // into the previous config. self.contigs[index - 1].expand_data_by(contig.total_size()); - overlap -= contig.total_size(); self.remove_contig_at(index); index += 0; } else if offset == 0 && size < contig.hole_size && index > 0 { @@ -217,12 +214,10 @@ impl Assembler { // the previous contig. self.contigs[index - 1].expand_data_by(size); self.contigs[index].shrink_hole_by(size); - overlap -= size; index += 1; } else if offset <= contig.hole_size && offset + size >= contig.hole_size { // The range being added covers both a part of the hole and a part of the data // in this contig, shrink the hole in this contig. - overlap -= contig.hole_size - offset; self.contigs[index].shrink_hole_to(offset); index += 1; } else if offset + size >= contig.hole_size { @@ -234,7 +229,6 @@ impl Assembler { { let inserted = self.add_contig_at(index)?; *inserted = Contig::hole_and_data(offset, size); - overlap -= size; } // Previous contigs[index] got moved to contigs[index+1] self.contigs[index + 1].shrink_hole_by(offset + size); @@ -253,7 +247,7 @@ impl Assembler { } debug_assert!(size == 0); - Ok(overlap != 0) + Ok(()) } /// Remove a contiguous range from the front of the assembler and `Some(data_size)`, @@ -364,84 +358,84 @@ mod test { #[test] fn test_empty_add_full() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(false)); + assert_eq!(assr.add(0, 16), Ok(())); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_empty_add_front() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 4), Ok(false)); + assert_eq!(assr.add(0, 4), Ok(())); assert_eq!(assr, contigs![(0, 4), (12, 0)]); } #[test] fn test_empty_add_back() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(12, 4), Ok(false)); + assert_eq!(assr.add(12, 4), Ok(())); assert_eq!(assr, contigs![(12, 4)]); } #[test] fn test_empty_add_mid() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(4, 8), Ok(false)); + assert_eq!(assr.add(4, 8), Ok(())); assert_eq!(assr, contigs![(4, 8), (4, 0)]); } #[test] fn test_partial_add_front() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 4), Ok(false)); + assert_eq!(assr.add(0, 4), Ok(())); assert_eq!(assr, contigs![(0, 12), (4, 0)]); } #[test] fn test_partial_add_back() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(12, 4), Ok(false)); + assert_eq!(assr.add(12, 4), Ok(())); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_front_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 8), Ok(true)); + assert_eq!(assr.add(0, 8), Ok(())); assert_eq!(assr, contigs![(0, 12), (4, 0)]); } #[test] fn test_partial_add_front_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(2, 6), Ok(true)); + assert_eq!(assr.add(2, 6), Ok(())); assert_eq!(assr, contigs![(2, 10), (4, 0)]); } #[test] fn test_partial_add_back_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(8, 8), Ok(true)); + assert_eq!(assr.add(8, 8), Ok(())); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_back_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(10, 4), Ok(true)); + assert_eq!(assr.add(10, 4), Ok(())); assert_eq!(assr, contigs![(4, 10), (2, 0)]); } #[test] fn test_partial_add_both_overlap() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(0, 16), Ok(true)); + assert_eq!(assr.add(0, 16), Ok(())); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_partial_add_both_overlap_split() { let mut assr = contigs![(4, 8), (4, 0)]; - assert_eq!(assr.add(2, 12), Ok(true)); + assert_eq!(assr.add(2, 12), Ok(())); assert_eq!(assr, contigs![(2, 12), (2, 0)]); } @@ -449,7 +443,7 @@ mod test { fn test_rejected_add_keeps_state() { let mut assr = Assembler::new(CONTIG_COUNT * 20); for c in 1..=CONTIG_COUNT - 1 { - assert_eq!(assr.add(c * 10, 3), Ok(false)); + assert_eq!(assr.add(c * 10, 3), Ok(())); } // Maximum of allowed holes is reached let assr_before = assr.clone(); @@ -499,7 +493,7 @@ mod test { #[test] fn test_iter_full() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(false)); + assert_eq!(assr.add(0, 16), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 26)]); } @@ -507,7 +501,7 @@ mod test { #[test] fn test_iter_offset() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 16), Ok(false)); + assert_eq!(assr.add(0, 16), Ok(())); let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(100, 116)]); } @@ -515,7 +509,7 @@ mod test { #[test] fn test_iter_one_front() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(0, 4), Ok(false)); + assert_eq!(assr.add(0, 4), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 14)]); } @@ -523,7 +517,7 @@ mod test { #[test] fn test_iter_one_back() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(12, 4), Ok(false)); + assert_eq!(assr.add(12, 4), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(22, 26)]); } @@ -531,7 +525,7 @@ mod test { #[test] fn test_iter_one_mid() { let mut assr = Assembler::new(16); - assert_eq!(assr.add(4, 8), Ok(false)); + assert_eq!(assr.add(4, 8), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(14, 22)]); } @@ -556,4 +550,12 @@ mod test { let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(102, 108), (110, 111), (113, 115)]); } + + #[test] + fn test_issue_694() { + let mut assr = Assembler::new(16); + assert_eq!(assr.add(0, 1), Ok(())); + assert_eq!(assr.add(2, 1), Ok(())); + assert_eq!(assr.add(1, 1), Ok(())); + } } From 09d64b0bedcd6c784254b451a49286251c869627 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 26 Oct 2022 14:46:56 +0200 Subject: [PATCH 433/566] Fix how `Instant` is displayed Instant was displayed incorrectly. For example, `Instant::from_millis(74)` would have been displayed as "0.74s" instead of the correct "0.074s". --- src/time.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/time.rs b/src/time.rs index c91b40559..961e4d7e7 100644 --- a/src/time.rs +++ b/src/time.rs @@ -130,7 +130,7 @@ impl From for ::std::time::SystemTime { impl fmt::Display for Instant { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}.{}s", self.secs(), self.millis()) + write!(f, "{}.{:0>3}s", self.secs(), self.millis()) } } @@ -361,8 +361,9 @@ mod test { #[test] fn test_instant_display() { + assert_eq!(format!("{}", Instant::from_millis(74)), "0.074s"); assert_eq!(format!("{}", Instant::from_millis(5674)), "5.674s"); - assert_eq!(format!("{}", Instant::from_millis(5000)), "5.0s"); + assert_eq!(format!("{}", Instant::from_millis(5000)), "5.000s"); } #[test] From 4300e7cc3ba1c8e485bd10b50af2f0a3dbbe2776 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Nov 2021 19:45:22 +0100 Subject: [PATCH 434/566] GAT-based Device trait. The current `'a` lifetime in the `Device` trait is essentially a workaround for lack of GATs. I'm just experimenting how this would look like, it'll have to wait until GATs are stable to go in. The main benefit is structs implementing `Device` can now borrow stuff. This wasn't possible before because the `for<'d> T: Device<'d>` bounds would essentially imply `T: 'static`. --- examples/utils.rs | 2 +- fuzz/utils.rs | 2 +- src/iface/interface/mod.rs | 18 +++++++++--------- src/phy/fault_injector.rs | 21 +++++++++++---------- src/phy/fuzz_injector.rs | 23 +++++++++++++---------- src/phy/loopback.rs | 10 +++++----- src/phy/mod.rs | 24 ++++++++++++++---------- src/phy/pcap_writer.rs | 21 ++++++++++++--------- src/phy/raw_socket.rs | 14 +++++++++----- src/phy/tracer.rs | 21 +++++++++++---------- src/phy/tuntap_interface.rs | 10 +++++----- 11 files changed, 91 insertions(+), 75 deletions(-) diff --git a/examples/utils.rs b/examples/utils.rs index 8473c7303..fc3193593 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -159,7 +159,7 @@ pub fn parse_middleware_options( loopback: bool, ) -> FaultInjector>>> where - D: for<'a> Device<'a>, + D: Device, { let drop_chance = matches .opt_str("drop-chance") diff --git a/fuzz/utils.rs b/fuzz/utils.rs index 89329d99d..206443420 100644 --- a/fuzz/utils.rs +++ b/fuzz/utils.rs @@ -93,7 +93,7 @@ pub fn parse_middleware_options( loopback: bool, ) -> FaultInjector>>> where - D: for<'a> Device<'a>, + D: Device, { let drop_chance = matches .opt_str("drop-chance") diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index b604c2397..c5050908b 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -563,7 +563,7 @@ let iface = builder.finalize(&mut device); /// [neighbor_cache]: #method.neighbor_cache pub fn finalize(self, device: &mut D) -> Interface<'a> where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { let caps = device.capabilities(); @@ -905,7 +905,7 @@ impl<'a> Interface<'a> { timestamp: Instant, ) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { self.inner.now = timestamp; @@ -947,7 +947,7 @@ impl<'a> Interface<'a> { timestamp: Instant, ) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { self.inner.now = timestamp; @@ -1047,7 +1047,7 @@ impl<'a> Interface<'a> { sockets: &mut SocketSet<'_>, ) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { self.inner.now = timestamp; @@ -1152,7 +1152,7 @@ impl<'a> Interface<'a> { fn socket_ingress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { let mut processed_any = false; let Self { @@ -1208,7 +1208,7 @@ impl<'a> Interface<'a> { fn socket_egress(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { let Self { inner, @@ -1318,7 +1318,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-igmp")] fn igmp_egress(&mut self, device: &mut D) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { match self.inner.igmp_report_state { IgmpReportState::ToSpecificQuery { @@ -1384,7 +1384,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] fn ipv4_egress(&mut self, device: &mut D) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { // Reset the buffer when we transmitted everything. if self.out_packets.ipv4_out_packet.finished() { @@ -1422,7 +1422,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] fn sixlowpan_egress(&mut self, device: &mut D) -> Result where - D: for<'d> Device<'d> + ?Sized, + D: Device + ?Sized, { // Reset the buffer when we transmitted everything. if self.out_packets.sixlowpan_out_packet.finished() { diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 6a83bf759..bf6bc6528 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -94,13 +94,13 @@ impl State { /// adverse network conditions (such as random packet loss or corruption), or software /// or hardware limitations (such as a limited number or size of usable network buffers). #[derive(Debug)] -pub struct FaultInjector Device<'a>> { +pub struct FaultInjector { inner: D, state: RefCell, config: Config, } -impl Device<'a>> FaultInjector { +impl FaultInjector { /// Create a fault injector device, using the given random number generator seed. pub fn new(inner: D, seed: u32) -> FaultInjector { let state = State { @@ -195,12 +195,13 @@ impl Device<'a>> FaultInjector { } } -impl<'a, D> Device<'a> for FaultInjector -where - D: for<'b> Device<'b>, -{ - type RxToken = RxToken<'a, >::RxToken>; - type TxToken = TxToken<'a, >::TxToken>; +impl Device for FaultInjector { + type RxToken<'a> = RxToken<'a, D::RxToken<'a>> + where + Self: 'a; + type TxToken<'a> = TxToken<'a, D::TxToken<'a>> + where + Self: 'a; fn capabilities(&self) -> DeviceCapabilities { let mut caps = self.inner.capabilities(); @@ -210,7 +211,7 @@ where caps } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let &mut Self { ref mut inner, ref state, @@ -233,7 +234,7 @@ where }) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { let &mut Self { ref mut inner, ref state, diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 4be1fc0e2..025517955 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -19,14 +19,14 @@ pub trait Fuzzer { #[allow(unused)] #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FuzzInjector Device<'a>, FTx: Fuzzer, FRx: Fuzzer> { +pub struct FuzzInjector { inner: D, fuzz_tx: FTx, fuzz_rx: FRx, } #[allow(unused)] -impl Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector { +impl FuzzInjector { /// Create a fuzz injector device. pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector { FuzzInjector { @@ -42,14 +42,17 @@ impl Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector } } -impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector +impl Device for FuzzInjector where - D: for<'b> Device<'b>, - FTx: Fuzzer + 'a, - FRx: Fuzzer + 'a, + FTx: Fuzzer, + FRx: Fuzzer, { - type RxToken = RxToken<'a, >::RxToken, FRx>; - type TxToken = TxToken<'a, >::TxToken, FTx>; + type RxToken<'a> = RxToken<'a, D::RxToken<'a>, FRx> + where + Self: 'a; + type TxToken<'a> = TxToken<'a, D::TxToken<'a>, FTx> + where + Self: 'a; fn capabilities(&self) -> DeviceCapabilities { let mut caps = self.inner.capabilities(); @@ -59,7 +62,7 @@ where caps } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let &mut Self { ref mut inner, ref fuzz_rx, @@ -78,7 +81,7 @@ where }) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { let &mut Self { ref mut inner, fuzz_rx: _, diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index 9b39aed33..e6a317828 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -29,9 +29,9 @@ impl Loopback { } } -impl<'a> Device<'a> for Loopback { - type RxToken = RxToken; - type TxToken = TxToken<'a>; +impl Device for Loopback { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken<'a>; fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { @@ -41,7 +41,7 @@ impl<'a> Device<'a> for Loopback { } } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.queue.pop_front().map(move |buffer| { let rx = RxToken { buffer }; let tx = TxToken { @@ -51,7 +51,7 @@ impl<'a> Device<'a> for Loopback { }) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { Some(TxToken { queue: &mut self.queue, }) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 3a7799dab..d95d02ff3 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -38,16 +38,16 @@ impl<'a> StmPhy { } } -impl<'a> phy::Device<'a> for StmPhy { - type RxToken = StmPhyRxToken<'a>; - type TxToken = StmPhyTxToken<'a>; +impl phy::Device for StmPhy { + type RxToken<'a> = StmPhyRxToken<'a> where Self: 'a; + type TxToken<'a> = StmPhyTxToken<'a> where Self: 'a; - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { Some((StmPhyRxToken(&mut self.rx_buffer[..]), StmPhyTxToken(&mut self.tx_buffer[..]))) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { Some(StmPhyTxToken(&mut self.tx_buffer[..])) } @@ -308,9 +308,13 @@ impl Default for Medium { /// The interface is based on _tokens_, which are types that allow to receive/transmit a /// single packet. The `receive` and `transmit` functions only construct such tokens, the /// real sending/receiving operation are performed when the tokens are consumed. -pub trait Device<'a> { - type RxToken: RxToken + 'a; - type TxToken: TxToken + 'a; +pub trait Device { + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; /// Construct a token pair consisting of one receive token and one transmit token. /// @@ -318,10 +322,10 @@ pub trait Device<'a> { /// on the contents of the received packet. For example, this makes it possible to /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes /// need to be sent back, without heap allocation. - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)>; + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; /// Construct a transmit token. - fn transmit(&'a mut self) -> Option; + fn transmit(&mut self) -> Option>; /// Get a description of device capabilities. fn capabilities(&self) -> DeviceCapabilities; diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 069e55503..edaf7844f 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -118,7 +118,7 @@ impl PcapSink for T { #[derive(Debug)] pub struct PcapWriter where - D: for<'a> Device<'a>, + D: Device, S: PcapSink, { lower: D, @@ -126,7 +126,7 @@ where mode: PcapMode, } -impl Device<'a>, S: PcapSink> PcapWriter { +impl PcapWriter { /// Creates a packet capture writer. pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter { let medium = lower.capabilities().medium; @@ -162,19 +162,22 @@ impl Device<'a>, S: PcapSink> PcapWriter { } } -impl<'a, D, S> Device<'a> for PcapWriter +impl Device for PcapWriter where - D: for<'b> Device<'b>, - S: PcapSink + 'a, + S: PcapSink, { - type RxToken = RxToken<'a, >::RxToken, S>; - type TxToken = TxToken<'a, >::TxToken, S>; + type RxToken<'a> = RxToken<'a, D::RxToken<'a>, S> + where + Self: 'a; + type TxToken<'a> = TxToken<'a, D::TxToken<'a>, S> + where + Self: 'a; fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let sink = &self.sink; let mode = self.mode; self.lower.receive().map(move |(rx_token, tx_token)| { @@ -192,7 +195,7 @@ where }) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { let sink = &self.sink; let mode = self.mode; self.lower diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index b760983a5..0cc45200d 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -54,9 +54,13 @@ impl RawSocket { } } -impl<'a> Device<'a> for RawSocket { - type RxToken = RxToken; - type TxToken = TxToken; +impl Device for RawSocket { + type RxToken<'a> = RxToken + where + Self: 'a; + type TxToken<'a> = TxToken + where + Self: 'a; fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { @@ -66,7 +70,7 @@ impl<'a> Device<'a> for RawSocket { } } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; match lower.recv(&mut buffer[..]) { @@ -83,7 +87,7 @@ impl<'a> Device<'a> for RawSocket { } } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { Some(TxToken { lower: self.lower.clone(), }) diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index fdf5b1cfc..934c6f0f0 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -12,12 +12,12 @@ use crate::{ /// A tracer is a device that pretty prints all packets traversing it /// using the provided writer function, and then passes them to another /// device. -pub struct Tracer Device<'a>> { +pub struct Tracer { inner: D, writer: fn(Instant, Packet), } -impl Device<'a>> Tracer { +impl Tracer { /// Create a tracer device. pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer { Tracer { inner, writer } @@ -44,18 +44,19 @@ impl Device<'a>> Tracer { } } -impl<'a, D> Device<'a> for Tracer -where - D: for<'b> Device<'b>, -{ - type RxToken = RxToken<>::RxToken>; - type TxToken = TxToken<>::TxToken>; +impl Device for Tracer { + type RxToken<'a> = RxToken> + where + Self: 'a; + type TxToken<'a> = TxToken> + where + Self: 'a; fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let &mut Self { ref mut inner, writer, @@ -77,7 +78,7 @@ where }) } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { let &mut Self { ref mut inner, writer, diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index 6792a7be5..7bc2aa0d9 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -40,9 +40,9 @@ impl TunTapInterface { } } -impl<'a> Device<'a> for TunTapInterface { - type RxToken = RxToken; - type TxToken = TxToken; +impl Device for TunTapInterface { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken; fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { @@ -52,7 +52,7 @@ impl<'a> Device<'a> for TunTapInterface { } } - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; match lower.recv(&mut buffer[..]) { @@ -69,7 +69,7 @@ impl<'a> Device<'a> for TunTapInterface { } } - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { Some(TxToken { lower: self.lower.clone(), }) From 9d86fb9c904d96d8113eea4b2c322f8f15c09e69 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Nov 2022 21:19:48 +0100 Subject: [PATCH 435/566] Bump MSRV to 1.65 --- .github/workflows/test.yml | 8 ++++---- CHANGELOG.md | 2 +- Cargo.toml | 2 +- README.md | 2 +- src/lib.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 160f6452f..a18cb9de9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,8 +20,8 @@ jobs: # Test on stable, MSRV, and nightly. # Failure is permitted on nightly. rust: - - stable - - 1.61.0 + #- stable # TODO: enable again when "stable" is 1.66 or higher. + - 1.65.0 - nightly features: @@ -64,8 +64,8 @@ jobs: # Test on stable, MSRV, and nightly. # Failure is permitted on nightly. rust: - - stable - - 1.61.0 + #- stable # TODO: enable again when "stable" is 1.66 or higher. + - 1.65.0 - nightly features: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e7cbbf7d..d12c4c73b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove IpAddress::Unspecified - When sending packets with a raw socket, the source IP address is sent unmodified (it was previously replaced with the interface's address if it was unspecified). - Fix enable `defmt/alloc` if `alloc` or `std` is enabled. -- Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.60 +- Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.65 ## [0.8.1] - 2022-05-12 diff --git a/Cargo.toml b/Cargo.toml index 994b9e644..29432b64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "smoltcp" version = "0.8.1" edition = "2018" -rust-version = "1.61" +rust-version = "1.65" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." documentation = "https://docs.rs/smoltcp/" diff --git a/README.md b/README.md index 042f1c473..61de6ca0f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ include complicated compile-time computations, such as macro or type tricks, eve at cost of performance degradation. _smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], -and compiles on stable Rust 1.61 and later. +and compiles on stable Rust 1.65 and later. _smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against the Linux TCP stack in loopback mode. diff --git a/src/lib.rs b/src/lib.rs index 08d16c085..74ff6bba7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,7 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.61 and up with any valid set of features. +//! This crate is guaranteed to compile on stable Rust 1.65 and up with any valid set of features. //! It *might* compile on older versions but that may change in any new patch release. //! //! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which From 13cc7f83ac4a6dd623639b688e544ae1f188c828 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Nov 2022 21:29:18 +0100 Subject: [PATCH 436/566] Clippy fixes. --- examples/utils.rs | 6 +----- src/iface/fragmentation.rs | 11 ++++------- src/iface/interface/ipv4.rs | 2 +- src/socket/tcp.rs | 4 ++-- src/wire/icmpv6.rs | 2 +- src/wire/igmp.rs | 2 +- src/wire/ipv6hopbyhop.rs | 4 ++-- src/wire/ipv6routing.rs | 2 +- src/wire/sixlowpan.rs | 15 +++++++-------- 9 files changed, 20 insertions(+), 28 deletions(-) diff --git a/examples/utils.rs b/examples/utils.rs index fc3193593..d2b60cafe 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -84,11 +84,7 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { free.join(" ") ); print!("{}", options.usage(&brief)); - process::exit(if matches.free.len() != free.len() { - 1 - } else { - 0 - }) + process::exit((matches.free.len() != free.len()) as _); } matches } diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 2ec35270f..8796be45a 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -364,7 +364,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the key was not found in the set. pub(crate) fn get_packet_assembler_mut(&mut self, key: &K) -> Result<&mut PacketAssembler<'a>> { if let Some(i) = self.index_buffer.get(key) { - Ok(&mut self.packet_buffer[*i as usize]) + Ok(&mut self.packet_buffer[*i]) } else { Err(Error::PacketAssemblerSetKeyNotFound) } @@ -379,7 +379,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { /// - Returns [`Error::PacketAssemblerIncomplete`] when the fragments assembler was empty or not fully assembled. pub(crate) fn get_assembled_packet(&mut self, key: &K) -> Result<&[u8]> { if let Some(i) = self.index_buffer.get(key) { - let p = self.packet_buffer[*i as usize].assemble()?; + let p = self.packet_buffer[*i].assemble()?; self.index_buffer.remove(key); Ok(p) } else { @@ -392,10 +392,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { loop { let mut key = None; for (k, i) in self.index_buffer.iter() { - if matches!( - self.packet_buffer[*i as usize].assembler, - AssemblerState::NotInit - ) { + if matches!(self.packet_buffer[*i].assembler, AssemblerState::NotInit) { key = Some(*k); break; } @@ -416,7 +413,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { F: Fn(&mut PacketAssembler<'_>) -> Result, { for (_, i) in &mut self.index_buffer.iter() { - let frag = &mut self.packet_buffer[*i as usize]; + let frag = &mut self.packet_buffer[*i]; if f(frag)? { frag.mark_discarded(); } diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 37e7f860e..42425f798 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -507,7 +507,7 @@ impl<'a> InterfaceInner<'a> { } tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( - &buffer[*frag_offset as usize + repr.buffer_len() as usize..][..payload_len], + &buffer[*frag_offset as usize + repr.buffer_len()..][..payload_len], ); // Update the frag offset for the next fragment. diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 242de2702..034863918 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -129,7 +129,7 @@ impl Default for RttEstimator { impl RttEstimator { fn retransmission_timeout(&self) -> Duration { let margin = RTTE_MIN_MARGIN.max(self.deviation * 4); - let ms = (self.rtt + margin).max(RTTE_MIN_RTO).min(RTTE_MAX_RTO); + let ms = (self.rtt + margin).clamp(RTTE_MIN_RTO, RTTE_MAX_RTO); Duration::from_millis(ms as u64) } @@ -1442,7 +1442,7 @@ impl<'a> Socket<'a> { if segment_in_window { // We've checked that segment_start >= window_start above. - payload_offset = (segment_start - window_start) as usize; + payload_offset = segment_start - window_start; self.local_rx_last_seq = Some(repr.seq_number); } else { // If we're in the TIME-WAIT state, restart the TIME-WAIT timeout, since diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index bd3246c2f..57355c718 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -555,7 +555,7 @@ impl<'a> Repr<'a> { { let ip_packet = Ipv6Packet::new_checked(packet.payload())?; - let payload = &packet.payload()[ip_packet.header_len() as usize..]; + let payload = &packet.payload()[ip_packet.header_len()..]; if payload.len() < 8 { return Err(Error); } diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 72c87150c..3b275f8c2 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -72,7 +72,7 @@ impl> Packet { /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); - if len < field::GROUP_ADDRESS.end as usize { + if len < field::GROUP_ADDRESS.end { Err(Error) } else { Ok(()) diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 434885f70..b350ba822 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -296,14 +296,14 @@ mod test { #[test] fn test_header_len_overflow() { let mut bytes = vec![]; - bytes.extend(&REPR_PACKET_PAD4); + bytes.extend(REPR_PACKET_PAD4); let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); assert_eq!(Header::new_checked(&bytes).unwrap_err(), Error); let mut bytes = vec![]; - bytes.extend(&REPR_PACKET_PAD12); + bytes.extend(REPR_PACKET_PAD12); let len = bytes.len() as u8; Header::new_unchecked(&mut bytes).set_header_len(len + 1); diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index b781b774c..1842c6f26 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -169,7 +169,7 @@ impl> Header { return Err(Error); } - if len < field::DATA(self.header_len()).end as usize { + if len < field::DATA(self.header_len()).end { return Err(Error); } diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 243b00c89..bd0e46dce 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -1215,10 +1215,9 @@ pub mod iphc { let mut len = 0; len += 2; // The minimal header length - len += if self.next_header == NextHeader::Compressed { - 0 // The next header is compressed (we don't need to inline what the next header is) - } else { - 1 // The next header field is inlined + len += match self.next_header { + NextHeader::Compressed => 0, // The next header is compressed (we don't need to inline what the next header is) + NextHeader::Uncompressed(_) => 1, // The next header field is inlined }; // Hop Limit size @@ -1604,10 +1603,10 @@ pub mod nhc { /// Return the size of the Next Header field. fn next_header_size(&self) -> usize { // If nh is set, then the Next Header is compressed using LOWPAN_NHC - if self.nh_field() == 1 { - 0 - } else { - 1 + match self.nh_field() { + 0 => 1, + 1 => 0, + _ => unreachable!(), } } } From c53e6682fb8615beed7c586b3e54e8210dcc8672 Mon Sep 17 00:00:00 2001 From: Gopa Kumar Date: Sat, 29 Oct 2022 17:05:58 -0400 Subject: [PATCH 437/566] Fix medium-ip not compiling complaining of needing EthernetAddress --- .github/workflows/test.yml | 1 + src/iface/interface/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a18cb9de9..63c46aa5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,6 +41,7 @@ jobs: - std medium-ethernet proto-ipv6 socket-tcp - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp - std medium-ip proto-ipv6 socket-icmp socket-tcp + - std medium-ip proto-ipv4 proto-ipv6 socket-tcp socket-udp # Test features chosen to be as aggressive as possible. - std medium-ethernet medium-ip medium-ieee802154 proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp socket-dns async diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index c5050908b..6e9c0bb16 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -79,7 +79,7 @@ impl<'a> OutPackets<'a> { } #[allow(unused)] -#[cfg(feature = "proto-ipv4")] +#[cfg(feature = "proto-ipv4-fragmentation")] pub(crate) struct Ipv4OutPacket<'a> { /// The buffer that holds the unfragmented 6LoWPAN packet. buffer: ManagedSlice<'a, u8>, From 50842508bbbd24922b9caacd8c5fc2b38197369b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 23 Sep 2022 16:34:51 +0200 Subject: [PATCH 438/566] add icmp tests for 6LoWPAN (also with frags) --- .github/workflows/test.yml | 1 + src/iface/interface/mod.rs | 22 +- src/iface/interface/sixlowpan.rs | 18 +- src/iface/interface/tests.rs | 437 +++++++++++++++++++++++++++++-- src/iface/neighbor.rs | 1 + src/phy/loopback.rs | 3 +- src/wire/ieee802154.rs | 7 + src/wire/ndisc.rs | 1 + src/wire/ndiscoption.rs | 1 + src/wire/sixlowpan.rs | 69 +++-- 10 files changed, 495 insertions(+), 65 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 63c46aa5c..e2ccb7a2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,6 +41,7 @@ jobs: - std medium-ethernet proto-ipv6 socket-tcp - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp - std medium-ip proto-ipv6 socket-icmp socket-tcp + - std medium-ieee802154 proto-sixlowpan proto-sixlowpan-fragmentation socket-udp - std medium-ip proto-ipv4 proto-ipv6 socket-tcp socket-udp # Test features chosen to be as aggressive as possible. diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 6e9c0bb16..87e90e006 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1529,8 +1529,11 @@ impl<'a> InterfaceInner<'a> { caps: DeviceCapabilities { #[cfg(feature = "medium-ethernet")] medium: crate::phy::Medium::Ethernet, - #[cfg(not(feature = "medium-ethernet"))] + #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))] medium: crate::phy::Medium::Ip, + #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] + medium: crate::phy::Medium::Ieee802154, + checksum: crate::phy::ChecksumCapabilities { #[cfg(feature = "proto-ipv4")] icmpv4: crate::phy::Checksum::Both, @@ -1577,10 +1580,17 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: 1, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + #[cfg(feature = "medium-ethernet")] hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), )), + #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] + hardware_addr: Some(crate::wire::HardwareAddress::Ieee802154( + crate::wire::Ieee802154Address::Extended([ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x2, 0x2, + ]), + )), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: None, @@ -2098,13 +2108,7 @@ impl<'a> InterfaceInner<'a> { _ => unreachable!(), }; - return self.dispatch_ieee802154( - dst_hardware_addr, - &ip_repr, - tx_token, - packet, - _out_packet, - ); + return self.dispatch_ieee802154(dst_hardware_addr, tx_token, packet, _out_packet); } // Dispatch IP/Ethernet: diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 70c030999..8d1672203 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -134,7 +134,8 @@ impl<'a> InterfaceInner<'a> { let udp_repr = check!(SixlowpanUdpNhcRepr::parse( &udp_packet, &iphc_repr.src_addr, - &iphc_repr.dst_addr + &iphc_repr.dst_addr, + &self.checksum_caps(), )); self.process_udp( @@ -284,7 +285,6 @@ impl<'a> InterfaceInner<'a> { pub(super) fn dispatch_ieee802154( &mut self, ll_dst_a: Ieee802154Address, - ip_repr: &IpRepr, tx_token: Tx, packet: IpPacket, _out_packet: Option<&mut OutPackets>, @@ -300,6 +300,8 @@ impl<'a> InterfaceInner<'a> { }, )?; + let ip_repr = packet.ip_repr(); + let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), #[allow(unreachable_patterns)] @@ -395,9 +397,15 @@ impl<'a> InterfaceInner<'a> { .. } = &mut _out_packet.unwrap().sixlowpan_out_packet; - if buffer.len() < total_size { - net_debug!("6LoWPAN: Fragmentation buffer is too small"); - return Err(Error::Exhausted); + match buffer { + managed::ManagedSlice::Borrowed(buffer) => { + if buffer.len() < total_size { + net_debug!("6LoWPAN: Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + } + #[cfg(any(feature = "std", feature = "alloc"))] + managed::ManagedSlice::Owned(buffer) => buffer.resize(total_size, 0), } *ll_dst_addr = ll_dst_a; diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 8da4a77fb..1dbbf489e 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -19,14 +19,25 @@ fn fill_slice(s: &mut [u8], val: u8) { } } -fn create<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { - #[cfg(feature = "medium-ethernet")] - return create_ethernet(); - #[cfg(not(feature = "medium-ethernet"))] - return create_ip(); +#[cfg(feature = "medium-ethernet")] +const MEDIUM: Medium = Medium::Ethernet; +#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))] +const MEDIUM: Medium = Medium::Ip; +#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] +const MEDIUM: Medium = Medium::Ieee802154; + +fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { + match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => create_ethernet(), + #[cfg(feature = "medium-ip")] + Medium::Ip => create_ip(), + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => create_ieee802154(), + } } -#[cfg(all(feature = "medium-ip"))] +#[cfg(feature = "medium-ip")] #[allow(unused)] fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device @@ -54,7 +65,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { (iface, SocketSet::new(vec![]), device) } -#[cfg(all(feature = "medium-ethernet"))] +#[cfg(feature = "medium-ethernet")] fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); @@ -89,6 +100,32 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { (iface, SocketSet::new(vec![]), device) } +#[cfg(feature = "medium-ieee802154")] +fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { + // Create a basic device + let mut device = Loopback::new(Medium::Ieee802154); + let ip_addrs = [ + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + + let iface_builder = InterfaceBuilder::new() + .hardware_addr(Ieee802154Address::default().into()) + .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .ip_addrs(ip_addrs); + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + let iface_builder = iface_builder + .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .sixlowpan_fragmentation_buffer(vec![]); + + let iface = iface_builder.finalize(&mut device); + + (iface, SocketSet::new(vec![]), device) +} + #[cfg(feature = "proto-igmp")] fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); @@ -126,7 +163,7 @@ fn test_builder_initialization_panic() { #[test] #[cfg(feature = "proto-ipv4")] fn test_no_icmp_no_unicast_ipv4() { - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); // Unknown Ipv4 Protocol // @@ -165,7 +202,7 @@ fn test_no_icmp_no_unicast_ipv4() { #[test] #[cfg(feature = "proto-ipv6")] fn test_no_icmp_no_unicast_ipv6() { - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); // Unknown Ipv6 Protocol // @@ -194,7 +231,7 @@ fn test_no_icmp_no_unicast_ipv6() { #[cfg(feature = "proto-ipv4")] fn test_icmp_error_no_payload() { static NO_BYTES: [u8; 0] = []; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { @@ -257,7 +294,7 @@ fn test_icmp_error_no_payload() { #[test] #[cfg(feature = "proto-ipv4")] fn test_local_subnet_broadcasts() { - let (mut iface, _, _device) = create(); + let (mut iface, _, _device) = create(MEDIUM); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); @@ -314,7 +351,7 @@ fn test_icmp_error_port_unreachable() { static UDP_PAYLOAD: [u8; 12] = [ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let mut udp_bytes_unicast = vec![0u8; 20]; let mut udp_bytes_broadcast = vec![0u8; 20]; @@ -420,7 +457,7 @@ fn test_handle_udp_broadcast() { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); @@ -502,7 +539,7 @@ fn test_handle_udp_broadcast() { fn test_handle_ipv4_broadcast() { use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let our_ipv4_addr = iface.ipv4_address().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); @@ -585,7 +622,7 @@ fn test_icmp_reply_size() { #[cfg(feature = "proto-ipv6")] const MAX_PAYLOAD_LEN: usize = 1192; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let src_addr = Ipv4Address([192, 168, 1, 1]); @@ -941,7 +978,7 @@ fn test_arp_flush_after_update_ip() { fn test_icmpv4_socket() { use crate::wire::Icmpv4Packet; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); @@ -1012,7 +1049,7 @@ fn test_icmpv4_socket() { #[test] #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { - let (mut iface, _, _device) = create(); + let (mut iface, _, _device) = create(MEDIUM); let mut new_addrs = vec![ IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), @@ -1035,7 +1072,7 @@ fn test_solicited_node_addrs() { #[test] #[cfg(feature = "proto-ipv6")] fn test_icmpv6_nxthdr_unknown() { - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); @@ -1129,7 +1166,7 @@ fn test_handle_igmp() { Ipv4Address::new(224, 0, 0, 56), ]; - let (mut iface, mut sockets, mut device) = create(); + let (mut iface, mut sockets, mut device) = create(MEDIUM); // Join multicast groups let timestamp = Instant::now(); @@ -1199,7 +1236,7 @@ fn test_handle_igmp() { fn test_raw_socket_no_reply() { use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let packets = 1; let rx_buffer = @@ -1276,7 +1313,7 @@ fn test_raw_socket_with_udp_socket() { static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - let (mut iface, mut sockets, _device) = create(); + let (mut iface, mut sockets, _device) = create(MEDIUM); let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); @@ -1367,3 +1404,361 @@ fn test_raw_socket_with_udp_socket() { Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) ); } + +#[cfg(all( + not(feature = "medium-ethernet"), + feature = "proto-sixlowpan", + feature = "proto-sixlowpan-fragmentation" +))] +#[test] +fn test_echo_request_sixlowpan_128_bytes() { + use crate::phy::Checksum; + + let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154); + // TODO: modify the example, such that we can also test if the checksum is correctly + // computed. + iface.inner.caps.checksum.icmpv6 = Checksum::None; + + assert_eq!(iface.inner.caps.medium, Medium::Ieee802154); + let now = iface.inner.now(); + + iface.inner.neighbor_cache.as_mut().unwrap().fill( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0]).into(), + HardwareAddress::Ieee802154(Ieee802154Address::default()), + now, + ); + + let mut ieee802154_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(5), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: Some(Ieee802154Pan(0xbeef)), + dst_addr: Some(Ieee802154Address::Extended([ + 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, + ])), + src_pan_id: Some(Ieee802154Pan(0xbeef)), + src_addr: Some(Ieee802154Address::Extended([ + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, + ])), + }; + + // NOTE: this data is retrieved from tests with Contiki-NG + + let request_first_part_packet = SixlowpanFragPacket::new_checked(&[ + 0xc0, 0xb0, 0x00, 0x8e, 0x6a, 0x33, 0x05, 0x25, 0x2c, 0x3a, 0x80, 0x00, 0xe0, 0x71, 0x00, + 0x27, 0x00, 0x02, 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + ]) + .unwrap(); + + let request_first_part_iphc_packet = + SixlowpanIphcPacket::new_checked(request_first_part_packet.payload()).unwrap(); + + let request_first_part_iphc_repr = SixlowpanIphcRepr::parse( + &request_first_part_iphc_packet, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + iface.inner.sixlowpan_address_context, + ) + .unwrap(); + + assert_eq!( + request_first_part_iphc_repr.src_addr, + Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, + 0x1a, + ]), + ); + assert_eq!( + request_first_part_iphc_repr.dst_addr, + Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, + 0x76, + ]), + ); + + let request_second_part = [ + 0xe0, 0xb0, 0x00, 0x8e, 0x10, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + &ieee802154_repr, + &request_first_part_packet.into_inner(), + Some(( + &mut iface.fragments.sixlowpan_fragments, + iface.fragments.sixlowpan_fragments_cache_timeout, + )), + ), + None + ); + + ieee802154_repr.sequence_number = Some(6); + + // data that was generated when using `ping -s 128` + let data = &[ + 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ]; + + let result = iface.inner.process_sixlowpan( + &mut sockets, + &ieee802154_repr, + &request_second_part, + Some(( + &mut iface.fragments.sixlowpan_fragments, + iface.fragments.sixlowpan_fragments_cache_timeout, + )), + ); + + assert_eq!( + result, + Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, + 0xfc, 0x76, + ]), + dst_addr: Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, + 0xb, 0x1a, + ]), + next_header: IpProtocol::Icmpv6, + payload_len: 136, + hop_limit: 64, + }, + Icmpv6Repr::EchoReply { + ident: 39, + seq_no: 2, + data, + } + ))) + ); + + iface.inner.neighbor_cache.as_mut().unwrap().fill( + IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, + ])), + HardwareAddress::Ieee802154(Ieee802154Address::default()), + Instant::now(), + ); + + let tx_token = device.transmit().unwrap(); + iface + .inner + .dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + result.unwrap(), + Some(&mut iface.out_packets), + ) + .unwrap(); + + assert_eq!( + device.queue[0], + &[ + 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb0, 0x5, 0x4e, 0x7a, 0x11, 0x3a, 0x92, 0xfc, 0x48, 0xc2, + 0xa4, 0x41, 0xfc, 0x76, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, 0x81, 0x0, 0x0, + 0x0, 0x0, 0x27, 0x0, 0x2, 0xa2, 0xc2, 0x2d, 0x63, 0x0, 0x0, 0x0, 0x0, 0xd9, 0x5e, 0xc, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + ] + ); + + iface.poll(Instant::now(), &mut device, &mut sockets); + + assert_eq!( + device.queue[1], + &[ + 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb0, 0x5, 0x4e, 0xf, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ] + ); +} + +#[cfg(all( + not(feature = "medium-ethernet"), + feature = "proto-sixlowpan", + feature = "proto-sixlowpan-fragmentation" +))] +#[test] +fn test_sixlowpan_udp_with_fragmentation() { + use crate::phy::Checksum; + + let mut ieee802154_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(5), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: Some(Ieee802154Pan(0xbeef)), + dst_addr: Some(Ieee802154Address::Extended([ + 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, + ])), + src_pan_id: Some(Ieee802154Pan(0xbeef)), + src_addr: Some(Ieee802154Address::Extended([ + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, + ])), + }; + + let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154); + iface.inner.caps.checksum.udp = Checksum::None; + + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + let udp_socket_handle = sockets.add(udp_socket); + + { + let socket = sockets.get_mut::(udp_socket_handle); + assert_eq!(socket.bind(6969), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + } + + let udp_first_part = &[ + 0xc0, 0xbc, 0x00, 0x92, 0x6e, 0x33, 0x07, 0xe7, 0xdc, 0xf0, 0xd3, 0xc9, 0x1b, 0x39, 0xbf, + 0xa0, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, + 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, + 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x49, 0x6e, + 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, 0x73, 0x20, 0x74, 0x6f, 0x72, + 0x74, 0x6f, 0x72, 0x2e, 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + &ieee802154_repr, + udp_first_part, + Some(( + &mut iface.fragments.sixlowpan_fragments, + iface.fragments.sixlowpan_fragments_cache_timeout + )) + ), + None + ); + + ieee802154_repr.sequence_number = Some(6); + + let udp_second_part = &[ + 0xe0, 0xbc, 0x00, 0x92, 0x11, 0x64, 0x69, 0x74, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, + 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, + 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + &ieee802154_repr, + udp_second_part, + Some(( + &mut iface.fragments.sixlowpan_fragments, + iface.fragments.sixlowpan_fragments_cache_timeout + )) + ), + None + ); + + let socket = sockets.get_mut::(udp_socket_handle); + + let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec."; + assert_eq!( + socket.recv(), + Ok(( + &udp_data[..], + IpEndpoint { + addr: IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, + 0xb, 0x1a, + ])), + port: 54217, + } + )) + ); + + let tx_token = device.transmit().unwrap(); + iface + .inner + .dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + IpPacket::Udp(( + IpRepr::Ipv6(Ipv6Repr { + src_addr: Ipv6Address::default(), + dst_addr: Ipv6Address::default(), + next_header: IpProtocol::Udp, + payload_len: udp_data.len(), + hop_limit: 64, + }), + UdpRepr { + src_port: 1234, + dst_port: 1234, + }, + udp_data, + )), + Some(&mut iface.out_packets), + ) + .unwrap(); + + iface.poll(Instant::now(), &mut device, &mut sockets); + + assert_eq!( + device.queue[0], + &[ + 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb4, 0x5, 0x4e, 0x7e, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x4, 0xd2, 0x4, 0xd2, 0xf6, + 0x4d, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, + 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, + 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, + 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, + 0x73, 0x20, 0x74, + ] + ); + + assert_eq!( + device.queue[1], + &[ + 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb4, 0x5, 0x4e, 0xf, 0x6f, 0x72, 0x74, 0x6f, 0x72, 0x2e, + 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, 0x64, 0x69, 0x74, 0x20, + 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, + 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, + 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, + 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, + ] + ); +} diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 2b3a1f918..2c8af7321 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -224,6 +224,7 @@ impl<'a> Cache<'a> { } } +#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] #[cfg(test)] mod test { use super::*; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index e6a317828..7b3b57124 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -11,7 +11,7 @@ use crate::Result; /// A loopback device. #[derive(Debug)] pub struct Loopback { - queue: VecDeque>, + pub(crate) queue: VecDeque>, medium: Medium, } @@ -73,6 +73,7 @@ impl phy::RxToken for RxToken { } #[doc(hidden)] +#[derive(Debug)] pub struct TxToken<'a> { queue: &'a mut VecDeque>, } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index b85a18b19..ac037a55b 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -89,6 +89,13 @@ pub enum Address { Extended([u8; 8]), } +#[cfg(test)] +impl Default for Address { + fn default() -> Self { + Address::Extended([0u8; 8]) + } +} + impl Address { /// The broadcast address. pub const BROADCAST: Address = Address::Short([0xff; 2]); diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index a6a99a1b1..ad4839f5b 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -514,6 +514,7 @@ impl<'a> Repr<'a> { } } +#[cfg(feature = "medium-ethernet")] #[cfg(test)] mod test { use super::*; diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index a990b243d..f56d3bb2e 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -628,6 +628,7 @@ impl> PrettyPrint for NdiscOption { } } +#[cfg(feature = "medium-ethernet")] #[cfg(test)] mod test { use super::Error; diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index bd0e46dce..2407730bb 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -227,7 +227,8 @@ pub mod frag { use byteorder::{ByteOrder, NetworkEndian}; /// Key used for identifying all the link fragments that belong to the same packet. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Key { ll_src_addr: Ieee802154Address, ll_dst_addr: Ieee802154Address, @@ -1412,11 +1413,14 @@ pub mod nhc { //! //! [RFC 6282 § 4]: https://datatracker.ietf.org/doc/html/rfc6282#section-4 use super::{Error, NextHeader, Result, DISPATCH_EXT_HEADER, DISPATCH_UDP_HEADER}; - use crate::wire::{ - ip::{checksum, Address as IpAddress}, - ipv6, - udp::Repr as UdpRepr, - IpProtocol, + use crate::{ + phy::ChecksumCapabilities, + wire::{ + ip::{checksum, Address as IpAddress}, + ipv6, + udp::Repr as UdpRepr, + IpProtocol, + }, }; use byteorder::{ByteOrder, NetworkEndian}; use ipv6::Address; @@ -1967,6 +1971,7 @@ pub mod nhc { packet: &UdpNhcPacket<&'a T>, src_addr: &ipv6::Address, dst_addr: &ipv6::Address, + checksum_caps: &ChecksumCapabilities, ) -> Result { packet.check_len()?; @@ -1974,28 +1979,27 @@ pub mod nhc { return Err(Error); } - let payload_len = packet.payload().len(); - let chk_sum = !checksum::combine(&[ - checksum::pseudo_header( - &IpAddress::Ipv6(*src_addr), - &IpAddress::Ipv6(*dst_addr), - crate::wire::ip::Protocol::Udp, - payload_len as u32 + 8, - ), - packet.src_port(), - packet.dst_port(), - payload_len as u16 + 8, - checksum::data(packet.payload()), - ]); - - if let Some(checksum) = packet.checksum() { - if chk_sum != checksum { - return Err(Error); + if checksum_caps.udp.rx() { + let payload_len = packet.payload().len(); + let chk_sum = !checksum::combine(&[ + checksum::pseudo_header( + &IpAddress::Ipv6(*src_addr), + &IpAddress::Ipv6(*dst_addr), + crate::wire::ip::Protocol::Udp, + payload_len as u32 + 8, + ), + packet.src_port(), + packet.dst_port(), + payload_len as u16 + 8, + checksum::data(packet.payload()), + ]); + + if let Some(checksum) = packet.checksum() { + if chk_sum != checksum { + return Err(Error); + } } - } else { - net_trace!("Currently we do not support elided checksums."); - return Err(Error); - }; + } Ok(Self(UdpRepr { src_port: packet.src_port(), @@ -2135,6 +2139,8 @@ pub mod nhc { #[cfg(test)] mod test { + use crate::phy::ChecksumCapabilities; + use super::*; #[test] @@ -2344,8 +2350,13 @@ mod test { let payload = udp_hdr.payload(); assert_eq!(String::from_utf8_lossy(payload), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. \n"); - let udp_repr = - nhc::UdpNhcRepr::parse(&udp_hdr, &iphc_repr.src_addr, &iphc_repr.dst_addr).unwrap(); + let udp_repr = nhc::UdpNhcRepr::parse( + &udp_hdr, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + &ChecksumCapabilities::default(), + ) + .unwrap(); assert_eq!(udp_repr.src_port, 53855); assert_eq!(udp_repr.dst_port, 6969); From 3057e735db7eb1b88b0d3980fe2dbd0b79660f2e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Nov 2022 21:08:27 +0100 Subject: [PATCH 439/566] Add changelog for v0.8.2 --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d12c4c73b..dfae7e77b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix enable `defmt/alloc` if `alloc` or `std` is enabled. - Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.65 +## [0.8.2] - 2022-11-27 + +- tcp: Fix return value of nagle_enable ([#642](https://github.com/smoltcp-rs/smoltcp/pull/642)) +- tcp: Only clear retransmit timer when all packets are acked ([#662](https://github.com/smoltcp-rs/smoltcp/pull/662)) +- tcp: Send incomplete fin packets even if nagle enabled ([#665](https://github.com/smoltcp-rs/smoltcp/pull/665)) +- phy: Fix mtu calculation for raw_socket ([#611](https://github.com/smoltcp-rs/smoltcp/pull/611)) +- wire: Fix ipv6 contains_addr function ([#605](https://github.com/smoltcp-rs/smoltcp/pull/605)) + ## [0.8.1] - 2022-05-12 - Remove unused `rand_core` dep. ([#589](https://github.com/smoltcp-rs/smoltcp/pull/589)) @@ -144,7 +152,8 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Use #[non_exhaustive] for enums and structs ([409](https://github.com/smoltcp-rs/smoltcp/pull/409), [411](https://github.com/smoltcp-rs/smoltcp/pull/411)) - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) -[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD +[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.2...HEAD +[0.8.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.8.0 [0.7.5]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.4...v0.7.5 From 160ceb91941a1d70c13c6c4b1bfc03b85b39be27 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Nov 2022 00:45:10 +0100 Subject: [PATCH 440/566] Make `std` imply `alloc` feature. This allows simplifying all cfg's to just check alloc. There should be no downside to using `extern crate alloc` in targets with `std`, since std support implies alloc support. --- Cargo.toml | 2 +- src/iface/fragmentation.rs | 14 +++++++------- src/iface/interface/sixlowpan.rs | 2 +- src/iface/neighbor.rs | 14 +++++++------- src/iface/socket_set.rs | 2 +- src/lib.rs | 2 +- src/phy/mod.rs | 4 ++-- src/socket/dns.rs | 2 +- src/storage/assembler.rs | 20 +++++++++----------- 9 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 29432b64d..e2330ab21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ rand = "0.8" url = "2.0" [features] -std = ["managed/std", "defmt?/alloc"] +std = ["managed/std", "alloc"] alloc = ["managed/alloc", "defmt?/alloc"] verbose = [] defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 8796be45a..b9f15baf3 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -84,7 +84,7 @@ impl<'a> PacketAssembler<'a> { } } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(b) => { if let Some(total_size) = total_size { b.resize(total_size, 0); @@ -164,7 +164,7 @@ impl<'a> PacketAssembler<'a> { return Err(Error::PacketAssemblerBufferTooSmall); } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(b) => { if offset + data.len() > b.len() { b.resize(offset + data.len(), 0); @@ -287,19 +287,19 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { panic!("The amount of places in the index buffer must be the same as the amount of possible fragments assemblers."); } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] (ManagedSlice::Borrowed(f), ManagedMap::Owned(_)) => { if f.is_empty() { panic!("The packet buffer cannot be empty."); } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] (ManagedSlice::Owned(_), ManagedMap::Borrowed(i)) => { if i.is_empty() { panic!("The index buffer cannot be empty."); } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] (ManagedSlice::Owned(_), ManagedMap::Owned(_)) => (), } @@ -326,7 +326,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { if self.packet_buffer.len() == self.index_buffer.len() { match &mut self.packet_buffer { ManagedSlice::Borrowed(_) => return Err(Error::PacketAssemblerSetFull), - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(b) => (), } } @@ -346,7 +346,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { fn get_free_packet_assembler(&mut self) -> Option { match &mut self.packet_buffer { ManagedSlice::Borrowed(_) => (), - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(b) => b.push(PacketAssembler::new(alloc::vec![])), } diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 8d1672203..485633b3c 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -404,7 +404,7 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Exhausted); } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] managed::ManagedSlice::Owned(buffer) => buffer.resize(total_size, 0), } diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index 2c8af7321..d61fc0e05 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -63,7 +63,7 @@ impl Answer { pub struct Cache<'a> { storage: ManagedMap<'a, IpAddress, Neighbor>, silent_until: Instant, - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] gc_threshold: usize, } @@ -75,7 +75,7 @@ impl<'a> Cache<'a> { pub(crate) const ENTRY_LIFETIME: Duration = Duration::from_millis(60_000); /// Default number of entries in the cache before GC kicks in - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] pub(crate) const GC_THRESHOLD: usize = 1024; /// Create a cache. The backing storage is cleared upon creation. @@ -91,13 +91,13 @@ impl<'a> Cache<'a> { Cache { storage, - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] gc_threshold: Self::GC_THRESHOLD, silent_until: Instant::from_millis(0), } } - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] pub fn new_with_limit(storage: T, gc_threshold: usize) -> Cache<'a> where T: Into>, @@ -121,12 +121,12 @@ impl<'a> Cache<'a> { debug_assert!(protocol_addr.is_unicast()); debug_assert!(hardware_addr.is_unicast()); - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] let current_storage_size = self.storage.len(); match self.storage { ManagedMap::Borrowed(_) => (), - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedMap::Owned(ref mut map) => { if current_storage_size >= self.gc_threshold { let new_btree_map = map @@ -173,7 +173,7 @@ impl<'a> Cache<'a> { .0 } // Owned maps can extend themselves. - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedMap::Owned(_) => unreachable!(), }; diff --git a/src/iface/socket_set.rs b/src/iface/socket_set.rs index ce52f5bb6..1611d579a 100644 --- a/src/iface/socket_set.rs +++ b/src/iface/socket_set.rs @@ -79,7 +79,7 @@ impl<'a> SocketSet<'a> { match self.sockets { ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"), - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(ref mut sockets) => { sockets.push(SocketStorage { inner: None }); let index = sockets.len() - 1; diff --git a/src/lib.rs b/src/lib.rs index 74ff6bba7..78f1f82ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::option_map_unit_fn)] #![allow(clippy::unit_arg)] -#[cfg(any(feature = "std", feature = "alloc"))] +#[cfg(feature = "alloc")] extern crate alloc; #[cfg(not(any( diff --git a/src/phy/mod.rs b/src/phy/mod.rs index d95d02ff3..54b109da8 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -100,7 +100,7 @@ mod sys; mod fault_injector; mod fuzz_injector; -#[cfg(any(feature = "std", feature = "alloc"))] +#[cfg(feature = "alloc")] mod loopback; mod pcap_writer; #[cfg(all(feature = "phy-raw_socket", unix))] @@ -120,7 +120,7 @@ pub use self::sys::wait; pub use self::fault_injector::FaultInjector; pub use self::fuzz_injector::{FuzzInjector, Fuzzer}; -#[cfg(any(feature = "std", feature = "alloc"))] +#[cfg(feature = "alloc")] pub use self::loopback::Loopback; pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; #[cfg(all(feature = "phy-raw_socket", unix))] diff --git a/src/socket/dns.rs b/src/socket/dns.rs index a287d0b11..0a5c36a68 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -184,7 +184,7 @@ impl<'a> Socket<'a> { match self.queries { ManagedSlice::Borrowed(_) => None, - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] ManagedSlice::Owned(ref mut queries) => { queries.push(None); let index = queries.len() - 1; diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 4770a246e..df36a7496 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -81,14 +81,12 @@ impl Contig { } } -#[cfg(all(feature = "alloc", not(feature = "std")))] +#[cfg(feature = "alloc")] use alloc::boxed::Box; -#[cfg(feature = "std")] -use std::boxed::Box; -#[cfg(any(feature = "std", feature = "alloc"))] +#[cfg(feature = "alloc")] const CONTIG_COUNT: usize = 32; -#[cfg(not(any(feature = "std", feature = "alloc")))] +#[cfg(not(feature = "alloc"))] const CONTIG_COUNT: usize = 4; /// A buffer (re)assembler. @@ -97,9 +95,9 @@ const CONTIG_COUNT: usize = 4; #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Assembler { - #[cfg(not(any(feature = "std", feature = "alloc")))] + #[cfg(not(feature = "alloc"))] contigs: [Contig; CONTIG_COUNT], - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] contigs: Box<[Contig; CONTIG_COUNT]>, } @@ -120,9 +118,9 @@ impl fmt::Display for Assembler { impl Assembler { /// Create a new buffer assembler for buffers of the given size. pub fn new(size: usize) -> Assembler { - #[cfg(not(any(feature = "std", feature = "alloc")))] + #[cfg(not(feature = "alloc"))] let mut contigs = [Contig::empty(); CONTIG_COUNT]; - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]); contigs[0] = Contig::hole(size); Assembler { contigs } @@ -328,9 +326,9 @@ mod test { impl From> for Assembler { fn from(vec: Vec<(usize, usize)>) -> Assembler { - #[cfg(not(any(feature = "std", feature = "alloc")))] + #[cfg(not(feature = "alloc"))] let mut contigs = [Contig::empty(); CONTIG_COUNT]; - #[cfg(any(feature = "std", feature = "alloc"))] + #[cfg(feature = "alloc")] let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]); for (i, &(hole_size, data_size)) in vec.iter().enumerate() { contigs[i] = Contig { From ef0d413a941ede945193c6f80f3084c30a0771cd Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Mon, 28 Nov 2022 20:21:38 +0300 Subject: [PATCH 441/566] Add printing of the needed size of fragmentation buffer. --- src/iface/interface/mod.rs | 5 ++++- src/iface/interface/sixlowpan.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 87e90e006..50e635e80 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -2196,7 +2196,10 @@ impl<'a> InterfaceInner<'a> { let first_frag_ip_len = self.caps.ip_mtu(); if buffer.len() < first_frag_ip_len { - net_debug!("Fragmentation buffer is too small"); + net_debug!( + "Fragmentation buffer is too small, at least {} needed", + first_frag_ip_len + ); return Err(Error::Exhausted); } diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 485633b3c..d5dce356a 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -400,7 +400,10 @@ impl<'a> InterfaceInner<'a> { match buffer { managed::ManagedSlice::Borrowed(buffer) => { if buffer.len() < total_size { - net_debug!("6LoWPAN: Fragmentation buffer is too small"); + net_debug!( + "6LoWPAN: Fragmentation buffer is too small, at least {} needed", + total_size + ); return Err(Error::Exhausted); } } From d235b3a16429ed0bc3749a2e841022d7442376f7 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 29 Nov 2022 15:25:33 +0100 Subject: [PATCH 442/566] add more tests in CI --- .github/workflows/test.yml | 2 ++ src/iface/interface/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e2ccb7a2d..2f87a5ccc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,6 +34,7 @@ jobs: # Test features chosen to be as orthogonal as possible. - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp socket-dns - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp + - std medium-ethernet proto-ipv4 proto-ipv4-fragmentation socket-raw socket-dns - std medium-ethernet proto-ipv4 proto-igmp socket-raw socket-dns - std medium-ethernet proto-ipv4 socket-udp socket-tcp socket-dns - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp @@ -41,6 +42,7 @@ jobs: - std medium-ethernet proto-ipv6 socket-tcp - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp - std medium-ip proto-ipv6 socket-icmp socket-tcp + - std medium-ieee802154 proto-sixlowpan socket-udp - std medium-ieee802154 proto-sixlowpan proto-sixlowpan-fragmentation socket-udp - std medium-ip proto-ipv4 proto-ipv6 socket-tcp socket-udp diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 50e635e80..33a2c5fff 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -319,7 +319,7 @@ impl<'a> InterfaceBuilder<'a> { ``` # use std::collections::BTreeMap; #[cfg(feature = "proto-ipv4-fragmentation")] -use smoltcp::iface::FragmentsCache; +use smoltcp::iface::ReassemblyBuffer; use smoltcp::iface::{InterfaceBuilder, NeighborCache}; # use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; @@ -332,7 +332,7 @@ let neighbor_cache = // ... # NeighborCache::new(BTreeMap::new()); # #[cfg(feature = "proto-ipv4-fragmentation")] # let ipv4_frag_cache = // ... -# FragmentsCache::new(vec![], BTreeMap::new()); +# ReassemblyBuffer::new(vec![], BTreeMap::new()); let ip_addrs = // ... # []; let builder = InterfaceBuilder::new() From 6fbde4f7ecaf2a9f0ebf230b266ee28af199c21c Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Mon, 28 Nov 2022 20:19:23 +0300 Subject: [PATCH 443/566] Fix panic when using IPv4 fragmentation with Layer 3 networks. --- src/iface/interface/ipv4.rs | 1 + src/iface/interface/mod.rs | 35 ++++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 42425f798..e4e7d044d 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -466,6 +466,7 @@ impl<'a> InterfaceInner<'a> { } // Emit function for the Ethernet header. + #[cfg(feature = "medium-ethernet")] let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { let mut frame = EthernetFrame::new_unchecked(tx_buffer); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 33a2c5fff..af4fa0153 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -91,6 +91,7 @@ pub(crate) struct Ipv4OutPacket<'a> { /// The IPv4 representation. repr: Ipv4Repr, /// The destination hardware address. + #[cfg(feature = "medium-ethernet")] dst_hardware_addr: EthernetAddress, /// The offset of the next fragment. frag_offset: u16, @@ -112,6 +113,7 @@ impl<'a> Ipv4OutPacket<'a> { payload_len: 0, hop_limit: 0, }, + #[cfg(feature = "medium-ethernet")] dst_hardware_addr: EthernetAddress::default(), frag_offset: 0, ident: 0, @@ -141,7 +143,10 @@ impl<'a> Ipv4OutPacket<'a> { payload_len: 0, hop_limit: 0, }; - self.dst_hardware_addr = EthernetAddress::default(); + #[cfg(feature = "medium-ethernet")] + { + self.dst_hardware_addr = EthernetAddress::default(); + } } } @@ -2129,12 +2134,20 @@ impl<'a> InterfaceInner<'a> { // If the medium is Ethernet, then we need to retrieve the destination hardware address. #[cfg(feature = "medium-ethernet")] - let (dst_hardware_addr, tx_token) = - match self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())? { - (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - #[cfg(feature = "medium-ieee802154")] - (HardwareAddress::Ieee802154(_), _) => unreachable!(), - }; + let (dst_hardware_addr, tx_token) = match self.caps.medium { + Medium::Ethernet => { + match self.lookup_hardware_addr( + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )? { + (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), + #[cfg(feature = "medium-ieee802154")] + (HardwareAddress::Ieee802154(_), _) => unreachable!(), + } + } + _ => (EthernetAddress([0; 6]), tx_token), + }; // Emit function for the Ethernet header. #[cfg(feature = "medium-ethernet")] @@ -2186,7 +2199,8 @@ impl<'a> InterfaceInner<'a> { repr: out_packet_repr, frag_offset, ident, - dst_hardware_addr: dst_address, + #[cfg(feature = "medium-ethernet")] + dst_hardware_addr: dst_address, } = &mut _out_packet.unwrap().ipv4_out_packet; // Calculate how much we will send now (including the Ethernet header). @@ -2203,7 +2217,10 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Exhausted); } - *dst_address = dst_hardware_addr; + #[cfg(feature = "medium-ethernet")] + { + *dst_address = dst_hardware_addr; + } // Save the total packet len (without the Ethernet header, but with the first // IP header). From 5206a8710e7c6e3d819eb235a5df5ccb8c4c7428 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Wed, 7 Dec 2022 11:13:06 -0500 Subject: [PATCH 444/566] Fix PacketBuffer contig_window check when empty --- src/storage/packet_buffer.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 287ab78ee..1f748af8e 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -82,6 +82,12 @@ impl<'a, H> PacketBuffer<'a, H> { return Err(Full); } + // Ring is currently empty. Clear it (resetting `read_at`) to maximize + // for contiguous space. + if self.payload_ring.is_empty() { + self.payload_ring.clear(); + } + let window = self.payload_ring.window(); let contig_window = self.payload_ring.contiguous_window(); @@ -373,6 +379,14 @@ mod test { assert_eq!(buffer.metadata_ring.len(), 1); } + #[test] + fn test_contiguous_window_wrap() { + let mut buffer = buffer(); + assert!(buffer.enqueue(15, ()).is_ok()); + assert!(buffer.dequeue().is_ok()); + assert!(buffer.enqueue(16, ()).is_ok()); + } + #[test] fn test_capacity_too_small() { let mut buffer = buffer(); From 0fe0ec8ef0c3a209629df5c443da353378496a49 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 13 Dec 2022 13:38:44 +0800 Subject: [PATCH 445/566] fix reversed udp server example in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61de6ca0f..faede60cf 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,7 @@ It responds to: * pings (`ping 192.168.69.1`); * UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`), - where it will respond "hello" to any incoming packet; + where it will respond with reversed chunks of the input indefinitely; * TCP connections on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969`), where it will respond "hello" to any incoming connection and immediately close it; * TCP connections on port 6970 (`socat stdio tcp4-connect:192.168.69.1:6970 <<<"abcdefg"`), From 24af392a25ede49b74f4760d7daf55fdd8551e79 Mon Sep 17 00:00:00 2001 From: Klim Tsoutsman Date: Sat, 17 Dec 2022 01:26:37 +1100 Subject: [PATCH 446/566] Make `AnySocket` object safe Signed-off-by: Klim Tsoutsman --- src/socket/mod.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 82de926a4..5c416f685 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -91,10 +91,16 @@ impl<'a> Socket<'a> { } /// A conversion trait for network sockets. -pub trait AnySocket<'a>: Sized { - fn upcast(self) -> Socket<'a>; - fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self>; - fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; +pub trait AnySocket<'a> { + fn upcast(self) -> Socket<'a> + where + Self: Sized; + fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self> + where + Self: Sized; + fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> + where + Self: Sized; } macro_rules! from_socket { From 8cf7625cc2e1fe2feb9ade4bbdbae9173381e49a Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 20 Dec 2022 12:02:10 +0000 Subject: [PATCH 447/566] ip_addrs field of an interface is now a heapless::Vec (one test still failing) --- examples/benchmark.rs | 3 +- examples/client.rs | 3 +- examples/dhcp_client.rs | 3 +- examples/dns.rs | 9 +++--- examples/httpclient.rs | 9 +++--- examples/loopback.rs | 3 +- examples/multicast.rs | 4 ++- examples/ping.rs | 9 +++--- examples/server.rs | 9 +++--- examples/sixlowpan.rs | 5 ++-- examples/sixlowpan_benchmark.rs | 5 ++-- src/iface/interface/mod.rs | 17 ++++++----- src/iface/interface/tests.rs | 50 +++++++++++++++------------------ 13 files changed, 66 insertions(+), 63 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index f3b1912ea..98a5ab266 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -93,7 +93,8 @@ fn main() { let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); if medium == Medium::Ethernet { diff --git a/examples/client.rs b/examples/client.rs index 381243cd5..19c961ad1 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -42,7 +42,8 @@ fn main() { let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)).unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 9c2851982..71906ef47 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -30,7 +30,8 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)).unwrap(); let mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); diff --git a/examples/dns.rs b/examples/dns.rs index ea86e402d..54272889f 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -43,11 +43,10 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let ip_addrs = [ - IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(src_ipv6, 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 8e2e407f2..3f1f1f737 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -36,11 +36,10 @@ fn main() { let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let ip_addrs = [ - IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/loopback.rs b/examples/loopback.rs index 1251a25b5..a8de6fa78 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -85,7 +85,8 @@ fn main() { let mut neighbor_cache_entries = [None; 8]; let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); - let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); let mut iface = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) diff --git a/examples/multicast.rs b/examples/multicast.rs index 6089a0c70..2ae6f782b 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -34,11 +34,13 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(ip_addr).unwrap(); let mut ipv4_multicast_storage = [None; 1]; let mut iface = InterfaceBuilder::new() .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) - .ip_addrs([ip_addr]) + .ip_addrs(ip_addrs) .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) .finalize(&mut device); diff --git a/examples/ping.rs b/examples/ping.rs index 3c654dd3c..105caaba8 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -116,11 +116,10 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let ip_addrs = [ - IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(src_ipv6, 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/server.rs b/examples/server.rs index 3a1b623d9..8a28bc055 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -58,11 +58,10 @@ fn main() { let tcp4_socket = tcp::Socket::new(tcp4_rx_buffer, tcp4_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let ip_addrs = [ - IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), - IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 369b733f4..61ad58b01 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -81,10 +81,11 @@ fn main() { let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); - let ip_addrs = [IpCidr::new( + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new( IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), 64, - )]; + )).unwrap(); let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 5b91e73da..9eb3e61fc 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -161,10 +161,11 @@ fn main() { let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); - let ip_addrs = [IpCidr::new( + let mut ip_addrs = heapless::Vec::::new(); + ip_addrs.push(IpCidr::new( IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), 64, - )]; + )).unwrap(); let cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index af4fa0153..f21d33e88 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -16,6 +16,7 @@ mod ipv4; mod ipv6; use core::cmp; +use heapless::Vec; use managed::{ManagedMap, ManagedSlice}; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] @@ -33,6 +34,8 @@ use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; +const MAX_IP_ADDRS_NUM: usize = 4; + pub(crate) struct FragmentsBuffer<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] pub(crate) ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, @@ -269,7 +272,7 @@ pub struct InterfaceInner<'a> { sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, - ip_addrs: ManagedSlice<'a, IpCidr>, + ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes<'a>, @@ -288,7 +291,7 @@ pub struct InterfaceBuilder<'a> { neighbor_cache: Option>, #[cfg(feature = "medium-ieee802154")] pan_id: Option, - ip_addrs: ManagedSlice<'a, IpCidr>, + ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes<'a>, @@ -365,7 +368,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "medium-ieee802154")] pan_id: None, - ip_addrs: ManagedSlice::Borrowed(&mut []), + ip_addrs: Vec::new(), #[cfg(feature = "proto-ipv4")] any_ip: false, routes: Routes::new(ManagedMap::Borrowed(&mut [])), @@ -433,7 +436,7 @@ let iface = builder.finalize(&mut device); /// [ip_addrs]: struct.Interface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self where - T: Into>, + T: Into>, { let ip_addrs = ip_addrs.into(); InterfaceInner::check_ip_addrs(&ip_addrs); @@ -1003,7 +1006,7 @@ impl<'a> Interface<'a> { /// /// # Panics /// This function panics if any of the addresses are not unicast. - pub fn update_ip_addrs)>(&mut self, f: F) { + pub fn update_ip_addrs)>(&mut self, f: F) { f(&mut self.inner.ip_addrs); InterfaceInner::flush_cache(&mut self.inner); InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) @@ -1556,7 +1559,7 @@ impl<'a> InterfaceInner<'a> { }, now: Instant::from_millis_const(0), - ip_addrs: ManagedSlice::Owned(vec![ + ip_addrs: Vec::from_slice(&vec![ #[cfg(feature = "proto-ipv4")] IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)), #[cfg(feature = "proto-ipv6")] @@ -1564,7 +1567,7 @@ impl<'a> InterfaceInner<'a> { Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), 64, )), - ]), + ]).unwrap(), rand: Rand::new(1234), routes: Routes::new(&mut [][..]), diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 1dbbf489e..3814f080d 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -42,14 +42,13 @@ fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); - let ip_addrs = [ - #[cfg(feature = "proto-ipv4")] - IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + #[cfg(feature = "proto-ipv4")] + ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); @@ -69,14 +68,13 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); - let ip_addrs = [ - #[cfg(feature = "proto-ipv4")] - IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + #[cfg(feature = "proto-ipv4")] + ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) @@ -104,12 +102,11 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); - let ip_addrs = [ - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), - #[cfg(feature = "proto-ipv6")] - IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), - ]; + let mut ip_addrs = heapless::Vec::::new(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); let iface_builder = InterfaceBuilder::new() .hardware_addr(Ieee802154Address::default().into()) @@ -1050,13 +1047,12 @@ fn test_icmpv4_socket() { #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { let (mut iface, _, _device) = create(MEDIUM); - let mut new_addrs = vec![ - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64), - IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64), - ]; + let mut new_addrs = heapless::Vec::::new(); + new_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)).unwrap(); + new_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64)).unwrap(); iface.update_ip_addrs(|addrs| { new_addrs.extend(addrs.to_vec()); - *addrs = From::from(new_addrs); + *addrs = new_addrs; }); assert!(iface .inner From aec718a08fd96f24b0d95a5e429d0dc5eadc29e1 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 20 Dec 2022 13:21:28 +0000 Subject: [PATCH 448/566] Fix clippy and rustfmt errors --- examples/benchmark.rs | 4 ++- examples/client.rs | 4 ++- examples/dhcp_client.rs | 4 ++- examples/dns.rs | 8 ++++-- examples/httpclient.rs | 12 ++++++--- examples/loopback.rs | 4 ++- examples/ping.rs | 8 ++++-- examples/server.rs | 12 ++++++--- examples/sixlowpan.rs | 10 +++++--- examples/sixlowpan_benchmark.rs | 10 +++++--- src/iface/interface/mod.rs | 5 ++-- src/iface/interface/tests.rs | 43 +++++++++++++++++++++++++-------- 12 files changed, 90 insertions(+), 34 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 98a5ab266..70a9baf71 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -94,7 +94,9 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); if medium == Medium::Ethernet { diff --git a/examples/client.rs b/examples/client.rs index 19c961ad1..1c7a94d4f 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -43,7 +43,9 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)) + .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 71906ef47..496010704 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -31,7 +31,9 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)).unwrap(); + ip_addrs + .push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) + .unwrap(); let mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); diff --git a/examples/dns.rs b/examples/dns.rs index 54272889f..e7f1bc8a2 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -44,9 +44,13 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 3f1f1f737..c406f6fb6 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -37,9 +37,15 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/loopback.rs b/examples/loopback.rs index a8de6fa78..de2c91692 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -86,7 +86,9 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); let mut iface = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) .neighbor_cache(neighbor_cache) diff --git a/examples/ping.rs b/examples/ping.rs index 105caaba8..85d889e7d 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -117,9 +117,13 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let mut routes_storage = [None; 2]; diff --git a/examples/server.rs b/examples/server.rs index 8a28bc055..0b0e219ff 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -59,9 +59,15 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 61ad58b01..605a9fdca 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -82,10 +82,12 @@ fn main() { 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new( - IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), - 64, - )).unwrap(); + ip_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )) + .unwrap(); let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 9eb3e61fc..54eac8a3b 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -162,10 +162,12 @@ fn main() { 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(IpCidr::new( - IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), - 64, - )).unwrap(); + ip_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )) + .unwrap(); let cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index f21d33e88..5b0972e6f 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1559,7 +1559,7 @@ impl<'a> InterfaceInner<'a> { }, now: Instant::from_millis_const(0), - ip_addrs: Vec::from_slice(&vec![ + ip_addrs: Vec::from_slice(&[ #[cfg(feature = "proto-ipv4")] IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)), #[cfg(feature = "proto-ipv6")] @@ -1567,7 +1567,8 @@ impl<'a> InterfaceInner<'a> { Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), 64, )), - ]).unwrap(), + ]) + .unwrap(), rand: Rand::new(1234), routes: Routes::new(&mut [][..]), diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 3814f080d..499f47d88 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -44,11 +44,17 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ip); let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] - ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); @@ -70,11 +76,17 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ethernet); let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] - ip_addrs.push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) @@ -104,9 +116,13 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ieee802154); let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); #[cfg(feature = "proto-ipv6")] - ip_addrs.push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)).unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); let iface_builder = InterfaceBuilder::new() .hardware_addr(Ieee802154Address::default().into()) @@ -1048,8 +1064,15 @@ fn test_icmpv4_socket() { fn test_solicited_node_addrs() { let (mut iface, _, _device) = create(MEDIUM); let mut new_addrs = heapless::Vec::::new(); - new_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)).unwrap(); - new_addrs.push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), 64)).unwrap(); + new_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) + .unwrap(); + new_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), + 64, + )) + .unwrap(); iface.update_ip_addrs(|addrs| { new_addrs.extend(addrs.to_vec()); *addrs = new_addrs; From eb811cfefd4a6a9cf5568e008cfb0f424fe5c399 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 20 Dec 2022 13:57:28 +0000 Subject: [PATCH 449/566] Use constant for vec size instead of hardcoded usize --- src/iface/interface/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 499f47d88..92922be64 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -42,7 +42,7 @@ fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) @@ -74,7 +74,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) @@ -114,7 +114,7 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv6")] ip_addrs .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) @@ -1063,7 +1063,7 @@ fn test_icmpv4_socket() { #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { let (mut iface, _, _device) = create(MEDIUM); - let mut new_addrs = heapless::Vec::::new(); + let mut new_addrs = heapless::Vec::::new(); new_addrs .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) .unwrap(); From 4ddb96b1b2983066b9477e74d823a6ca8f7744fe Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 20 Dec 2022 14:04:24 +0000 Subject: [PATCH 450/566] Increased max number of IPs from 4 to 5 because some test use 5 --- examples/benchmark.rs | 2 +- examples/client.rs | 2 +- examples/dhcp_client.rs | 2 +- examples/dns.rs | 2 +- examples/httpclient.rs | 2 +- examples/loopback.rs | 2 +- examples/multicast.rs | 2 +- examples/ping.rs | 2 +- examples/server.rs | 2 +- examples/sixlowpan.rs | 2 +- examples/sixlowpan_benchmark.rs | 2 +- src/iface/interface/mod.rs | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 70a9baf71..a6b6cab5d 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -93,7 +93,7 @@ fn main() { let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) .unwrap(); diff --git a/examples/client.rs b/examples/client.rs index 1c7a94d4f..3d9f05ca7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -42,7 +42,7 @@ fn main() { let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)) .unwrap(); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 496010704..4931acfd6 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -30,7 +30,7 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) .unwrap(); diff --git a/examples/dns.rs b/examples/dns.rs index e7f1bc8a2..1126795f4 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -43,7 +43,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) .unwrap(); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index c406f6fb6..23ab71767 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -36,7 +36,7 @@ fn main() { let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) .unwrap(); diff --git a/examples/loopback.rs b/examples/loopback.rs index de2c91692..9aa0d432c 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -85,7 +85,7 @@ fn main() { let mut neighbor_cache_entries = [None; 8]; let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) .unwrap(); diff --git a/examples/multicast.rs b/examples/multicast.rs index 2ae6f782b..aa181febf 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -34,7 +34,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs.push(ip_addr).unwrap(); let mut ipv4_multicast_storage = [None; 1]; let mut iface = InterfaceBuilder::new() diff --git a/examples/ping.rs b/examples/ping.rs index 85d889e7d..9a97d66ce 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -116,7 +116,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) .unwrap(); diff --git a/examples/server.rs b/examples/server.rs index 0b0e219ff..48af46f2d 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -58,7 +58,7 @@ fn main() { let tcp4_socket = tcp::Socket::new(tcp4_rx_buffer, tcp4_tx_buffer); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) .unwrap(); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 605a9fdca..2df9cc493 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -81,7 +81,7 @@ fn main() { let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new( IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 54eac8a3b..4e71b9b49 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -161,7 +161,7 @@ fn main() { let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, ]); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new( IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 5b0972e6f..a7a399e97 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -34,7 +34,7 @@ use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; -const MAX_IP_ADDRS_NUM: usize = 4; +const MAX_IP_ADDRS_NUM: usize = 5; pub(crate) struct FragmentsBuffer<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] From bf827533fe88465927c44ffbc982455d2d5c9628 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 20 Dec 2022 14:20:52 +0000 Subject: [PATCH 451/566] Fixed problem with doc test --- src/iface/interface/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index a7a399e97..6737fbec4 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -342,7 +342,7 @@ let neighbor_cache = // ... # let ipv4_frag_cache = // ... # ReassemblyBuffer::new(vec![], BTreeMap::new()); let ip_addrs = // ... -# []; +# heapless::Vec::::new(); let builder = InterfaceBuilder::new() .hardware_addr(hw_addr.into()) .neighbor_cache(neighbor_cache) From 66325a1b8f2788bd2b186d4dfe1f87383768b6fd Mon Sep 17 00:00:00 2001 From: Klim Tsoutsman Date: Thu, 22 Dec 2022 09:59:10 +1100 Subject: [PATCH 452/566] Remove `Sized` bound from `Socket` methods Signed-off-by: Klim Tsoutsman --- src/socket/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 5c416f685..02f2e02bf 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -92,15 +92,9 @@ impl<'a> Socket<'a> { /// A conversion trait for network sockets. pub trait AnySocket<'a> { - fn upcast(self) -> Socket<'a> - where - Self: Sized; - fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self> - where - Self: Sized; - fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> - where - Self: Sized; + fn upcast(self) -> Socket<'a>; + fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self>; + fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; } macro_rules! from_socket { From 33496076a1e8f3a1716e39a887c5f41b375c5f4d Mon Sep 17 00:00:00 2001 From: Klim Tsoutsman Date: Thu, 22 Dec 2022 11:02:44 +1100 Subject: [PATCH 453/566] Make `AnySocket` object safe Apologies, in #718, removing the sized bound in `downcast` and `downcast_mut` implicitly made `AnySocket` object unsafe as they don't have a `self` method. I had a bit of a brain fart. Signed-off-by: Klim Tsoutsman --- src/socket/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/socket/mod.rs b/src/socket/mod.rs index 02f2e02bf..7d48b4234 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -93,8 +93,12 @@ impl<'a> Socket<'a> { /// A conversion trait for network sockets. pub trait AnySocket<'a> { fn upcast(self) -> Socket<'a>; - fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self>; - fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>; + fn downcast<'c>(socket: &'c Socket<'a>) -> Option<&'c Self> + where + Self: Sized; + fn downcast_mut<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> + where + Self: Sized; } macro_rules! from_socket { From 9beb57a992ae213f577d4e735d24850dfec3c1a9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Dec 2022 01:59:22 +0100 Subject: [PATCH 454/566] iface: use heapless Vec for routes. Lookup is O(n) now. However, it previously did 32 (or 128 for ipv6!) map lookups. Since the route table typically doesn't have that many routes, the new code is likely faster even if it's O(n). --- examples/client.rs | 3 +- examples/dhcp_client.rs | 4 +- examples/dns.rs | 3 +- examples/httpclient.rs | 3 +- examples/ping.rs | 3 +- src/iface/interface/mod.rs | 14 ++-- src/iface/route.rs | 145 ++++++++++++++++++------------------- src/wire/ipv6.rs | 41 ++++++++--- 8 files changed, 110 insertions(+), 106 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 3d9f05ca7..07b19ba3c 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -47,8 +47,7 @@ fn main() { .push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)) .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); - let mut routes_storage = [None; 1]; - let mut routes = Routes::new(&mut routes_storage[..]); + let mut routes = Routes::new(); routes.add_default_ipv4_route(default_v4_gw).unwrap(); let medium = device.capabilities().medium; diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 4931acfd6..c5ff81de4 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -34,11 +34,9 @@ fn main() { ip_addrs .push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) .unwrap(); - let mut routes_storage = [None; 1]; - let routes = Routes::new(&mut routes_storage[..]); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); + let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) diff --git a/examples/dns.rs b/examples/dns.rs index 1126795f4..1189f4292 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -53,8 +53,7 @@ fn main() { .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes_storage = [None; 2]; - let mut routes = Routes::new(&mut routes_storage[..]); + let mut routes = Routes::new(); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 23ab71767..d85bd94df 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -48,8 +48,7 @@ fn main() { .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes_storage = [None; 2]; - let mut routes = Routes::new(&mut routes_storage[..]); + let mut routes = Routes::new(); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); diff --git a/examples/ping.rs b/examples/ping.rs index 9a97d66ce..d8afa5acc 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -126,8 +126,7 @@ fn main() { .unwrap(); let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes_storage = [None; 2]; - let mut routes = Routes::new(&mut routes_storage[..]); + let mut routes = Routes::new(); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 6737fbec4..30472b963 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -275,7 +275,7 @@ pub struct InterfaceInner<'a> { ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, - routes: Routes<'a>, + routes: Routes, #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, /// When to report for (all or) the next multicast group membership via IGMP @@ -294,7 +294,7 @@ pub struct InterfaceBuilder<'a> { ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, - routes: Routes<'a>, + routes: Routes, /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, @@ -371,7 +371,7 @@ let iface = builder.finalize(&mut device); ip_addrs: Vec::new(), #[cfg(feature = "proto-ipv4")] any_ip: false, - routes: Routes::new(ManagedMap::Borrowed(&mut [])), + routes: Routes::new(), #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), random_seed: 0, @@ -469,7 +469,7 @@ let iface = builder.finalize(&mut device); /// [routes]: struct.Interface.html#method.routes pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a> where - T: Into>, + T: Into, { self.routes = routes.into(); self @@ -1023,11 +1023,11 @@ impl<'a> Interface<'a> { self.inner.ipv4_address() } - pub fn routes(&self) -> &Routes<'a> { + pub fn routes(&self) -> &Routes { &self.inner.routes } - pub fn routes_mut(&mut self) -> &mut Routes<'a> { + pub fn routes_mut(&mut self) -> &mut Routes { &mut self.inner.routes } @@ -1570,7 +1570,7 @@ impl<'a> InterfaceInner<'a> { ]) .unwrap(), rand: Rand::new(1234), - routes: Routes::new(&mut [][..]), + routes: Routes::new(), #[cfg(feature = "proto-ipv4")] any_ip: false, diff --git a/src/iface/route.rs b/src/iface/route.rs index ed87b5255..ee55c89c8 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -1,6 +1,5 @@ use crate::time::Instant; -use core::ops::Bound; -use managed::ManagedMap; +use heapless::Vec; use crate::wire::{IpAddress, IpCidr}; #[cfg(feature = "proto-ipv4")] @@ -9,10 +8,13 @@ use crate::wire::{Ipv4Address, Ipv4Cidr}; use crate::wire::{Ipv6Address, Ipv6Cidr}; use crate::{Error, Result}; +pub const MAX_ROUTE_COUNT: usize = 4; + /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Route { + pub cidr: IpCidr, pub via_router: IpAddress, /// `None` means "forever". pub preferred_until: Option, @@ -20,11 +22,18 @@ pub struct Route { pub expires_at: Option, } +#[cfg(feature = "proto-ipv4")] +const IPV4_DEFAULT: IpCidr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(0, 0, 0, 0), 0)); +#[cfg(feature = "proto-ipv6")] +const IPV6_DEFAULT: IpCidr = + IpCidr::Ipv6(Ipv6Cidr::new(Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0)); + impl Route { /// Returns a route to 0.0.0.0/0 via the `gateway`, with no expiry. #[cfg(feature = "proto-ipv4")] pub fn new_ipv4_gateway(gateway: Ipv4Address) -> Route { Route { + cidr: IPV4_DEFAULT, via_router: gateway.into(), preferred_until: None, expires_at: None, @@ -35,6 +44,7 @@ impl Route { #[cfg(feature = "proto-ipv6")] pub fn new_ipv6_gateway(gateway: Ipv6Address) -> Route { Route { + cidr: IPV6_DEFAULT, via_router: gateway.into(), preferred_until: None, expires_at: None, @@ -43,42 +53,21 @@ impl Route { } /// A routing table. -/// -/// # Examples -/// -/// On systems with heap, this table can be created with: -/// -/// ```rust -/// use std::collections::BTreeMap; -/// use smoltcp::iface::Routes; -/// let mut routes = Routes::new(BTreeMap::new()); -/// ``` -/// -/// On systems without heap, use: -/// -/// ```rust -/// use smoltcp::iface::Routes; -/// let mut routes_storage = []; -/// let mut routes = Routes::new(&mut routes_storage[..]); -/// ``` #[derive(Debug)] -pub struct Routes<'a> { - storage: ManagedMap<'a, IpCidr, Route>, +pub struct Routes { + storage: Vec, } -impl<'a> Routes<'a> { - /// Creates a routing tables. The backing storage is **not** cleared - /// upon creation. - pub fn new(storage: T) -> Routes<'a> - where - T: Into>, - { - let storage = storage.into(); - Routes { storage } +impl Routes { + /// Creates a new empty routing table. + pub fn new() -> Self { + Self { + storage: Vec::new(), + } } /// Update the routes of this node. - pub fn update)>(&mut self, f: F) { + pub fn update)>(&mut self, f: F) { f(&mut self.storage); } @@ -87,12 +76,11 @@ impl<'a> Routes<'a> { /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv4")] pub fn add_default_ipv4_route(&mut self, gateway: Ipv4Address) -> Result> { - let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); - let route = Route::new_ipv4_gateway(gateway); - match self.storage.insert(cidr, route) { - Ok(route) => Ok(route), - Err((_cidr, _route)) => Err(Error::Exhausted), - } + let old = self.remove_default_ipv4_route(); + self.storage + .push(Route::new_ipv4_gateway(gateway)) + .map_err(|_| Error::Exhausted)?; + Ok(old) } /// Add a default ipv6 gateway (ie. "ip -6 route add ::/0 via `gateway`"). @@ -100,12 +88,11 @@ impl<'a> Routes<'a> { /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv6")] pub fn add_default_ipv6_route(&mut self, gateway: Ipv6Address) -> Result> { - let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0); - let route = Route::new_ipv6_gateway(gateway); - match self.storage.insert(cidr, route) { - Ok(route) => Ok(route), - Err((_cidr, _route)) => Err(Error::Exhausted), - } + let old = self.remove_default_ipv6_route(); + self.storage + .push(Route::new_ipv6_gateway(gateway)) + .map_err(|_| Error::Exhausted)?; + Ok(old) } /// Remove the default ipv4 gateway @@ -113,8 +100,16 @@ impl<'a> Routes<'a> { /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv4")] pub fn remove_default_ipv4_route(&mut self) -> Option { - let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); - self.storage.remove(&cidr) + if let Some((i, _)) = self + .storage + .iter() + .enumerate() + .find(|(_, r)| r.cidr == IPV4_DEFAULT) + { + Some(self.storage.remove(i)) + } else { + None + } } /// Remove the default ipv6 gateway @@ -122,38 +117,35 @@ impl<'a> Routes<'a> { /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv6")] pub fn remove_default_ipv6_route(&mut self) -> Option { - let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0); - self.storage.remove(&cidr) + if let Some((i, _)) = self + .storage + .iter() + .enumerate() + .find(|(_, r)| r.cidr == IPV6_DEFAULT) + { + Some(self.storage.remove(i)) + } else { + None + } } pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option { assert!(addr.is_unicast()); - let cidr = match addr { - #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)), - #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)), - }; - - for (prefix, route) in self - .storage - .range((Bound::Unbounded::, Bound::Included(cidr))) - .rev() - { - // TODO: do something with route.preferred_until - if let Some(expires_at) = route.expires_at { - if timestamp > expires_at { - continue; + self.storage + .iter() + // Keep only matching routes + .filter(|route| { + if let Some(expires_at) = route.expires_at { + if timestamp > expires_at { + return false; + } } - } - - if prefix.contains_addr(addr) { - return Some(route.via_router); - } - } - - None + route.cidr.contains_addr(addr) + }) + // pick the most specific one (highest prefix_len) + .max_by_key(|route| route.cidr.prefix_len()) + .map(|route| route.via_router) } } @@ -209,8 +201,7 @@ mod test { #[test] fn test_fill() { - let mut routes_storage = [None, None, None]; - let mut routes = Routes::new(&mut routes_storage[..]); + let mut routes = Routes::new(); assert_eq!( routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), @@ -234,12 +225,13 @@ mod test { ); let route = Route { + cidr: cidr_1().into(), via_router: ADDR_1A.into(), preferred_until: None, expires_at: None, }; routes.update(|storage| { - storage.insert(cidr_1().into(), route).unwrap(); + storage.push(route).unwrap(); }); assert_eq!( @@ -264,12 +256,13 @@ mod test { ); let route2 = Route { + cidr: cidr_2().into(), via_router: ADDR_2A.into(), preferred_until: Some(Instant::from_millis(10)), expires_at: Some(Instant::from_millis(10)), }; routes.update(|storage| { - storage.insert(cidr_2().into(), route2).unwrap(); + storage.push(route2).unwrap(); }); assert_eq!( diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 988355192..2f7b00e09 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -68,17 +68,34 @@ impl Address { /// Construct an IPv6 address from parts. #[allow(clippy::too_many_arguments)] - pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address { - let mut addr = [0u8; ADDR_SIZE]; - NetworkEndian::write_u16(&mut addr[..2], a0); - NetworkEndian::write_u16(&mut addr[2..4], a1); - NetworkEndian::write_u16(&mut addr[4..6], a2); - NetworkEndian::write_u16(&mut addr[6..8], a3); - NetworkEndian::write_u16(&mut addr[8..10], a4); - NetworkEndian::write_u16(&mut addr[10..12], a5); - NetworkEndian::write_u16(&mut addr[12..14], a6); - NetworkEndian::write_u16(&mut addr[14..], a7); - Address(addr) + pub const fn new( + a0: u16, + a1: u16, + a2: u16, + a3: u16, + a4: u16, + a5: u16, + a6: u16, + a7: u16, + ) -> Address { + Address([ + (a0 >> 8) as u8, + a0 as u8, + (a1 >> 8) as u8, + a1 as u8, + (a2 >> 8) as u8, + a2 as u8, + (a3 >> 8) as u8, + a3 as u8, + (a4 >> 8) as u8, + a4 as u8, + (a5 >> 8) as u8, + a5 as u8, + (a6 >> 8) as u8, + a6 as u8, + (a7 >> 8) as u8, + a7 as u8, + ]) } /// Construct an IPv6 address from a sequence of octets, in big-endian. @@ -324,7 +341,7 @@ impl Cidr { /// /// # Panics /// This function panics if the prefix length is larger than 128. - pub fn new(address: Address, prefix_len: u8) -> Cidr { + pub const fn new(address: Address, prefix_len: u8) -> Cidr { assert!(prefix_len <= 128); Cidr { address, From 4eb49b808a665158dba60e9001777fbfa75fff9d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Dec 2022 02:55:01 +0100 Subject: [PATCH 455/566] iface: use heapless LinearMap for neighbor cache. --- examples/benchmark.rs | 3 +- examples/client.rs | 2 +- examples/dhcp_client.rs | 5 +- examples/dns.rs | 3 +- examples/httpclient.rs | 3 +- examples/loopback.rs | 5 +- examples/multicast.rs | 3 +- examples/ping.rs | 3 +- examples/server.rs | 2 +- examples/sixlowpan.rs | 2 +- examples/sixlowpan_benchmark.rs | 2 +- src/iface/interface/mod.rs | 8 +- src/iface/interface/tests.rs | 4 +- src/iface/neighbor.rs | 156 ++++++-------------------------- 14 files changed, 44 insertions(+), 157 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index a6b6cab5d..e7220d4a5 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -4,7 +4,6 @@ mod utils; use log::debug; use std::cmp; -use std::collections::BTreeMap; use std::io::{Read, Write}; use std::net::TcpStream; use std::os::unix::io::AsRawFd; @@ -82,7 +81,7 @@ fn main() { _ => panic!("invalid mode"), }; - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); diff --git a/examples/client.rs b/examples/client.rs index 07b19ba3c..e1e03191d 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -35,7 +35,7 @@ fn main() { let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let port = u16::from_str(&matches.free[1]).expect("invalid port format"); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index c5ff81de4..132dc0266 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -2,10 +2,9 @@ mod utils; use log::*; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes, SocketSet}; +use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::socket::dhcpv4; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; @@ -28,7 +27,7 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let mut ip_addrs = heapless::Vec::::new(); ip_addrs diff --git a/examples/dns.rs b/examples/dns.rs index 1189f4292..3e9cae034 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -15,7 +15,6 @@ use smoltcp::time::Instant; use smoltcp::wire::{ DnsQueryType, EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, }; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; fn main() { @@ -33,7 +32,7 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let name = &matches.free[0]; - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let servers = &[ Ipv4Address::new(8, 8, 4, 4).into(), diff --git a/examples/httpclient.rs b/examples/httpclient.rs index d85bd94df..6c612eaea 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -1,7 +1,6 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; use url::Url; @@ -29,7 +28,7 @@ fn main() { let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let url = Url::parse(&matches.free[1]).expect("invalid url format"); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); diff --git a/examples/loopback.rs b/examples/loopback.rs index 9aa0d432c..206730e1e 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -82,16 +82,13 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true) }; - let mut neighbor_cache_entries = [None; 8]; - let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); - let mut ip_addrs = heapless::Vec::::new(); ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) .unwrap(); let mut iface = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(neighbor_cache) + .neighbor_cache(NeighborCache::new()) .ip_addrs(ip_addrs) .finalize(&mut device); diff --git a/examples/multicast.rs b/examples/multicast.rs index aa181febf..d514f1aae 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -1,7 +1,6 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; @@ -28,7 +27,7 @@ fn main() { let fd = device.as_raw_fd(); let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let local_addr = Ipv4Address::new(192, 168, 69, 2); diff --git a/examples/ping.rs b/examples/ping.rs index d8afa5acc..271e29d55 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -4,7 +4,6 @@ use byteorder::{ByteOrder, NetworkEndian}; use log::debug; use smoltcp::iface::SocketSet; use std::cmp; -use std::collections::BTreeMap; use std::collections::HashMap; use std::os::unix::io::AsRawFd; use std::str::FromStr; @@ -106,7 +105,7 @@ fn main() { .unwrap_or(5), ); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let remote_addr = address; diff --git a/examples/server.rs b/examples/server.rs index 48af46f2d..f4ecc8c5d 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -29,7 +29,7 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let udp_rx_buffer = udp::PacketBuffer::new( vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 2df9cc493..fa3e04491 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -68,7 +68,7 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 4e71b9b49..53a4d4305 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -148,7 +148,7 @@ fn main() { _ => panic!("invalid mode"), }; - let neighbor_cache = NeighborCache::new(BTreeMap::new()); + let neighbor_cache = NeighborCache::new(); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 30472b963..8c69640d2 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -259,7 +259,7 @@ pub struct InterfaceInner<'a> { rand: Rand, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option>, + neighbor_cache: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Option, #[cfg(feature = "medium-ieee802154")] @@ -288,7 +288,7 @@ pub struct InterfaceBuilder<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option>, + neighbor_cache: Option, #[cfg(feature = "medium-ieee802154")] pan_id: Option, ip_addrs: Vec, @@ -337,7 +337,7 @@ let mut device = // ... let hw_addr = // ... # EthernetAddress::default(); let neighbor_cache = // ... -# NeighborCache::new(BTreeMap::new()); +# NeighborCache::new(); # #[cfg(feature = "proto-ipv4-fragmentation")] # let ipv4_frag_cache = // ... # ReassemblyBuffer::new(vec![], BTreeMap::new()); @@ -496,7 +496,7 @@ let iface = builder.finalize(&mut device); /// Set the Neighbor Cache the interface will use. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { + pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache) -> Self { self.neighbor_cache = Some(neighbor_cache); self } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 92922be64..04027d05a 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -90,7 +90,7 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let iface_builder = InterfaceBuilder::new() .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .neighbor_cache(NeighborCache::new()) .ip_addrs(ip_addrs); #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -126,7 +126,7 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let iface_builder = InterfaceBuilder::new() .hardware_addr(Ieee802154Address::default().into()) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .neighbor_cache(NeighborCache::new()) .ip_addrs(ip_addrs); #[cfg(feature = "proto-sixlowpan-fragmentation")] diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index d61fc0e05..d189b7503 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -1,11 +1,16 @@ // Heads up! Before working on this file you should read, at least, // the parts of RFC 1122 that discuss ARP. -use managed::ManagedMap; +use heapless::LinearMap; use crate::time::{Duration, Instant}; use crate::wire::{HardwareAddress, IpAddress}; +#[cfg(not(test))] +pub const NEIGHBOR_CACHE_SIZE: usize = 16; +#[cfg(test)] +pub const NEIGHBOR_CACHE_SIZE: usize = 3; + /// A cached neighbor. /// /// A neighbor mapping translates from a protocol address to a hardware address, @@ -41,73 +46,23 @@ impl Answer { } /// A neighbor cache backed by a map. -/// -/// # Examples -/// -/// On systems with heap, this cache can be created with: -/// -/// ```rust -/// use std::collections::BTreeMap; -/// use smoltcp::iface::NeighborCache; -/// let mut neighbor_cache = NeighborCache::new(BTreeMap::new()); -/// ``` -/// -/// On systems without heap, use: -/// -/// ```rust -/// use smoltcp::iface::NeighborCache; -/// let mut neighbor_cache_storage = [None; 8]; -/// let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_storage[..]); -/// ``` #[derive(Debug)] -pub struct Cache<'a> { - storage: ManagedMap<'a, IpAddress, Neighbor>, +pub struct Cache { + storage: LinearMap, silent_until: Instant, - #[cfg(feature = "alloc")] - gc_threshold: usize, } -impl<'a> Cache<'a> { +impl Cache { /// Minimum delay between discovery requests, in milliseconds. pub(crate) const SILENT_TIME: Duration = Duration::from_millis(1_000); /// Neighbor entry lifetime, in milliseconds. pub(crate) const ENTRY_LIFETIME: Duration = Duration::from_millis(60_000); - /// Default number of entries in the cache before GC kicks in - #[cfg(feature = "alloc")] - pub(crate) const GC_THRESHOLD: usize = 1024; - - /// Create a cache. The backing storage is cleared upon creation. - /// - /// # Panics - /// This function panics if `storage.len() == 0`. - pub fn new(storage: T) -> Cache<'a> - where - T: Into>, - { - let mut storage = storage.into(); - storage.clear(); - - Cache { - storage, - #[cfg(feature = "alloc")] - gc_threshold: Self::GC_THRESHOLD, - silent_until: Instant::from_millis(0), - } - } - - #[cfg(feature = "alloc")] - pub fn new_with_limit(storage: T, gc_threshold: usize) -> Cache<'a> - where - T: Into>, - { - let mut storage = storage.into(); - storage.clear(); - - Cache { - storage, - gc_threshold, + /// Create a cache. + pub fn new() -> Self { + Self { + storage: LinearMap::new(), silent_until: Instant::from_millis(0), } } @@ -121,24 +76,6 @@ impl<'a> Cache<'a> { debug_assert!(protocol_addr.is_unicast()); debug_assert!(hardware_addr.is_unicast()); - #[cfg(feature = "alloc")] - let current_storage_size = self.storage.len(); - - match self.storage { - ManagedMap::Borrowed(_) => (), - #[cfg(feature = "alloc")] - ManagedMap::Owned(ref mut map) => { - if current_storage_size >= self.gc_threshold { - let new_btree_map = map - .iter_mut() - .map(|(key, value)| (*key, *value)) - .filter(|(_, v)| timestamp < v.expires_at) - .collect(); - - *map = new_btree_map; - } - } - }; let neighbor = Neighbor { expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr, @@ -158,24 +95,13 @@ impl<'a> Cache<'a> { net_trace!("filled {} => {} (was empty)", protocol_addr, hardware_addr); } Err((protocol_addr, neighbor)) => { - // If we're going down this branch, it means that a fixed-size cache storage - // is full, and we need to evict an entry. - let old_protocol_addr = match self.storage { - ManagedMap::Borrowed(ref mut pairs) => { - pairs - .iter() - .min_by_key(|pair_opt| { - let (_protocol_addr, neighbor) = pair_opt.unwrap(); - neighbor.expires_at - }) - .expect("empty neighbor cache storage") // unwraps min_by_key - .unwrap() // unwraps pair - .0 - } - // Owned maps can extend themselves. - #[cfg(feature = "alloc")] - ManagedMap::Owned(_) => unreachable!(), - }; + // If we're going down this branch, it means the cache is full, and we need to evict an entry. + let old_protocol_addr = *self + .storage + .iter() + .min_by_key(|(_, neighbor)| neighbor.expires_at) + .expect("empty neighbor cache storage") + .0; let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap(); match self.storage.insert(protocol_addr, neighbor) { @@ -229,7 +155,6 @@ impl<'a> Cache<'a> { mod test { use super::*; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4}; - use std::collections::BTreeMap; use crate::wire::EthernetAddress; @@ -240,8 +165,7 @@ mod test { #[test] fn test_fill() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); assert!(!cache .lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)) @@ -273,8 +197,7 @@ mod test { #[test] fn test_expire() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); assert_eq!( @@ -291,8 +214,7 @@ mod test { #[test] fn test_replace() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); assert_eq!( @@ -306,33 +228,9 @@ mod test { ); } - #[test] - fn test_cache_gc() { - let mut cache = Cache::new_with_limit(BTreeMap::new(), 2); - cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100)); - cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); - // Adding third item after the expiration of the previous - // two should garbage collect - cache.fill( - MOCK_IP_ADDR_3, - HADDR_C, - Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2, - ); - - assert_eq!(cache.storage.len(), 1); - assert_eq!( - cache.lookup( - &MOCK_IP_ADDR_3, - Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2 - ), - Answer::Found(HADDR_C) - ); - } - #[test] fn test_evict() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100)); cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); @@ -357,8 +255,7 @@ mod test { #[test] fn test_hush() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); assert_eq!( cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), @@ -378,8 +275,7 @@ mod test { #[test] fn test_flush() { - let mut cache_storage = [Default::default(); 3]; - let mut cache = Cache::new(&mut cache_storage[..]); + let mut cache = Cache::new(); cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); assert_eq!( From e9016e7dad5a63859b586c894715cdc98aef6fd3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Dec 2022 03:27:31 +0100 Subject: [PATCH 456/566] iface: add PhantomData to avoid unused lifetime errors. --- src/iface/interface/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 8c69640d2..9268f9e55 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -16,6 +16,7 @@ mod ipv4; mod ipv6; use core::cmp; +use core::marker::PhantomData; use heapless::Vec; use managed::{ManagedMap, ManagedSlice}; @@ -258,6 +259,8 @@ pub struct InterfaceInner<'a> { now: Instant, rand: Rand, + phantom: PhantomData<&'a mut ()>, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -285,6 +288,8 @@ pub struct InterfaceInner<'a> { /// A builder structure used for creating a network interface. pub struct InterfaceBuilder<'a> { + phantom: PhantomData<&'a mut ()>, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -360,6 +365,8 @@ let iface = builder.finalize(&mut device); #[allow(clippy::new_without_default)] pub fn new() -> Self { InterfaceBuilder { + phantom: PhantomData, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: None, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -672,6 +679,7 @@ let iface = builder.finalize(&mut device); _lifetime: core::marker::PhantomData, }, inner: InterfaceInner { + phantom: PhantomData, now: Instant::from_secs(0), caps, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -1534,6 +1542,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(test)] pub(crate) fn mock() -> Self { Self { + phantom: PhantomData, caps: DeviceCapabilities { #[cfg(feature = "medium-ethernet")] medium: crate::phy::Medium::Ethernet, From d9f5b4b3c2e449ee90739dc33d5564f3ebd0e19f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Dec 2022 03:31:48 +0100 Subject: [PATCH 457/566] clippy: allow new_without_default. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 78f1f82ab..204139f28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ compile_error!("at least one socket needs to be enabled"); */ #![allow(clippy::identity_op)] #![allow(clippy::option_map_unit_fn)] #![allow(clippy::unit_arg)] +#![allow(clippy::new_without_default)] #[cfg(feature = "alloc")] extern crate alloc; From 94a52d8baf8128b752e8a36a0bca3dfb95f7488f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 18:23:06 +0100 Subject: [PATCH 458/566] iface: use heapless LinearMap for ipv4_multicast_groups --- examples/multicast.rs | 2 -- src/iface/interface/mod.rs | 26 ++++++++++++++------------ src/iface/interface/tests.rs | 12 ++++-------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/examples/multicast.rs b/examples/multicast.rs index d514f1aae..ab46e8508 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -35,12 +35,10 @@ fn main() { let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ip_addrs = heapless::Vec::::new(); ip_addrs.push(ip_addr).unwrap(); - let mut ipv4_multicast_storage = [None; 1]; let mut iface = InterfaceBuilder::new() .hardware_addr(ethernet_addr.into()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) .finalize(&mut device); let now = Instant::now(); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 9268f9e55..d57261bfa 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -17,8 +17,8 @@ mod ipv6; use core::cmp; use core::marker::PhantomData; -use heapless::Vec; -use managed::{ManagedMap, ManagedSlice}; +use heapless::{LinearMap, Vec}; +use managed::ManagedSlice; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] use super::fragmentation::PacketAssemblerSet; @@ -35,7 +35,9 @@ use crate::time::{Duration, Instant}; use crate::wire::*; use crate::{Error, Result}; -const MAX_IP_ADDRS_NUM: usize = 5; +const MAX_IP_ADDR_COUNT: usize = 5; +#[cfg(feature = "proto-igmp")] +const MAX_IPV4_MULTICAST_GROUPS: usize = 4; pub(crate) struct FragmentsBuffer<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] @@ -275,12 +277,12 @@ pub struct InterfaceInner<'a> { sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, - ip_addrs: Vec, + ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes, #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + ipv4_multicast_groups: LinearMap, /// When to report for (all or) the next multicast group membership via IGMP #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState, @@ -296,13 +298,13 @@ pub struct InterfaceBuilder<'a> { neighbor_cache: Option, #[cfg(feature = "medium-ieee802154")] pan_id: Option, - ip_addrs: Vec, + ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes, /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, + ipv4_multicast_groups: LinearMap, random_seed: u64, #[cfg(feature = "proto-ipv4-fragmentation")] @@ -380,7 +382,7 @@ let iface = builder.finalize(&mut device); any_ip: false, routes: Routes::new(), #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + ipv4_multicast_groups: LinearMap::new(), random_seed: 0, #[cfg(feature = "proto-ipv4-fragmentation")] @@ -443,7 +445,7 @@ let iface = builder.finalize(&mut device); /// [ip_addrs]: struct.Interface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self where - T: Into>, + T: Into>, { let ip_addrs = ip_addrs.into(); InterfaceInner::check_ip_addrs(&ip_addrs); @@ -495,7 +497,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-igmp")] pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self where - T: Into>, + T: Into>, { self.ipv4_multicast_groups = ipv4_multicast_groups.into(); self @@ -1014,7 +1016,7 @@ impl<'a> Interface<'a> { /// /// # Panics /// This function panics if any of the addresses are not unicast. - pub fn update_ip_addrs)>(&mut self, f: F) { + pub fn update_ip_addrs)>(&mut self, f: F) { f(&mut self.inner.ip_addrs); InterfaceInner::flush_cache(&mut self.inner); InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) @@ -1615,7 +1617,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: ManagedMap::Borrowed(&mut []), + ipv4_multicast_groups: LinearMap::new(), } } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 04027d05a..2e482fa8c 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -42,7 +42,7 @@ fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) @@ -63,8 +63,6 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) .ipv4_fragmentation_buffer(vec![]); - #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); let iface = iface_builder.finalize(&mut device); (iface, SocketSet::new(vec![]), device) @@ -74,7 +72,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv4")] ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) @@ -103,8 +101,6 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) .ipv4_fragmentation_buffer(vec![]); - #[cfg(feature = "proto-igmp")] - let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); let iface = iface_builder.finalize(&mut device); (iface, SocketSet::new(vec![]), device) @@ -114,7 +110,7 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); - let mut ip_addrs = heapless::Vec::::new(); + let mut ip_addrs = heapless::Vec::::new(); #[cfg(feature = "proto-ipv6")] ip_addrs .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) @@ -1063,7 +1059,7 @@ fn test_icmpv4_socket() { #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { let (mut iface, _, _device) = create(MEDIUM); - let mut new_addrs = heapless::Vec::::new(); + let mut new_addrs = heapless::Vec::::new(); new_addrs .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) .unwrap(); From 8b92e20871f81de4cd2fd116885d64c8dd7ea6f0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 21:46:31 +0100 Subject: [PATCH 459/566] Remove feature rust-1_28 --- Cargo.toml | 3 --- src/phy/loopback.rs | 3 --- 2 files changed, 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e2330ab21..c5395b15f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,9 +74,6 @@ default = [ "async" ] -# experimental; do not use; no guarantees provided that this feature will be kept -"rust-1_28" = [] - [[example]] name = "packet2pcap" path = "utils/packet2pcap.rs" diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index 7b3b57124..5bf035419 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -1,8 +1,5 @@ -#[cfg(not(feature = "rust-1_28"))] use alloc::collections::VecDeque; use alloc::vec::Vec; -#[cfg(feature = "rust-1_28")] -use alloc::VecDeque; use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; From 9183ca7b2756ffbc6bcbb55ac7e4fff1000c4ec7 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 22 Dec 2022 09:56:53 +0100 Subject: [PATCH 460/566] Fix pretty print and tcp socket connect tests This fixes #723. --- src/socket/tcp.rs | 39 +++++++++++++++++++++++++++++++++++++-- src/wire/pretty_print.rs | 17 ++++++++++++++--- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 034863918..78213b702 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -731,8 +731,43 @@ impl<'a> Socket<'a> { /// The local port must be provided explicitly. Assuming `fn get_ephemeral_port() -> u16` /// allocates a port between 49152 and 65535, a connection may be established as follows: /// - /// ```rust,ignore - /// socket.connect((IpAddress::v4(10, 0, 0, 1), 80), get_ephemeral_port()) + /// ```rust + /// # #[cfg(all( + /// # feature = "medium-ethernet", + /// # feature = "proto-ipv4", + /// # ))] + /// # { + /// # use smoltcp::socket::tcp::{Socket, SocketBuffer}; + /// # use smoltcp::iface::{InterfaceBuilder, NeighborCache}; + /// # use smoltcp::wire::{HardwareAddress, EthernetAddress, IpAddress, IpCidr}; + /// # + /// # fn get_ephemeral_port() -> u16 { + /// # 49152 + /// # } + /// # + /// # let mut socket = Socket::new( + /// # SocketBuffer::new(vec![0; 1200]), + /// # SocketBuffer::new(vec![0; 1200]) + /// # ); + /// # + /// # let mut ip_addrs = heapless::Vec::::new(); + /// # ip_addrs + /// # .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + /// # .unwrap(); + /// # + /// # let mut device =smoltcp::phy::Loopback::new(smoltcp::phy::Medium::Ethernet); + /// # let mut iface = InterfaceBuilder::new() + /// # .hardware_addr(HardwareAddress::Ethernet(EthernetAddress::default())) + /// # .neighbor_cache(NeighborCache::new()) + /// # .ip_addrs(ip_addrs) + /// # .finalize(&mut device); + /// # + /// socket.connect( + /// iface.context(), + /// (IpAddress::v4(10, 0, 0, 1), 80), + /// get_ephemeral_port() + /// ).unwrap(); + /// # } /// ``` /// /// The local address may optionally be provided. diff --git a/src/wire/pretty_print.rs b/src/wire/pretty_print.rs index a972cb641..fe7d8b892 100644 --- a/src/wire/pretty_print.rs +++ b/src/wire/pretty_print.rs @@ -7,7 +7,7 @@ easily human readable packet listings. A packet can be formatted using the `PrettyPrinter` wrapper: -```rust,ignore +```rust use smoltcp::wire::*; let buffer = vec![ // Ethernet II @@ -15,7 +15,7 @@ let buffer = vec![ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, // IPv4 - 0x45, 0x00, 0x00, 0x18, + 0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13, 0x14, @@ -25,7 +25,18 @@ let buffer = vec![ 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff ]; -print!("{}", PrettyPrinter::>::new("", &buffer)); + +let result = "\ +EthernetII src=11-12-13-14-15-16 dst=01-02-03-04-05-06 type=IPv4\n\ +\\ IPv4 src=17.18.19.20 dst=33.34.35.36 proto=ICMP (checksum incorrect)\n \ + \\ ICMPv4 echo request id=4660 seq=43981 len=4\ +"; + +#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] +assert_eq!( + result, + &format!("{}", PrettyPrinter::>::new("", &buffer)) +); ``` */ From 339545411afa69c472db0b02fadf7f792ffb7e3c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 30 Dec 2022 02:41:09 +0100 Subject: [PATCH 461/566] Clippy fixes. --- src/iface/fragmentation.rs | 2 +- src/iface/interface/mod.rs | 4 ++-- src/parsers.rs | 2 +- src/phy/fault_injector.rs | 4 ++-- src/storage/assembler.rs | 2 +- src/wire/arp.rs | 13 ++++--------- src/wire/ethernet.rs | 6 +++--- src/wire/icmpv4.rs | 18 +++++++++--------- src/wire/icmpv6.rs | 8 ++++---- src/wire/ieee802154.rs | 4 ++-- src/wire/igmp.rs | 18 ++++++++---------- src/wire/ip.rs | 28 ++++++++++++++-------------- src/wire/ipv4.rs | 8 ++++---- src/wire/ipv6.rs | 14 +++++++------- src/wire/ipv6fragment.rs | 4 ++-- src/wire/ipv6hopbyhop.rs | 4 ++-- src/wire/ipv6option.rs | 10 +++++----- src/wire/ipv6routing.rs | 6 +++--- src/wire/mod.rs | 6 +++--- src/wire/ndiscoption.rs | 22 +++++++++++----------- src/wire/tcp.rs | 18 +++++++++--------- src/wire/udp.rs | 4 ++-- 22 files changed, 99 insertions(+), 106 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index b9f15baf3..d022e1754 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -43,7 +43,7 @@ impl fmt::Display for AssemblerState { expires_at, offset_correction, } => { - write!(f, "{} expires at {}", assembler, expires_at)?; + write!(f, "{assembler} expires at {expires_at}")?; } } Ok(()) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index d57261bfa..c6a951497 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1747,11 +1747,11 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "socket-raw")] - fn raw_socket_filter<'frame>( + fn raw_socket_filter( &mut self, sockets: &mut SocketSet, ip_repr: &IpRepr, - ip_payload: &'frame [u8], + ip_payload: &[u8], ) -> bool { let mut handled_by_raw_socket = false; diff --git a/src/parsers.rs b/src/parsers.rs index cf67b44e5..bb17e35d9 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -95,7 +95,7 @@ impl<'a> Parser<'a> { fn accept_digit(&mut self, hex: bool) -> Result { let digit = self.advance()?; - if (b'0'..=b'9').contains(&digit) { + if digit.is_ascii_digit() { Ok(digit - b'0') } else if hex && (b'a'..=b'f').contains(&digit) { Ok(digit - b'a' + 10) diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index bf6bc6528..d5e22c48f 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -138,12 +138,12 @@ impl FaultInjector { /// Return the maximum packet transmission rate, in packets per second. pub fn max_tx_rate(&self) -> u64 { - self.config.max_rx_rate + self.config.max_tx_rate } /// Return the maximum packet reception rate, in packets per second. pub fn max_rx_rate(&self) -> u64 { - self.config.max_tx_rate + self.config.max_rx_rate } /// Return the interval for packet rate limiting, in milliseconds. diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index df36a7496..860b83783 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -108,7 +108,7 @@ impl fmt::Display for Assembler { if contig.is_empty() { break; } - write!(f, "{} ", contig)?; + write!(f, "{contig} ")?; } write!(f, "]")?; Ok(()) diff --git a/src/wire/arp.rs b/src/wire/arp.rs index bd83009cf..bb0df3a0e 100644 --- a/src/wire/arp.rs +++ b/src/wire/arp.rs @@ -322,7 +322,7 @@ impl Repr { impl> fmt::Display for Packet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), _ => { write!(f, "ARP (unrecognized)")?; write!( @@ -360,12 +360,7 @@ impl fmt::Display for Repr { } => { write!( f, - "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", - source_hardware_addr, - source_protocol_addr, - target_hardware_addr, - target_protocol_addr, - operation + "ARP type=Ethernet+IPv4 src={source_hardware_addr}/{source_protocol_addr} tgt={target_hardware_addr}/{target_protocol_addr} op={operation:?}" ) } } @@ -381,8 +376,8 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet), + Err(err) => write!(f, "{indent}({err})"), + Ok(packet) => write!(f, "{indent}{packet}"), } } } diff --git a/src/wire/ethernet.rs b/src/wire/ethernet.rs index 7c0081460..53dc1eacb 100644 --- a/src/wire/ethernet.rs +++ b/src/wire/ethernet.rs @@ -18,7 +18,7 @@ impl fmt::Display for EtherType { EtherType::Ipv4 => write!(f, "IPv4"), EtherType::Ipv6 => write!(f, "IPv6"), EtherType::Arp => write!(f, "ARP"), - EtherType::Unknown(id) => write!(f, "0x{:04x}", id), + EtherType::Unknown(id) => write!(f, "0x{id:04x}"), } } } @@ -230,10 +230,10 @@ impl> PrettyPrint for Frame { indent: &mut PrettyIndent, ) -> fmt::Result { let frame = match Frame::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), + Err(err) => return write!(f, "{indent}({err})"), Ok(frame) => frame, }; - write!(f, "{}{}", indent, frame)?; + write!(f, "{indent}{frame}")?; match frame.ethertype() { #[cfg(feature = "proto-ipv4")] diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index c665c5d07..60e12153f 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -45,7 +45,7 @@ impl fmt::Display for Message { Message::ParamProblem => write!(f, "parameter problem"), Message::Timestamp => write!(f, "timestamp"), Message::TimestampReply => write!(f, "timestamp reply"), - Message::Unknown(id) => write!(f, "{}", id), + Message::Unknown(id) => write!(f, "{id}"), } } } @@ -109,7 +109,7 @@ impl fmt::Display for DstUnreachable { } DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"), DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"), - DstUnreachable::Unknown(id) => write!(f, "{}", id), + DstUnreachable::Unknown(id) => write!(f, "{id}"), } } } @@ -143,7 +143,7 @@ impl fmt::Display for TimeExceeded { match *self { TimeExceeded::TtlExpired => write!(f, "time-to-live exceeded in transit"), TimeExceeded::FragExpired => write!(f, "fragment reassembly time exceeded"), - TimeExceeded::Unknown(id) => write!(f, "{}", id), + TimeExceeded::Unknown(id) => write!(f, "{id}"), } } } @@ -556,9 +556,9 @@ impl<'a> Repr<'a> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self, &ChecksumCapabilities::default()) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "ICMPv4 ({})", err)?; + write!(f, "ICMPv4 ({err})")?; write!(f, " type={:?}", self.msg_type())?; match self.msg_type() { Message::DstUnreachable => { @@ -600,10 +600,10 @@ impl<'a> fmt::Display for Repr<'a> { data.len() ), Repr::DstUnreachable { reason, .. } => { - write!(f, "ICMPv4 destination unreachable ({})", reason) + write!(f, "ICMPv4 destination unreachable ({reason})") } Repr::TimeExceeded { reason, .. } => { - write!(f, "ICMPv4 time exceeded ({})", reason) + write!(f, "ICMPv4 time exceeded ({reason})") } } } @@ -618,10 +618,10 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { let packet = match Packet::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), + Err(err) => return write!(f, "{indent}({err})"), Ok(packet) => packet, }; - write!(f, "{}{}", indent, packet)?; + write!(f, "{indent}{packet}")?; match packet.msg_type() { Message::DstUnreachable | Message::TimeExceeded => { diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 57355c718..86dde1854 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -94,7 +94,7 @@ impl fmt::Display for Message { Message::Redirect => write!(f, "redirect"), Message::MldQuery => write!(f, "multicast listener query"), Message::MldReport => write!(f, "multicast listener report"), - Message::Unknown(id) => write!(f, "{}", id), + Message::Unknown(id) => write!(f, "{id}"), } } } @@ -134,7 +134,7 @@ impl fmt::Display for DstUnreachable { write!(f, "source address failed ingress/egress policy") } DstUnreachable::RejectRoute => write!(f, "reject route to destination"), - DstUnreachable::Unknown(id) => write!(f, "{}", id), + DstUnreachable::Unknown(id) => write!(f, "{id}"), } } } @@ -157,7 +157,7 @@ impl fmt::Display for ParamProblem { ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."), ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."), ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."), - ParamProblem::Unknown(id) => write!(f, "{}", id), + ParamProblem::Unknown(id) => write!(f, "{id}"), } } } @@ -177,7 +177,7 @@ impl fmt::Display for TimeExceeded { match *self { TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"), TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"), - TimeExceeded::Unknown(id) => write!(f, "{}", id), + TimeExceeded::Unknown(id) => write!(f, "{id}"), } } } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index ac037a55b..1d3f183b9 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -28,7 +28,7 @@ impl fmt::Display for FrameType { FrameType::Multipurpose => write!(f, "Multipurpose"), FrameType::FragmentOrFrak => write!(f, "FragmentOrFrak"), FrameType::Extended => write!(f, "Extended"), - FrameType::Unknown(id) => write!(f, "0b{:04b}", id), + FrameType::Unknown(id) => write!(f, "0b{id:04b}"), } } } @@ -59,7 +59,7 @@ impl fmt::Display for AddressingMode { AddressingMode::Absent => write!(f, "Absent"), AddressingMode::Short => write!(f, "Short"), AddressingMode::Extended => write!(f, "Extended"), - AddressingMode::Unknown(id) => write!(f, "0b{:04b}", id), + AddressingMode::Unknown(id) => write!(f, "0b{id:04b}"), } } } diff --git a/src/wire/igmp.rs b/src/wire/igmp.rs index 3b275f8c2..ac13ece1a 100644 --- a/src/wire/igmp.rs +++ b/src/wire/igmp.rs @@ -44,7 +44,7 @@ impl fmt::Display for Message { Message::MembershipReportV2 => write!(f, "version 2 membership report"), Message::LeaveGroup => write!(f, "leave group"), Message::MembershipReportV1 => write!(f, "version 1 membership report"), - Message::Unknown(id) => write!(f, "{}", id), + Message::Unknown(id) => write!(f, "{id}"), } } } @@ -324,8 +324,8 @@ const fn duration_to_max_resp_code(duration: Duration) -> u8 { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), - Err(err) => write!(f, "IGMP ({})", err), + Ok(repr) => write!(f, "{repr}"), + Err(err) => write!(f, "IGMP ({err})"), } } } @@ -339,19 +339,17 @@ impl fmt::Display for Repr { version, } => write!( f, - "IGMP membership query max_resp_time={} group_addr={} version={:?}", - max_resp_time, group_addr, version + "IGMP membership query max_resp_time={max_resp_time} group_addr={group_addr} version={version:?}" ), Repr::MembershipReport { group_addr, version, } => write!( f, - "IGMP membership report group_addr={} version={:?}", - group_addr, version + "IGMP membership report group_addr={group_addr} version={version:?}" ), Repr::LeaveGroup { group_addr } => { - write!(f, "IGMP leave group group_addr={})", group_addr) + write!(f, "IGMP leave group group_addr={group_addr})") } } } @@ -366,8 +364,8 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => writeln!(f, "{}({})", indent, err), - Ok(packet) => writeln!(f, "{}{}", indent, packet), + Err(err) => writeln!(f, "{indent}({err})"), + Ok(packet) => writeln!(f, "{indent}{packet}"), } } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index b738b93cc..fa1894316 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -74,7 +74,7 @@ impl fmt::Display for Protocol { Protocol::Icmpv6 => write!(f, "ICMPv6"), Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"), Protocol::Ipv6Opts => write!(f, "IPv6-Opts"), - Protocol::Unknown(id) => write!(f, "0x{:02x}", id), + Protocol::Unknown(id) => write!(f, "0x{id:02x}"), } } } @@ -245,9 +245,9 @@ impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { #[cfg(feature = "proto-ipv4")] - Address::Ipv4(addr) => write!(f, "{}", addr), + Address::Ipv4(addr) => write!(f, "{addr}"), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(addr) => write!(f, "{}", addr), + Address::Ipv6(addr) => write!(f, "{addr}"), } } } @@ -313,9 +313,9 @@ impl Cidr { pub fn contains_addr(&self, addr: &Address) -> bool { match (self, addr) { #[cfg(feature = "proto-ipv4")] - (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr), + (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr), #[cfg(feature = "proto-ipv6")] - (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr), + (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr), #[allow(unreachable_patterns)] _ => false, } @@ -326,9 +326,9 @@ impl Cidr { pub fn contains_subnet(&self, subnet: &Cidr) -> bool { match (self, subnet) { #[cfg(feature = "proto-ipv4")] - (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other), + (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other), #[cfg(feature = "proto-ipv6")] - (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other), + (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other), #[allow(unreachable_patterns)] _ => false, } @@ -353,9 +353,9 @@ impl fmt::Display for Cidr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { #[cfg(feature = "proto-ipv4")] - Cidr::Ipv4(cidr) => write!(f, "{}", cidr), + Cidr::Ipv4(cidr) => write!(f, "{cidr}"), #[cfg(feature = "proto-ipv6")] - Cidr::Ipv6(cidr) => write!(f, "{}", cidr), + Cidr::Ipv6(cidr) => write!(f, "{cidr}"), } } } @@ -826,7 +826,7 @@ pub fn pretty_print_ip_payload>( Protocol::Udp => { indent.increase(f)?; match UdpPacket::<&[u8]>::new_checked(payload) { - Err(err) => write!(f, "{}({})", indent, err), + Err(err) => write!(f, "{indent}({err})"), Ok(udp_packet) => { match UdpRepr::parse( &udp_packet, @@ -834,7 +834,7 @@ pub fn pretty_print_ip_payload>( &repr.dst_addr(), &checksum_caps, ) { - Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err), + Err(err) => write!(f, "{indent}{udp_packet} ({err})"), Ok(udp_repr) => { write!( f, @@ -854,7 +854,7 @@ pub fn pretty_print_ip_payload>( Protocol::Tcp => { indent.increase(f)?; match TcpPacket::<&[u8]>::new_checked(payload) { - Err(err) => write!(f, "{}({})", indent, err), + Err(err) => write!(f, "{indent}({err})"), Ok(tcp_packet) => { match TcpRepr::parse( &tcp_packet, @@ -862,9 +862,9 @@ pub fn pretty_print_ip_payload>( &repr.dst_addr(), &checksum_caps, ) { - Err(err) => write!(f, "{}{} ({})", indent, tcp_packet, err), + Err(err) => write!(f, "{indent}{tcp_packet} ({err})"), Ok(tcp_repr) => { - write!(f, "{}{}", indent, tcp_repr)?; + write!(f, "{indent}{tcp_repr}")?; let valid = tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr()); format_checksum(f, valid) diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index f49e6ab49..cdebe7b39 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -702,9 +702,9 @@ impl Repr { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self, &ChecksumCapabilities::ignored()) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv4 ({})", err)?; + write!(f, "IPv4 ({err})")?; write!( f, " src={} dst={} proto={} hop_limit={}", @@ -767,7 +767,7 @@ impl> PrettyPrint for Packet { let checksum_caps = ChecksumCapabilities::ignored(); let (ip_repr, payload) = match Packet::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), + Err(err) => return write!(f, "{indent}({err})"), Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) { Err(_) => return Ok(()), Ok(ip_repr) => { @@ -781,7 +781,7 @@ impl> PrettyPrint for Packet { )?; return Ok(()); } else { - write!(f, "{}{}", indent, ip_repr)?; + write!(f, "{indent}{ip_repr}")?; format_checksum(f, ip_packet.verify_checksum())?; (ip_repr, ip_packet.payload()) } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 2f7b00e09..47baf5a4b 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -286,17 +286,17 @@ impl fmt::Display for Address { // When the state is Head or Tail write a u16 in hexadecimal // without the leading colon if the value is not 0. (_, &State::Head) => { - write!(f, "{:x}", word)?; + write!(f, "{word:x}")?; State::HeadBody } (_, &State::Tail) => { - write!(f, "{:x}", word)?; + write!(f, "{word:x}")?; State::TailBody } // Write the u16 with a leading colon when parsing a value // that isn't the first in a section (_, &State::HeadBody) | (_, &State::TailBody) => { - write!(f, ":{:x}", word)?; + write!(f, ":{word:x}")?; state } } @@ -638,9 +638,9 @@ impl + AsMut<[u8]>> Packet { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv6 ({})", err)?; + write!(f, "IPv6 ({err})")?; Ok(()) } } @@ -728,11 +728,11 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { let (ip_repr, payload) = match Packet::new_checked(buffer) { - Err(err) => return write!(f, "{}({})", indent, err), + Err(err) => return write!(f, "{indent}({err})"), Ok(ip_packet) => match Repr::parse(&ip_packet) { Err(_) => return Ok(()), Ok(ip_repr) => { - write!(f, "{}{}", indent, ip_repr)?; + write!(f, "{indent}{ip_repr}")?; (ip_repr, ip_packet.payload()) } }, diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index a6d9d5063..d8bfe0337 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -148,9 +148,9 @@ impl + AsMut<[u8]>> Header { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv6 Fragment ({})", err)?; + write!(f, "IPv6 Fragment ({err})")?; Ok(()) } } diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index b350ba822..96b81b231 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -147,9 +147,9 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv6 Hop-by-Hop Options ({})", err)?; + write!(f, "IPv6 Hop-by-Hop Options ({err})")?; Ok(()) } } diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 17dfea7f0..f3fb6dfbd 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -16,7 +16,7 @@ impl fmt::Display for Type { match *self { Type::Pad1 => write!(f, "Pad1"), Type::PadN => write!(f, "PadN"), - Type::Unknown(id) => write!(f, "{}", id), + Type::Unknown(id) => write!(f, "{id}"), } } } @@ -44,7 +44,7 @@ impl fmt::Display for FailureType { FailureType::Discard => write!(f, "discard"), FailureType::DiscardSendAll => write!(f, "discard and send error"), FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"), - FailureType::Unknown(id) => write!(f, "Unknown({})", id), + FailureType::Unknown(id) => write!(f, "Unknown({id})"), } } } @@ -204,9 +204,9 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Ipv6Option<&'a mut T> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv6 Extension Option ({})", err)?; + write!(f, "IPv6 Extension Option ({err})")?; Ok(()) } } @@ -344,7 +344,7 @@ impl<'a> fmt::Display for Repr<'a> { match *self { Repr::Pad1 => write!(f, "{} ", Type::Pad1), Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len), - Repr::Unknown { type_, length, .. } => write!(f, "{} length={} ", type_, length), + Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "), } } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 1842c6f26..1626ddb2f 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -44,7 +44,7 @@ impl fmt::Display for Type { Type::Experiment1 => write!(f, "Experiment1"), Type::Experiment2 => write!(f, "Experiment2"), Type::Reserved => write!(f, "Reserved"), - Type::Unknown(id) => write!(f, "{}", id), + Type::Unknown(id) => write!(f, "{id}"), } } } @@ -385,9 +385,9 @@ impl + AsMut<[u8]>> Header { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "IPv6 Routing ({})", err)?; + write!(f, "IPv6 Routing ({err})")?; Ok(()) } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index c0684a9f1..9e8a52972 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -319,9 +319,9 @@ impl core::fmt::Display for HardwareAddress { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { #[cfg(feature = "medium-ethernet")] - HardwareAddress::Ethernet(addr) => write!(f, "{}", addr), + HardwareAddress::Ethernet(addr) => write!(f, "{addr}"), #[cfg(feature = "medium-ieee802154")] - HardwareAddress::Ieee802154(addr) => write!(f, "{}", addr), + HardwareAddress::Ieee802154(addr) => write!(f, "{addr}"), } } } @@ -413,7 +413,7 @@ impl core::fmt::Display for RawHardwareAddress { if i != 0 { write!(f, ":")?; } - write!(f, "{:02x}", b)?; + write!(f, "{b:02x}")?; } Ok(()) } diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index f56d3bb2e..e72c5a619 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -32,7 +32,7 @@ impl fmt::Display for Type { Type::PrefixInformation => write!(f, "prefix information"), Type::RedirectedHeader => write!(f, "redirected header"), Type::Mtu => write!(f, "mtu"), - Type::Unknown(id) => write!(f, "{}", id), + Type::Unknown(id) => write!(f, "{id}"), } } } @@ -383,9 +383,9 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match Repr::parse(self) { - Ok(repr) => write!(f, "{}", repr), + Ok(repr) => write!(f, "{repr}"), Err(err) => { - write!(f, "NDISC Option ({})", err)?; + write!(f, "NDISC Option ({err})")?; Ok(()) } } @@ -583,26 +583,26 @@ impl<'a> fmt::Display for Repr<'a> { write!(f, "NDISC Option: ")?; match *self { Repr::SourceLinkLayerAddr(addr) => { - write!(f, "SourceLinkLayer addr={}", addr) + write!(f, "SourceLinkLayer addr={addr}") } Repr::TargetLinkLayerAddr(addr) => { - write!(f, "TargetLinkLayer addr={}", addr) + write!(f, "TargetLinkLayer addr={addr}") } Repr::PrefixInformation(PrefixInformation { prefix, prefix_len, .. }) => { - write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) + write!(f, "PrefixInformation prefix={prefix}/{prefix_len}") } Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { - write!(f, "RedirectedHeader header={}", header) + write!(f, "RedirectedHeader header={header}") } Repr::Mtu(mtu) => { - write!(f, "MTU mtu={}", mtu) + write!(f, "MTU mtu={mtu}") } Repr::Unknown { type_: id, length, .. } => { - write!(f, "Unknown({}) length={}", id, length) + write!(f, "Unknown({id}) length={length}") } } } @@ -617,11 +617,11 @@ impl> PrettyPrint for NdiscOption { indent: &mut PrettyIndent, ) -> fmt::Result { match NdiscOption::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), + Err(err) => write!(f, "{indent}({err})"), Ok(ndisc) => match Repr::parse(&ndisc) { Err(_) => Ok(()), Ok(repr) => { - write!(f, "{}{}", indent, repr) + write!(f, "{indent}{repr}") } }, } diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index ce59df27b..fff61bd1e 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -1024,16 +1024,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { while !options.is_empty() { let (next_options, option) = match TcpOption::parse(options) { Ok(res) => res, - Err(err) => return write!(f, " ({})", err), + Err(err) => return write!(f, " ({err})"), }; match option { TcpOption::EndOfList => break, TcpOption::NoOperation => (), - TcpOption::MaxSegmentSize(value) => write!(f, " mss={}", value)?, - TcpOption::WindowScale(value) => write!(f, " ws={}", value)?, + TcpOption::MaxSegmentSize(value) => write!(f, " mss={value}")?, + TcpOption::WindowScale(value) => write!(f, " ws={value}")?, TcpOption::SackPermitted => write!(f, " sACK")?, - TcpOption::SackRange(slice) => write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s - TcpOption::Unknown { kind, .. } => write!(f, " opt({})", kind)?, + TcpOption::SackRange(slice) => write!(f, " sACKr{slice:?}")?, // debug print conveniently includes the []s + TcpOption::Unknown { kind, .. } => write!(f, " opt({kind})")?, } options = next_options; } @@ -1053,12 +1053,12 @@ impl<'a> fmt::Display for Repr<'a> { } write!(f, " seq={}", self.seq_number)?; if let Some(ack_number) = self.ack_number { - write!(f, " ack={}", ack_number)?; + write!(f, " ack={ack_number}")?; } write!(f, " win={}", self.window_len)?; write!(f, " len={}", self.payload.len())?; if let Some(max_seg_size) = self.max_seg_size { - write!(f, " mss={}", max_seg_size)?; + write!(f, " mss={max_seg_size}")?; } Ok(()) } @@ -1073,8 +1073,8 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet), + Err(err) => write!(f, "{indent}({err})"), + Ok(packet) => write!(f, "{indent}{packet}"), } } } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 0a7bc49cd..c19db05ff 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -305,8 +305,8 @@ impl> PrettyPrint for Packet { indent: &mut PrettyIndent, ) -> fmt::Result { match Packet::new_checked(buffer) { - Err(err) => write!(f, "{}({})", indent, err), - Ok(packet) => write!(f, "{}{}", indent, packet), + Err(err) => write!(f, "{indent}({err})"), + Ok(packet) => write!(f, "{indent}{packet}"), } } } From 38094d9c69feb6c51840527ac5457fb6cdafc419 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Jan 2023 00:19:59 +0100 Subject: [PATCH 462/566] Switch to Rust 2021. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c5395b15f..289600449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "smoltcp" version = "0.8.1" -edition = "2018" +edition = "2021" rust-version = "1.65" authors = ["whitequark "] description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap." From 3a080d818bf6f65e42c3716644d3dafd291b57c4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Jan 2023 00:20:05 +0100 Subject: [PATCH 463/566] Clippy fixes --- examples/benchmark.rs | 2 +- examples/dns.rs | 4 ++-- examples/multicast.rs | 8 ++++---- examples/ping.rs | 4 ++-- examples/sixlowpan_benchmark.rs | 4 ++-- examples/utils.rs | 4 ++-- src/iface/interface/mod.rs | 2 +- src/wire/icmpv6.rs | 2 +- src/wire/ieee802154.rs | 2 +- src/wire/ip.rs | 7 ++----- src/wire/ipv6.rs | 2 +- src/wire/ipv6option.rs | 2 +- utils/packet2pcap.rs | 6 +++--- 13 files changed, 23 insertions(+), 26 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index e7220d4a5..ad3e6dfcd 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -46,7 +46,7 @@ fn client(kind: Client) { // print!("(P:{})", result); processed += result } - Err(err) => panic!("cannot process: {}", err), + Err(err) => panic!("cannot process: {err}"), } } diff --git a/examples/dns.rs b/examples/dns.rs index 3e9cae034..40aa785a2 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -89,11 +89,11 @@ fn main() { .get_query_result(query) { Ok(addrs) => { - println!("Query done: {:?}", addrs); + println!("Query done: {addrs:?}"); break; } Err(GetQueryResultError::Pending) => {} // not done yet - Err(e) => panic!("query failed: {:?}", e), + Err(e) => panic!("query failed: {e:?}"), } phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); diff --git a/examples/multicast.rs b/examples/multicast.rs index ab46e8508..11c90c48a 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -83,13 +83,13 @@ fn main() { // For display purposes only - normally we wouldn't process incoming IGMP packets // in the application layer match socket.recv() { - Err(e) => println!("Recv IGMP error: {:?}", e), + Err(e) => println!("Recv IGMP error: {e:?}"), Ok(buf) => { Ipv4Packet::new_checked(buf) .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) - .map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr)) - .unwrap_or_else(|e| println!("parse IGMP error: {:?}", e)); + .map(|igmp_repr| println!("IGMP packet: {igmp_repr:?}")) + .unwrap_or_else(|e| println!("parse IGMP error: {e:?}")); } } } @@ -105,7 +105,7 @@ fn main() { .map(|(data, sender)| { println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender) }) - .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); + .unwrap_or_else(|e| println!("Recv UDP error: {e:?}")); } phy_wait(fd, iface.poll_delay(timestamp, &sockets)).expect("wait error"); diff --git a/examples/ping.rs b/examples/ping.rs index 271e29d55..76626d656 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -247,7 +247,7 @@ fn main() { if timestamp - *from < timeout { true } else { - println!("From {} icmp_seq={} timeout", remote_addr, seq); + println!("From {remote_addr} icmp_seq={seq} timeout"); false } }); @@ -269,7 +269,7 @@ fn main() { } } - println!("--- {} ping statistics ---", remote_addr); + println!("--- {remote_addr} ping statistics ---"); println!( "{} packets transmitted, {} received, {:.0}% packet loss", seq_no, diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 53a4d4305..38e5d7a6e 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -65,7 +65,7 @@ use std::thread; use std::fs; fn if_nametoindex(ifname: &str) -> u32 { - let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{}/ifindex", ifname)) + let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{ifname}/ifindex")) .expect("couldn't read interface from \"/sys/devices/virtual/net\"") .replace('\n', ""); contents.parse::().unwrap() @@ -111,7 +111,7 @@ fn client(kind: Client) { // print!("(P:{})", result); processed += result } - Err(err) => panic!("cannot process: {}", err), + Err(err) => panic!("cannot process: {err}"), } } diff --git a/examples/utils.rs b/examples/utils.rs index d2b60cafe..dbe907615 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -26,7 +26,7 @@ where Builder::new() .format(move |buf, record| { let elapsed = since_startup(); - let timestamp = format!("[{}]", elapsed); + let timestamp = format!("[{elapsed}]"); if record.target().starts_with("smoltcp::") { writeln!( buf, @@ -73,7 +73,7 @@ pub fn create_options() -> (Options, Vec<&'static str>) { pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches { match options.parse(env::args().skip(1)) { Err(err) => { - println!("{}", err); + println!("{err}"); process::exit(1) } Ok(matches) => { diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index c6a951497..349fa737d 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1630,7 +1630,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] fn check_hardware_addr(addr: &HardwareAddress) { if !addr.is_unicast() { - panic!("Ethernet address {} is not unicast", addr) + panic!("Ethernet address {addr} is not unicast") } } diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index 86dde1854..fa5416e58 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -418,7 +418,7 @@ impl + AsMut<[u8]>> Packet { let data = self.buffer.as_mut(); NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0); } - ty => panic!("Message type `{}` does not have any reserved fields.", ty), + ty => panic!("Message type `{ty}` does not have any reserved fields."), } } diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 1d3f183b9..2c2b64899 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -872,7 +872,7 @@ mod test { let mut frame = Frame::new_unchecked(&mut buffer[..buffer_len]); repr.emit(&mut frame); - println!("{:2x?}", frame); + println!("{frame:2x?}"); assert_eq!(frame.frame_type(), FrameType::Data); assert!(!frame.security_enabled()); diff --git a/src/wire/ip.rs b/src/wire/ip.rs index fa1894316..e3a3478c5 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -591,7 +591,7 @@ impl Repr { hop_limit, }), #[allow(unreachable_patterns)] - _ => panic!("IP version mismatch: src={:?} dst={:?}", src_addr, dst_addr), + _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"), } } @@ -783,10 +783,7 @@ pub mod checksum { } #[allow(unreachable_patterns)] - _ => panic!( - "Unexpected pseudo header addresses: {}, {}", - src_addr, dst_addr - ), + _ => panic!("Unexpected pseudo header addresses: {src_addr}, {dst_addr}"), } } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 47baf5a4b..8fe8d8133 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -787,7 +787,7 @@ mod test { #[test] fn test_address_format() { assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES)); - assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR)); + assert_eq!("fe80::1", format!("{LINK_LOCAL_ADDR}")); assert_eq!( "fe80::7f00:0:1", format!( diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index f3fb6dfbd..963b066bf 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -534,7 +534,7 @@ mod test { }), ) => continue, (6, Err(Error)) => continue, - (i, res) => panic!("Unexpected option `{:?}` at index {}", res, i), + (i, res) => panic!("Unexpected option `{res:?}` at index {i}"), } } } diff --git a/utils/packet2pcap.rs b/utils/packet2pcap.rs index b76780ea9..7d06c6f16 100644 --- a/utils/packet2pcap.rs +++ b/utils/packet2pcap.rs @@ -24,7 +24,7 @@ fn convert( } fn print_usage(program: &str, opts: Options) { - let brief = format!("Usage: {} [options] INPUT OUTPUT", program); + let brief = format!("Usage: {program} [options] INPUT OUTPUT"); print!("{}", opts.usage(&brief)); } @@ -44,7 +44,7 @@ fn main() { let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(e) => { - eprintln!("{}", e); + eprintln!("{e}"); return; } }; @@ -67,7 +67,7 @@ fn main() { ) { Ok(()) => (), Err(e) => { - eprintln!("Cannot convert packet to pcap: {}", e); + eprintln!("Cannot convert packet to pcap: {e}"); exit(1); } } From 19a0389eb1a84c04af3189c2cd4ffa879d8c8fa5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 22:20:42 +0100 Subject: [PATCH 464/566] assembler: don't return Option. --- src/iface/fragmentation.rs | 2 +- src/socket/tcp.rs | 3 ++- src/storage/assembler.rs | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index d022e1754..96888dc85 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -233,7 +233,7 @@ impl<'a> PacketAssembler<'a> { total_size, .. } => match (total_size, assembler.peek_front()) { - (Some(total_size), Some(front)) => Ok(front == *total_size), + (Some(total_size), front) => Ok(front == *total_size), _ => Ok(false), }, } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 78213b702..05e9cc2a9 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1822,7 +1822,8 @@ impl<'a> Socket<'a> { } } - if let Some(contig_len) = self.assembler.remove_front() { + let contig_len = self.assembler.remove_front(); + if contig_len != 0 { debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Enqueue the contiguous data octets in front of the buffer. tcp_trace!( diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 860b83783..b821c0f6a 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -137,12 +137,12 @@ impl Assembler { } /// Return length of the front contiguous range without removing it from the assembler - pub fn peek_front(&self) -> Option { + pub fn peek_front(&self) -> usize { let front = self.front(); if front.has_hole() { - None + 0 } else { - Some(front.data_size) + front.data_size } } @@ -250,16 +250,16 @@ impl Assembler { /// Remove a contiguous range from the front of the assembler and `Some(data_size)`, /// or return `None` if there is no such range. - pub fn remove_front(&mut self) -> Option { + pub fn remove_front(&mut self) -> usize { let front = self.front(); if front.has_hole() { - None + 0 } else { let last_hole = self.remove_contig_at(0); last_hole.hole_size += front.data_size; debug_assert!(front.data_size > 0); - Some(front.data_size) + front.data_size } } @@ -452,20 +452,20 @@ mod test { #[test] fn test_empty_remove_front() { let mut assr = contigs![(12, 0)]; - assert_eq!(assr.remove_front(), None); + assert_eq!(assr.remove_front(), 0); } #[test] fn test_trailing_hole_remove_front() { let mut assr = contigs![(0, 4), (8, 0)]; - assert_eq!(assr.remove_front(), Some(4)); + assert_eq!(assr.remove_front(), 4); assert_eq!(assr, contigs![(12, 0)]); } #[test] fn test_trailing_data_remove_front() { let mut assr = contigs![(0, 4), (4, 4)]; - assert_eq!(assr.remove_front(), Some(4)); + assert_eq!(assr.remove_front(), 4); assert_eq!(assr, contigs![(4, 4), (4, 0)]); } @@ -474,7 +474,7 @@ mod test { let mut vec = vec![(1, 1); CONTIG_COUNT]; vec[0] = (0, 2); let mut assr = Assembler::from(vec); - assert_eq!(assr.remove_front(), Some(2)); + assert_eq!(assr.remove_front(), 2); let mut vec = vec![(1, 1); CONTIG_COUNT]; vec[CONTIG_COUNT - 1] = (2, 0); let exp_assr = Assembler::from(vec); From 274260caf926903a2558064f7459e5efc1d32141 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Dec 2022 01:03:53 +0100 Subject: [PATCH 465/566] assembler: refactor algorithm to not care about total buffer size. - Assembler no longer needs the total buffer size, added range indices can be as high as needed. - Fixes quadratic run time when adding a range that overlaps with many existing ranges. --- src/iface/fragmentation.rs | 6 +- src/socket/tcp.rs | 18 +- src/storage/assembler.rs | 345 +++++++++++++++++++++++-------------- 3 files changed, 224 insertions(+), 145 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 96888dc85..2025fe9b3 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -93,11 +93,7 @@ impl<'a> PacketAssembler<'a> { } self.assembler = AssemblerState::Assembling { - assembler: Assembler::new(if let Some(total_size) = total_size { - total_size - } else { - usize::MAX - }), + assembler: Assembler::new(), total_size, expires_at, offset_correction, diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 05e9cc2a9..0aa5f4f2b 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -454,7 +454,7 @@ impl<'a> Socket<'a> { state: State::Closed, timer: Timer::new(), rtte: RttEstimator::default(), - assembler: Assembler::new(rx_buffer.capacity()), + assembler: Assembler::new(), tx_buffer, rx_buffer, rx_fin_received: false, @@ -675,7 +675,7 @@ impl<'a> Socket<'a> { self.state = State::Closed; self.timer = Timer::new(); self.rtte = RttEstimator::default(); - self.assembler = Assembler::new(self.rx_buffer.capacity()); + self.assembler = Assembler::new(); self.tx_buffer.clear(); self.rx_buffer.clear(); self.rx_fin_received = false; @@ -1800,7 +1800,6 @@ impl<'a> Socket<'a> { // Try adding payload octets to the assembler. match self.assembler.add(payload_offset, payload_len) { Ok(()) => { - debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Place payload octets into the buffer. tcp_trace!( "rx buffer: receiving {} octets at offset {}", @@ -1824,7 +1823,6 @@ impl<'a> Socket<'a> { let contig_len = self.assembler.remove_front(); if contig_len != 0 { - debug_assert!(self.assembler.total_size() == self.rx_buffer.capacity()); // Enqueue the contiguous data octets in front of the buffer. tcp_trace!( "rx buffer: enqueueing {} octets (now {})", @@ -3699,7 +3697,7 @@ mod test { // Update our scaling parameters for a TCP with a scaled buffer. assert_eq!(s.rx_buffer.len(), 0); s.rx_buffer = SocketBuffer::new(vec![0; 262143]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); s.remote_win_scale = Some(0); s.remote_last_win = 65535; s.remote_win_shift = 2; @@ -5850,7 +5848,7 @@ mod test { fn test_zero_window_ack() { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); send!( s, TcpRepr { @@ -5890,7 +5888,7 @@ mod test { fn test_zero_window_ack_on_window_growth() { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); send!( s, TcpRepr { @@ -5969,7 +5967,7 @@ mod test { fn test_announce_window_after_read() { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); send!( s, TcpRepr { @@ -6382,7 +6380,7 @@ mod test { fn test_buffer_wraparound_rx() { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); send!( s, TcpRepr { @@ -6919,7 +6917,7 @@ mod test { fn test_doesnt_accept_wrong_port() { let mut s = socket_established(); s.rx_buffer = SocketBuffer::new(vec![0; 6]); - s.assembler = Assembler::new(s.rx_buffer.capacity()); + s.assembler = Assembler::new(); let tcp_repr = TcpRepr { seq_number: REMOTE_SEQ + 1, diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index b821c0f6a..9c9493f09 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -34,13 +34,6 @@ impl Contig { } } - fn hole(size: usize) -> Contig { - Contig { - hole_size: size, - data_size: 0, - } - } - fn hole_and_data(hole_size: usize, data_size: usize) -> Contig { Contig { hole_size, @@ -60,14 +53,6 @@ impl Contig { self.hole_size + self.data_size } - fn is_empty(&self) -> bool { - self.total_size() == 0 - } - - fn expand_data_by(&mut self, size: usize) { - self.data_size += size; - } - fn shrink_hole_by(&mut self, size: usize) { self.hole_size -= size; } @@ -105,7 +90,7 @@ impl fmt::Display for Assembler { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "[ ")?; for contig in self.contigs.iter() { - if contig.is_empty() { + if !contig.has_data() { break; } write!(f, "{contig} ")?; @@ -115,23 +100,20 @@ impl fmt::Display for Assembler { } } +// Invariant on Assembler::contigs: +// - There's an index `i` where all contigs before have data, and all contigs after don't (are unused). +// - All contigs with data must have hole_size != 0, except the first. + impl Assembler { - /// Create a new buffer assembler for buffers of the given size. - pub fn new(size: usize) -> Assembler { + /// Create a new buffer assembler. + pub fn new() -> Assembler { #[cfg(not(feature = "alloc"))] - let mut contigs = [Contig::empty(); CONTIG_COUNT]; + let contigs = [Contig::empty(); CONTIG_COUNT]; #[cfg(feature = "alloc")] - let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]); - contigs[0] = Contig::hole(size); + let contigs = Box::new([Contig::empty(); CONTIG_COUNT]); Assembler { contigs } } - /// FIXME(whitequark): remove this once I'm certain enough that the assembler works well. - #[allow(dead_code)] - pub(crate) fn total_size(&self) -> usize { - self.contigs.iter().map(|contig| contig.total_size()).sum() - } - fn front(&self) -> Contig { self.contigs[0] } @@ -155,30 +137,24 @@ impl Assembler { !self.front().has_data() } - /// Remove a contig at the given index, and return a pointer to the first contig - /// without data. - fn remove_contig_at(&mut self, at: usize) -> &mut Contig { - debug_assert!(!self.contigs[at].is_empty()); + /// Remove a contig at the given index. + fn remove_contig_at(&mut self, at: usize) { + debug_assert!(self.contigs[at].has_data()); for i in at..self.contigs.len() - 1 { - self.contigs[i] = self.contigs[i + 1]; if !self.contigs[i].has_data() { - self.contigs[i + 1] = Contig::empty(); - return &mut self.contigs[i]; + return; } + self.contigs[i] = self.contigs[i + 1]; } // Removing the last one. - let p = &mut self.contigs[self.contigs.len() - 1]; - *p = Contig::empty(); - p + self.contigs[self.contigs.len() - 1] = Contig::empty(); } /// Add a contig at the given index, and return a pointer to it. fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> { - debug_assert!(!self.contigs[at].is_empty()); - - if !self.back().is_empty() { + if self.back().has_data() { return Err(TooManyHolesError); } @@ -192,59 +168,82 @@ impl Assembler { /// Add a new contiguous range to the assembler, and return `Ok(bool)`, /// or return `Err(TooManyHolesError)` if too many discontiguities are already recorded. - pub fn add(&mut self, mut offset: usize, mut size: usize) -> Result<(), TooManyHolesError> { - let mut index = 0; - while index != self.contigs.len() && size != 0 { - let contig = self.contigs[index]; - - if offset >= contig.total_size() { - // The range being added does not cover this contig, skip it. - index += 1; - } else if offset == 0 && size >= contig.hole_size && index > 0 { - // The range being added covers the entire hole in this contig, merge it - // into the previous config. - self.contigs[index - 1].expand_data_by(contig.total_size()); - self.remove_contig_at(index); - index += 0; - } else if offset == 0 && size < contig.hole_size && index > 0 { - // The range being added covers a part of the hole in this contig starting - // at the beginning, shrink the hole in this contig and expand data in - // the previous contig. - self.contigs[index - 1].expand_data_by(size); - self.contigs[index].shrink_hole_by(size); - index += 1; - } else if offset <= contig.hole_size && offset + size >= contig.hole_size { - // The range being added covers both a part of the hole and a part of the data - // in this contig, shrink the hole in this contig. - self.contigs[index].shrink_hole_to(offset); - index += 1; - } else if offset + size >= contig.hole_size { - // The range being added covers only a part of the data in this contig, skip it. - index += 1; - } else if offset + size < contig.hole_size { - // The range being added covers a part of the hole but not of the data - // in this contig, add a new contig containing the range. - { - let inserted = self.add_contig_at(index)?; - *inserted = Contig::hole_and_data(offset, size); - } + pub fn add(&mut self, mut offset: usize, size: usize) -> Result<(), TooManyHolesError> { + if size == 0 { + return Ok(()); + } + + let mut i = 0; + + // Find index of the contig containing the start of the range. + loop { + if i == self.contigs.len() { + // The new range is after all the previous ranges, but there/s no space to add it. + return Err(TooManyHolesError); + } + let contig = &mut self.contigs[i]; + if !contig.has_data() { + // The new range is after all the previous ranges. Add it. + *contig = Contig::hole_and_data(offset, size); + return Ok(()); + } + if offset <= contig.total_size() { + break; + } + offset -= contig.total_size(); + i += 1; + } + + let contig = &mut self.contigs[i]; + if offset < contig.hole_size { + // Range starts within the hole. + + if offset + size < contig.hole_size { + // Range also ends within the hole. + let new_contig = self.add_contig_at(i)?; + new_contig.hole_size = offset; + new_contig.data_size = size; + // Previous contigs[index] got moved to contigs[index+1] - self.contigs[index + 1].shrink_hole_by(offset + size); - index += 2; - } else { - unreachable!() + self.contigs[i + 1].shrink_hole_by(offset + size); + return Ok(()); } - // Skip the portion of the range covered by this contig. - if offset >= contig.total_size() { - offset = offset.saturating_sub(contig.total_size()); - } else { - size = (offset + size).saturating_sub(contig.total_size()); - offset = 0; + // The range being added covers both a part of the hole and a part of the data + // in this contig, shrink the hole in this contig. + contig.shrink_hole_to(offset); + } + + // coalesce contigs to the right. + let mut j = i + 1; + while j < self.contigs.len() + && self.contigs[j].has_data() + && offset + size >= self.contigs[i].total_size() + self.contigs[j].hole_size + { + self.contigs[i].data_size += self.contigs[j].total_size(); + j += 1; + } + let shift = j - i - 1; + if shift != 0 { + for x in i + 1..self.contigs.len() - shift { + if !self.contigs[x].has_data() { + break; + } + self.contigs[x] = self.contigs[x + shift]; + } + } + + if offset + size > self.contigs[i].total_size() { + // The added range still extends beyond the current contig. Increase data size. + let left = offset + size - self.contigs[i].total_size(); + self.contigs[i].data_size += left; + + // Decrease hole size of the next, if any. + if i + 1 < self.contigs.len() && self.contigs[i + 1].has_data() { + self.contigs[i + 1].hole_size -= left; } } - debug_assert!(size == 0); Ok(()) } @@ -252,12 +251,10 @@ impl Assembler { /// or return `None` if there is no such range. pub fn remove_front(&mut self) -> usize { let front = self.front(); - if front.has_hole() { + if front.has_hole() || !front.has_data() { 0 } else { - let last_hole = self.remove_contig_at(0); - last_hole.hole_size += front.data_size; - + self.remove_contig_at(0); debug_assert!(front.data_size > 0); front.data_size } @@ -348,99 +345,98 @@ mod test { #[test] fn test_new() { - let assr = Assembler::new(16); - assert_eq!(assr.total_size(), 16); - assert_eq!(assr, contigs![(16, 0)]); + let assr = Assembler::new(); + assert_eq!(assr, contigs![]); } #[test] fn test_empty_add_full() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 16), Ok(())); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_empty_add_front() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 4), Ok(())); - assert_eq!(assr, contigs![(0, 4), (12, 0)]); + assert_eq!(assr, contigs![(0, 4)]); } #[test] fn test_empty_add_back() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(12, 4), Ok(())); assert_eq!(assr, contigs![(12, 4)]); } #[test] fn test_empty_add_mid() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(4, 8), Ok(())); - assert_eq!(assr, contigs![(4, 8), (4, 0)]); + assert_eq!(assr, contigs![(4, 8)]); } #[test] fn test_partial_add_front() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(0, 4), Ok(())); - assert_eq!(assr, contigs![(0, 12), (4, 0)]); + assert_eq!(assr, contigs![(0, 12)]); } #[test] fn test_partial_add_back() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(12, 4), Ok(())); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_front_overlap() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(0, 8), Ok(())); - assert_eq!(assr, contigs![(0, 12), (4, 0)]); + assert_eq!(assr, contigs![(0, 12)]); } #[test] fn test_partial_add_front_overlap_split() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(2, 6), Ok(())); - assert_eq!(assr, contigs![(2, 10), (4, 0)]); + assert_eq!(assr, contigs![(2, 10)]); } #[test] fn test_partial_add_back_overlap() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(8, 8), Ok(())); assert_eq!(assr, contigs![(4, 12)]); } #[test] fn test_partial_add_back_overlap_split() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(10, 4), Ok(())); - assert_eq!(assr, contigs![(4, 10), (2, 0)]); + assert_eq!(assr, contigs![(4, 10)]); } #[test] fn test_partial_add_both_overlap() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(0, 16), Ok(())); assert_eq!(assr, contigs![(0, 16)]); } #[test] fn test_partial_add_both_overlap_split() { - let mut assr = contigs![(4, 8), (4, 0)]; + let mut assr = contigs![(4, 8)]; assert_eq!(assr.add(2, 12), Ok(())); - assert_eq!(assr, contigs![(2, 12), (2, 0)]); + assert_eq!(assr, contigs![(2, 12)]); } #[test] fn test_rejected_add_keeps_state() { - let mut assr = Assembler::new(CONTIG_COUNT * 20); - for c in 1..=CONTIG_COUNT - 1 { + let mut assr = Assembler::new(); + for c in 1..=CONTIG_COUNT { assert_eq!(assr.add(c * 10, 3), Ok(())); } // Maximum of allowed holes is reached @@ -451,22 +447,22 @@ mod test { #[test] fn test_empty_remove_front() { - let mut assr = contigs![(12, 0)]; + let mut assr = contigs![]; assert_eq!(assr.remove_front(), 0); } #[test] fn test_trailing_hole_remove_front() { - let mut assr = contigs![(0, 4), (8, 0)]; + let mut assr = contigs![(0, 4)]; assert_eq!(assr.remove_front(), 4); - assert_eq!(assr, contigs![(12, 0)]); + assert_eq!(assr, contigs![]); } #[test] fn test_trailing_data_remove_front() { let mut assr = contigs![(0, 4), (4, 4)]; assert_eq!(assr.remove_front(), 4); - assert_eq!(assr, contigs![(4, 4), (4, 0)]); + assert_eq!(assr, contigs![(4, 4)]); } #[test] @@ -476,21 +472,57 @@ mod test { let mut assr = Assembler::from(vec); assert_eq!(assr.remove_front(), 2); let mut vec = vec![(1, 1); CONTIG_COUNT]; - vec[CONTIG_COUNT - 1] = (2, 0); + vec[CONTIG_COUNT - 1] = (0, 0); let exp_assr = Assembler::from(vec); assert_eq!(assr, exp_assr); } + #[test] + fn test_shrink_next_hole() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(100, 10), Ok(())); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add(40, 30), Ok(())); + assert_eq!(assr, contigs![(40, 30), (30, 10)]); + } + + #[test] + fn test_join_two() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(10, 10), Ok(())); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add(15, 40), Ok(())); + assert_eq!(assr, contigs![(10, 50)]); + } + + #[test] + fn test_join_two_reversed() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add(10, 10), Ok(())); + assert_eq!(assr.add(15, 40), Ok(())); + assert_eq!(assr, contigs![(10, 50)]); + } + + #[test] + fn test_join_two_overlong() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add(10, 10), Ok(())); + assert_eq!(assr.add(15, 60), Ok(())); + assert_eq!(assr, contigs![(10, 65)]); + } + #[test] fn test_iter_empty() { - let assr = Assembler::new(16); + let assr = Assembler::new(); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![]); } #[test] fn test_iter_full() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 16), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 26)]); @@ -498,7 +530,7 @@ mod test { #[test] fn test_iter_offset() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 16), Ok(())); let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(100, 116)]); @@ -506,7 +538,7 @@ mod test { #[test] fn test_iter_one_front() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 4), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(10, 14)]); @@ -514,7 +546,7 @@ mod test { #[test] fn test_iter_one_back() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(12, 4), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(22, 26)]); @@ -522,7 +554,7 @@ mod test { #[test] fn test_iter_one_mid() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(4, 8), Ok(())); let segments: Vec<_> = assr.iter_data(10).collect(); assert_eq!(segments, vec![(14, 22)]); @@ -530,30 +562,83 @@ mod test { #[test] fn test_iter_one_trailing_gap() { - let assr = contigs![(4, 8), (4, 0)]; + let assr = contigs![(4, 8)]; let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(104, 112)]); } #[test] fn test_iter_two_split() { - let assr = contigs![(2, 6), (4, 1), (1, 0)]; + let assr = contigs![(2, 6), (4, 1)]; let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(102, 108), (112, 113)]); } #[test] fn test_iter_three_split() { - let assr = contigs![(2, 6), (2, 1), (2, 2), (1, 0)]; + let assr = contigs![(2, 6), (2, 1), (2, 2)]; let segments: Vec<_> = assr.iter_data(100).collect(); assert_eq!(segments, vec![(102, 108), (110, 111), (113, 115)]); } #[test] fn test_issue_694() { - let mut assr = Assembler::new(16); + let mut assr = Assembler::new(); assert_eq!(assr.add(0, 1), Ok(())); assert_eq!(assr.add(2, 1), Ok(())); assert_eq!(assr.add(1, 1), Ok(())); } + + // Test against an obviously-correct but inefficient bitmap impl. + #[test] + fn test_random() { + use rand::Rng; + + for _ in 0..1000 { + println!("==="); + let mut assr = Assembler::new(); + let mut map = [false; 128]; + + for _ in 0..30 { + let offset = rand::thread_rng().gen_range(0..80); + let size = rand::thread_rng().gen_range(0..47); + + println!("add {}..{} {}", offset, offset + size, size); + // Real impl + let res = assr.add(offset, size); + + // Bitmap impl + let mut map2 = map; + map2[offset..][..size].fill(true); + + let mut contigs = vec![]; + let mut hole: usize = 0; + let mut data: usize = 0; + for b in map2 { + if b { + data += 1; + } else { + if data != 0 { + contigs.push((hole, data)); + hole = 0; + data = 0; + } + hole += 1; + } + } + + // Compare. + let wanted_res = if contigs.len() > CONTIG_COUNT { + Err(TooManyHolesError) + } else { + Ok(()) + }; + assert_eq!(res, wanted_res); + if res.is_ok() { + map = map2; + assert_eq!(assr, Assembler::from(contigs)); + } + } + } + } } From 027ec16ef0ec79d0beb45767b2ecbd50a4e63468 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Dec 2022 01:13:19 +0100 Subject: [PATCH 466/566] packet assembler: remove NotInit state. --- src/iface/fragmentation.rs | 264 ++++++++++--------------------- src/iface/interface/sixlowpan.rs | 2 +- src/lib.rs | 4 - 3 files changed, 88 insertions(+), 182 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 2025fe9b3..66afcf82d 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -17,37 +17,11 @@ use crate::Result; #[derive(Debug)] pub struct PacketAssembler<'a> { buffer: ManagedSlice<'a, u8>, - assembler: AssemblerState, -} - -/// Holds the state of the assembling of one packet. -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, PartialEq)] -enum AssemblerState { - NotInit, - Assembling { - assembler: Assembler, - total_size: Option, - expires_at: Instant, - offset_correction: isize, - }, -} -impl fmt::Display for AssemblerState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - AssemblerState::NotInit => write!(f, "Not init")?, - AssemblerState::Assembling { - assembler, - total_size, - expires_at, - offset_correction, - } => { - write!(f, "{assembler} expires at {expires_at}")?; - } - } - Ok(()) - } + assembler: Assembler, + total_size: Option, + expires_at: Instant, + offset_correction: isize, } impl<'a> PacketAssembler<'a> { @@ -59,10 +33,21 @@ impl<'a> PacketAssembler<'a> { let s = storage.into(); PacketAssembler { buffer: s, - assembler: AssemblerState::NotInit, + + assembler: Assembler::new(), + total_size: None, + expires_at: Instant::ZERO, + offset_correction: 0, } } + pub(crate) fn reset(&mut self) { + self.assembler = Assembler::new(); + self.total_size = None; + self.expires_at = Instant::ZERO; + self.offset_correction = 0; + } + /// Start with saving fragments. /// We initialize the assembler with the total size of the final packet. /// @@ -76,143 +61,102 @@ impl<'a> PacketAssembler<'a> { expires_at: Instant, offset_correction: isize, ) -> Result<()> { - match &mut self.buffer { - ManagedSlice::Borrowed(b) => { - if let Some(total_size) = total_size { - if b.len() < total_size { - return Err(Error::PacketAssemblerBufferTooSmall); - } - } - } - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => { - if let Some(total_size) = total_size { - b.resize(total_size, 0); - } - } + self.reset(); + if let Some(total_size) = total_size { + self.set_total_size(total_size)?; } - - self.assembler = AssemblerState::Assembling { - assembler: Assembler::new(), - total_size, - expires_at, - offset_correction, - }; - + self.expires_at = expires_at; + self.offset_correction = offset_correction; Ok(()) } /// Set the total size of the packet assembler. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the - /// assembler with [Self::start]). pub(crate) fn set_total_size(&mut self, size: usize) -> Result<()> { - match self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { - ref mut total_size, .. - } => { - *total_size = Some(size); - Ok(()) + if let Some(old_size) = self.total_size { + if old_size != size { + return Err(Error::Malformed); } } + + match &mut self.buffer { + ManagedSlice::Borrowed(b) => { + if b.len() < size { + return Err(Error::PacketAssemblerBufferTooSmall); + } + } + #[cfg(feature = "alloc")] + ManagedSlice::Owned(b) => b.resize(size, 0), + } + + self.total_size = Some(size); + Ok(()) } /// Return the instant when the assembler expires. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the - /// assembler with [Self::start]). pub(crate) fn expires_at(&self) -> Result { - match self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { expires_at, .. } => Ok(expires_at), - } + Ok(self.expires_at) } /// Add a fragment into the packet that is being reassembled. /// /// # Errors /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the - /// assembler with [Self::start]). /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// place. /// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data. pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result { - match self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { - ref mut assembler, - total_size, - offset_correction, - .. - } => { - let offset = offset as isize + offset_correction; - let offset = if offset <= 0 { 0 } else { offset as usize }; - - match &mut self.buffer { - ManagedSlice::Borrowed(b) => { - if offset + data.len() > b.len() { - return Err(Error::PacketAssemblerBufferTooSmall); - } - } - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => { - if offset + data.len() > b.len() { - b.resize(offset + data.len(), 0); - } - } - } + let offset = offset as isize + self.offset_correction; + let offset = if offset <= 0 { 0 } else { offset as usize }; - let len = data.len(); - self.buffer[offset..][..len].copy_from_slice(data); - - net_debug!( - "frag assembler: receiving {} octests at offset {}", - len, - offset - ); - - match assembler.add(offset, data.len()) { - Ok(()) => { - net_debug!("assembler: {}", self.assembler); - self.is_complete() - } - // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? - Err(_) => Err(Error::PacketAssemblerTooManyHoles), + match &mut self.buffer { + ManagedSlice::Borrowed(b) => { + if offset + data.len() > b.len() { + return Err(Error::PacketAssemblerBufferTooSmall); + } + } + #[cfg(feature = "alloc")] + ManagedSlice::Owned(b) => { + if offset + data.len() > b.len() { + b.resize(offset + data.len(), 0); } } } + + let len = data.len(); + self.buffer[offset..][..len].copy_from_slice(data); + + net_debug!( + "frag assembler: receiving {} octests at offset {}", + len, + offset + ); + + match self.assembler.add(offset, data.len()) { + Ok(()) => { + net_debug!("assembler: {}", self.assembler); + self.is_complete() + } + // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? + Err(_) => Err(Error::PacketAssemblerTooManyHoles), + } } /// Get an immutable slice of the underlying packet data. - /// This will mark the assembler state as [`AssemblerState::NotInit`] such that it can be reused. + /// This will mark the assembler as empty, so that it can be reused. /// /// # Errors /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the - /// assembler with [`Self::start`]). /// - Returns [`Error::PacketAssemblerIncomplete`] when not all the fragments have been collected. pub(crate) fn assemble(&mut self) -> Result<&'_ [u8]> { - let b = match self.assembler { - AssemblerState::NotInit => return Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { total_size, .. } => { - if self.is_complete()? { - // NOTE: we can unwrap because `is_complete` already checks this. - let total_size = total_size.unwrap(); - let a = &self.buffer[..total_size]; - self.assembler = AssemblerState::NotInit; - a - } else { - return Err(Error::PacketAssemblerIncomplete); - } - } - }; - Ok(b) + if !self.is_complete()? { + return Err(Error::PacketAssemblerIncomplete); + } + + // NOTE: we can unwrap because `is_complete` already checks this. + let total_size = self.total_size.unwrap(); + let a = &self.buffer[..total_size]; + + Ok(a) } /// Returns `true` when all fragments have been received, otherwise `false`. @@ -222,33 +166,15 @@ impl<'a> PacketAssembler<'a> { /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the /// assembler with [`Self::start`]). pub(crate) fn is_complete(&self) -> Result { - match &self.assembler { - AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), - AssemblerState::Assembling { - assembler, - total_size, - .. - } => match (total_size, assembler.peek_front()) { - (Some(total_size), front) => Ok(front == *total_size), - _ => Ok(false), - }, + match (self.total_size, self.assembler.peek_front()) { + (Some(total_size), front) => Ok(front == total_size), + _ => Ok(false), } } - /// Returns `true` when the packet assembler is free to use. - fn is_free(&self) -> bool { - self.assembler == AssemblerState::NotInit - } - - /// Mark this assembler as [`AssemblerState::NotInit`]. - /// This is then cleaned up by the [`PacketAssemblerSet`]. - pub fn mark_discarded(&mut self) { - self.assembler = AssemblerState::NotInit; - } - - /// Returns `true` when the [`AssemblerState`] is discarded. - pub fn is_discarded(&self) -> bool { - matches!(self.assembler, AssemblerState::NotInit) + /// Returns `true` when the packet assembler is empty (free to use). + fn is_empty(&self) -> bool { + self.assembler.is_empty() } } @@ -349,7 +275,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { self.packet_buffer .iter() .enumerate() - .find(|(_, b)| b.is_free()) + .find(|(_, b)| b.is_empty()) .map(|(i, _)| i) } @@ -388,7 +314,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { loop { let mut key = None; for (k, i) in self.index_buffer.iter() { - if matches!(self.packet_buffer[*i].assembler, AssemblerState::NotInit) { + if self.packet_buffer[*i].is_empty() { key = Some(*k); break; } @@ -411,7 +337,7 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { for (_, i) in &mut self.index_buffer.iter() { let frag = &mut self.packet_buffer[*i]; if f(frag)? { - frag.mark_discarded(); + frag.reset(); } } @@ -439,22 +365,6 @@ mod tests { id: usize, } - #[test] - fn packet_assembler_not_init() { - let mut p_assembler = PacketAssembler::new(vec![]); - let data = b"Hello World!"; - assert_eq!( - p_assembler.add(&data[..], data.len()), - Err(Error::PacketAssemblerNotInit) - ); - - assert_eq!( - p_assembler.is_complete(), - Err(Error::PacketAssemblerNotInit) - ); - assert_eq!(p_assembler.assemble(), Err(Error::PacketAssemblerNotInit)); - } - #[test] fn packet_assembler_buffer_too_small() { let mut storage = [0u8; 1]; diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index d5dce356a..d30330e72 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -274,7 +274,7 @@ impl<'a> InterfaceInner<'a> { Ok(false) => None, Err(Error::PacketAssemblerOverlap) => { net_trace!("6LoWPAN: overlap in packet"); - frags.mark_discarded(); + frags.reset(); None } Err(_) => None, diff --git a/src/lib.rs b/src/lib.rs index 204139f28..7fdad70f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,9 +184,6 @@ pub enum Error { /// An incoming fragment arrived too late. ReassemblyTimeout, - /// The packet assembler is not initialized, thus it cannot know what the final size of the - /// packet would be. - PacketAssemblerNotInit, /// The buffer of the assembler is to small and thus the final packet wont fit into it. PacketAssemblerBufferTooSmall, /// The packet assembler did not receive all the fragments for assembling the final packet. @@ -226,7 +223,6 @@ impl fmt::Display for Error { Error::Malformed => write!(f, "malformed packet"), Error::Dropped => write!(f, "dropped by socket"), Error::ReassemblyTimeout => write!(f, "incoming fragment arrived too late"), - Error::PacketAssemblerNotInit => write!(f, "packet assembler was not initialized"), Error::PacketAssemblerBufferTooSmall => { write!(f, "packet assembler buffer too small for final packet") } From 40269822a345f933afba4915c7a8b1a01ca21389 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Dec 2022 01:31:27 +0100 Subject: [PATCH 467/566] packet assembler: remove unused Error::PacketAssemblerOverlap --- src/iface/fragmentation.rs | 1 - src/iface/interface/ipv4.rs | 11 ++--------- src/iface/interface/sixlowpan.rs | 5 ----- src/lib.rs | 5 ----- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 66afcf82d..f1d1fdc68 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -103,7 +103,6 @@ impl<'a> PacketAssembler<'a> { /// /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// place. - /// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data. pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result { let offset = offset as isize + self.offset_correction; let offset = if offset <= 0 { 0 } else { offset as usize }; diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index e4e7d044d..b95e5b1e5 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -39,7 +39,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] let ip_payload = { - const REASSEMBLY_TIMEOUT: u64 = 90; + const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(90); let fragments = _fragments.unwrap(); @@ -58,11 +58,7 @@ impl<'a> InterfaceInner<'a> { e => check!(e), }; - check!(p.start( - None, - self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), - 0 - )); + check!(p.start(None, self.now + REASSEMBLY_TIMEOUT, 0)); check!(fragments.get_packet_assembler_mut(&key)) } @@ -86,9 +82,6 @@ impl<'a> InterfaceInner<'a> { Ok(false) => { return None; } - Err(Error::PacketAssemblerOverlap) => { - return None; - } Err(e) => { net_debug!("fragmentation error: {}", e); return None; diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index d30330e72..084685711 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -272,11 +272,6 @@ impl<'a> InterfaceInner<'a> { } } Ok(false) => None, - Err(Error::PacketAssemblerOverlap) => { - net_trace!("6LoWPAN: overlap in packet"); - frags.reset(); - None - } Err(_) => None, } } diff --git a/src/lib.rs b/src/lib.rs index 7fdad70f0..4640dd804 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -190,8 +190,6 @@ pub enum Error { PacketAssemblerIncomplete, /// There are too many holes in the packet assembler (should be fixed in the future?). PacketAssemblerTooManyHoles, - /// There was an overlap when adding data to the packet assembler. - PacketAssemblerOverlap, /// The packet assembler set has no place for assembling a new stream of fragments. PacketAssemblerSetFull, @@ -231,9 +229,6 @@ impl fmt::Display for Error { f, "packet assembler has too many holes (internal smoltcp error)" ), - Error::PacketAssemblerOverlap => { - write!(f, "overlap when adding data to packet assembler") - } Error::PacketAssemblerSetFull => write!(f, "packet assembler set is full"), Error::PacketAssemblerSetKeyNotFound => { write!(f, "packet assembler set does not find key") From af115a57691af82feb8700aeb83a506b391706d6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Dec 2022 03:38:32 +0100 Subject: [PATCH 468/566] packet assembler: simplify data structure and api. --- examples/client.rs | 33 +-- examples/server.rs | 20 +- examples/sixlowpan.rs | 14 +- examples/sixlowpan_benchmark.rs | 6 +- src/iface/fragmentation.rs | 488 ++++++++++--------------------- src/iface/interface/ethernet.rs | 2 +- src/iface/interface/ipv4.rs | 43 +-- src/iface/interface/mod.rs | 49 +--- src/iface/interface/sixlowpan.rs | 72 ++--- src/iface/interface/tests.rs | 17 +- src/lib.rs | 24 -- src/storage/assembler.rs | 4 + src/wire/sixlowpan.rs | 39 +-- 13 files changed, 254 insertions(+), 557 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index e1e03191d..4ef8d48bb 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,16 +1,9 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; -#[cfg(any( - feature = "proto-sixlowpan-fragmentation", - feature = "proto-ipv4-fragmentation" -))] -use smoltcp::iface::ReassemblyBuffer; - use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; @@ -51,33 +44,25 @@ fn main() { routes.add_default_ipv4_route(default_v4_gw).unwrap(); let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); + let builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); #[cfg(feature = "proto-ipv4-fragmentation")] let mut ipv4_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-ipv4-fragmentation")] - { - let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder - .ipv4_reassembly_buffer(ipv4_frag_cache) - .ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); - } + let builder = builder.ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder - .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) - .sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); - } + let builder = builder.sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); - if medium == Medium::Ethernet { - builder = builder + let builder = if medium == Medium::Ethernet { + builder .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); - } + .neighbor_cache(neighbor_cache) + } else { + builder + }; let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); diff --git a/examples/server.rs b/examples/server.rs index f4ecc8c5d..6608f8b5b 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,15 +1,9 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::fmt::Write; use std::os::unix::io::AsRawFd; -#[cfg(any( - feature = "proto-sixlowpan-fragmentation", - feature = "proto-ipv4-fragmentation" -))] -use smoltcp::iface::ReassemblyBuffer; use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{tcp, udp}; @@ -82,22 +76,12 @@ fn main() { #[cfg(feature = "proto-ipv4-fragmentation")] let mut ipv4_out_packet_cache = [0u8; 10_000]; #[cfg(feature = "proto-ipv4-fragmentation")] - { - let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder - .ipv4_reassembly_buffer(ipv4_frag_cache) - .ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); - } + let builder = builder.ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder - .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) - .sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); - } + let mut builder = builder.sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); if medium == Medium::Ethernet { builder = builder diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index fa3e04491..23cec9f7c 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -43,11 +43,10 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, ReassemblyBuffer, SocketSet}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; @@ -96,20 +95,11 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache); - #[cfg(feature = "proto-ipv4-fragmentation")] - { - let ipv4_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder.ipv4_reassembly_buffer(ipv4_frag_cache); - } - #[cfg(feature = "proto-sixlowpan-fragmentation")] let mut out_packet_buffer = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { - let sixlowpan_frag_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - builder = builder - .sixlowpan_reassembly_buffer(sixlowpan_frag_cache) - .sixlowpan_fragmentation_buffer(&mut out_packet_buffer[..]); + builder = builder.sixlowpan_fragmentation_buffer(&mut out_packet_buffer[..]); } let mut iface = builder.finalize(&mut device); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 38e5d7a6e..6ec01f306 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -44,11 +44,10 @@ mod utils; use log::debug; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, ReassemblyBuffer, SocketSet}; +use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; @@ -169,15 +168,12 @@ fn main() { )) .unwrap(); - let cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); - let mut builder = InterfaceBuilder::new() .ip_addrs(ip_addrs) .pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache) - .sixlowpan_reassembly_buffer(cache) .sixlowpan_fragmentation_buffer(vec![]); let mut iface = builder.finalize(&mut device); diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index f1d1fdc68..d56ce83c9 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -6,8 +6,24 @@ use managed::{ManagedMap, ManagedSlice}; use crate::storage::Assembler; use crate::time::{Duration, Instant}; -use crate::Error; -use crate::Result; + +// TODO: make configurable. +const BUFFER_SIZE: usize = 1500; + +#[cfg(feature = "alloc")] +type Buffer = alloc::vec::Vec; +#[cfg(not(feature = "alloc"))] +type Buffer = [u8; BUFFER_SIZE]; + +const PACKET_ASSEMBLER_COUNT: usize = 4; + +/// Problem when assembling: something was out of bounds. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AssemblerError; + +/// Packet assembler is full +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AssemblerFullError; /// Holds different fragments of one packet, used for assembling fragmented packets. /// @@ -15,8 +31,9 @@ use crate::Result; /// or should be statically allocated based upon the MTU of the type of packet being /// assembled (ex: 1280 for a IPv6 frame). #[derive(Debug)] -pub struct PacketAssembler<'a> { - buffer: ManagedSlice<'a, u8>, +pub struct PacketAssembler { + key: Option, + buffer: Buffer, assembler: Assembler, total_size: Option, @@ -24,15 +41,16 @@ pub struct PacketAssembler<'a> { offset_correction: isize, } -impl<'a> PacketAssembler<'a> { +impl PacketAssembler { /// Create a new empty buffer for fragments. - pub fn new(storage: S) -> Self - where - S: Into>, - { - let s = storage.into(); - PacketAssembler { - buffer: s, + pub fn new() -> Self { + Self { + key: None, + + #[cfg(feature = "alloc")] + buffer: Buffer::new(), + #[cfg(not(feature = "alloc"))] + buffer: [0u8; BUFFER_SIZE], assembler: Assembler::new(), total_size: None, @@ -42,50 +60,33 @@ impl<'a> PacketAssembler<'a> { } pub(crate) fn reset(&mut self) { - self.assembler = Assembler::new(); + self.key = None; + self.assembler.clear(); self.total_size = None; self.expires_at = Instant::ZERO; self.offset_correction = 0; } - /// Start with saving fragments. - /// We initialize the assembler with the total size of the final packet. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when the buffer is too small for holding all the - /// fragments of a packet. - pub(crate) fn start( - &mut self, - total_size: Option, - expires_at: Instant, - offset_correction: isize, - ) -> Result<()> { - self.reset(); - if let Some(total_size) = total_size { - self.set_total_size(total_size)?; - } - self.expires_at = expires_at; - self.offset_correction = offset_correction; - Ok(()) + pub(crate) fn set_offset_correction(&mut self, correction: isize) { + self.offset_correction = correction; } /// Set the total size of the packet assembler. - pub(crate) fn set_total_size(&mut self, size: usize) -> Result<()> { + pub(crate) fn set_total_size(&mut self, size: usize) -> Result<(), AssemblerError> { if let Some(old_size) = self.total_size { if old_size != size { - return Err(Error::Malformed); + return Err(AssemblerError); } } - match &mut self.buffer { - ManagedSlice::Borrowed(b) => { - if b.len() < size { - return Err(Error::PacketAssemblerBufferTooSmall); - } - } - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => b.resize(size, 0), + #[cfg(not(feature = "alloc"))] + if self.buffer.len() < size { + return Err(AssemblerError); + } + + #[cfg(feature = "alloc")] + if self.buffer.len() < size { + self.buffer.resize(size, 0); } self.total_size = Some(size); @@ -93,8 +94,8 @@ impl<'a> PacketAssembler<'a> { } /// Return the instant when the assembler expires. - pub(crate) fn expires_at(&self) -> Result { - Ok(self.expires_at) + pub(crate) fn expires_at(&self) -> Instant { + self.expires_at } /// Add a fragment into the packet that is being reassembled. @@ -103,29 +104,25 @@ impl<'a> PacketAssembler<'a> { /// /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// place. - pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result { + pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result<(), AssemblerError> { let offset = offset as isize + self.offset_correction; let offset = if offset <= 0 { 0 } else { offset as usize }; - match &mut self.buffer { - ManagedSlice::Borrowed(b) => { - if offset + data.len() > b.len() { - return Err(Error::PacketAssemblerBufferTooSmall); - } - } - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => { - if offset + data.len() > b.len() { - b.resize(offset + data.len(), 0); - } - } + #[cfg(not(feature = "alloc"))] + if self.buffer.len() < offset + data.len() { + return Err(AssemblerError); + } + + #[cfg(feature = "alloc")] + if self.buffer.len() < offset + data.len() { + self.buffer.resize(offset + data.len(), 0); } let len = data.len(); self.buffer[offset..][..len].copy_from_slice(data); net_debug!( - "frag assembler: receiving {} octests at offset {}", + "frag assembler: receiving {} octets at offset {}", len, offset ); @@ -133,225 +130,92 @@ impl<'a> PacketAssembler<'a> { match self.assembler.add(offset, data.len()) { Ok(()) => { net_debug!("assembler: {}", self.assembler); - self.is_complete() + Ok(()) + } + Err(_) => { + net_debug!("packet assembler: too many holes, dropping."); + Err(AssemblerError) } - // NOTE(thvdveld): hopefully we wont get too many holes errors I guess? - Err(_) => Err(Error::PacketAssemblerTooManyHoles), } } - /// Get an immutable slice of the underlying packet data. + /// Get an immutable slice of the underlying packet data, if reassembly complete. /// This will mark the assembler as empty, so that it can be reused. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerIncomplete`] when not all the fragments have been collected. - pub(crate) fn assemble(&mut self) -> Result<&'_ [u8]> { - if !self.is_complete()? { - return Err(Error::PacketAssemblerIncomplete); + pub(crate) fn assemble(&mut self) -> Option<&'_ [u8]> { + if !self.is_complete() { + return None; } // NOTE: we can unwrap because `is_complete` already checks this. let total_size = self.total_size.unwrap(); - let a = &self.buffer[..total_size]; - - Ok(a) + self.reset(); + Some(&self.buffer[..total_size]) } /// Returns `true` when all fragments have been received, otherwise `false`. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerNotInit`] when the assembler was not initialized (try initializing the - /// assembler with [`Self::start`]). - pub(crate) fn is_complete(&self) -> Result { - match (self.total_size, self.assembler.peek_front()) { - (Some(total_size), front) => Ok(front == total_size), - _ => Ok(false), - } + pub(crate) fn is_complete(&self) -> bool { + self.total_size == Some(self.assembler.peek_front()) } - /// Returns `true` when the packet assembler is empty (free to use). - fn is_empty(&self) -> bool { - self.assembler.is_empty() + /// Returns `true` when the packet assembler is free to use. + fn is_free(&self) -> bool { + self.key.is_none() } } /// Set holding multiple [`PacketAssembler`]. #[derive(Debug)] -pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy> { - packet_buffer: ManagedSlice<'a, PacketAssembler<'a>>, - index_buffer: ManagedMap<'a, Key, usize>, +pub struct PacketAssemblerSet { + assemblers: [PacketAssembler; PACKET_ASSEMBLER_COUNT], } -impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> { +impl PacketAssemblerSet { /// Create a new set of packet assemblers. - /// - /// # Panics - /// - /// This will panic when: - /// - The packet buffer and index buffer don't have the same size or are empty (when they are - /// both borrowed). - /// - The packet buffer is empty (when only the packet buffer is borrowed). - /// - The index buffer is empty (when only the index buffer is borrowed). - pub fn new(packet_buffer: FB, index_buffer: IB) -> Self - where - FB: Into>>, - IB: Into>, - { - let packet_buffer = packet_buffer.into(); - let index_buffer = index_buffer.into(); - - match (&packet_buffer, &index_buffer) { - (ManagedSlice::Borrowed(f), ManagedMap::Borrowed(i)) => { - if f.len() != i.len() { - panic!("The amount of places in the index buffer must be the same as the amount of possible fragments assemblers."); - } - } - #[cfg(feature = "alloc")] - (ManagedSlice::Borrowed(f), ManagedMap::Owned(_)) => { - if f.is_empty() { - panic!("The packet buffer cannot be empty."); - } - } - #[cfg(feature = "alloc")] - (ManagedSlice::Owned(_), ManagedMap::Borrowed(i)) => { - if i.is_empty() { - panic!("The index buffer cannot be empty."); - } - } - #[cfg(feature = "alloc")] - (ManagedSlice::Owned(_), ManagedMap::Owned(_)) => (), - } - + pub fn new() -> Self { Self { - packet_buffer, - index_buffer, - } - } - - /// Reserve a [`PacketAssembler`], which is linked to a specific key. - /// Returns the reserved fragments assembler. - /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerSetFull`] when every [`PacketAssembler`] in the buffer is used (only - /// when the non allocating version of is used). - pub(crate) fn reserve_with_key(&mut self, key: &K) -> Result<&mut PacketAssembler<'a>> { - // Check how many WIP reassemblies we have. - // The limit is currently set to 255. - if self.index_buffer.len() == u8::MAX as usize { - return Err(Error::PacketAssemblerSetFull); - } - - if self.packet_buffer.len() == self.index_buffer.len() { - match &mut self.packet_buffer { - ManagedSlice::Borrowed(_) => return Err(Error::PacketAssemblerSetFull), - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => (), - } - } - - let i = self - .get_free_packet_assembler() - .ok_or(Error::PacketAssemblerSetFull)?; - - // NOTE(thvdveld): this should not fail because we already checked the available space. - match self.index_buffer.insert(*key, i) { - Ok(_) => Ok(&mut self.packet_buffer[i]), - Err(_) => unreachable!(), - } - } - - /// Return the first free packet assembler available from the cache. - fn get_free_packet_assembler(&mut self) -> Option { - match &mut self.packet_buffer { - ManagedSlice::Borrowed(_) => (), - #[cfg(feature = "alloc")] - ManagedSlice::Owned(b) => b.push(PacketAssembler::new(alloc::vec![])), + // TODO: support any PACKET_ASSEMBLER_COUNT + assemblers: [ + PacketAssembler::new(), + PacketAssembler::new(), + PacketAssembler::new(), + PacketAssembler::new(), + ], } - - self.packet_buffer - .iter() - .enumerate() - .find(|(_, b)| b.is_empty()) - .map(|(i, _)| i) } - /// Return a mutable slice to a packet assembler. - /// - /// # Errors + /// Get a [`PacketAssembler`] for a specific key. /// - /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the key was not found in the set. - pub(crate) fn get_packet_assembler_mut(&mut self, key: &K) -> Result<&mut PacketAssembler<'a>> { - if let Some(i) = self.index_buffer.get(key) { - Ok(&mut self.packet_buffer[*i]) - } else { - Err(Error::PacketAssemblerSetKeyNotFound) - } - } - - /// Return the assembled packet from a packet assembler. - /// This also removes it from the set. + /// If it doesn't exist, it is created, with the `expires_at` timestamp. /// - /// # Errors - /// - /// - Returns [`Error::PacketAssemblerSetKeyNotFound`] when the `key` was not found. - /// - Returns [`Error::PacketAssemblerIncomplete`] when the fragments assembler was empty or not fully assembled. - pub(crate) fn get_assembled_packet(&mut self, key: &K) -> Result<&[u8]> { - if let Some(i) = self.index_buffer.get(key) { - let p = self.packet_buffer[*i].assemble()?; - self.index_buffer.remove(key); - Ok(p) - } else { - Err(Error::PacketAssemblerSetKeyNotFound) - } - } - - /// Remove all [`PacketAssembler`]s that are marked as discarded. - pub fn remove_discarded(&mut self) { - loop { - let mut key = None; - for (k, i) in self.index_buffer.iter() { - if self.packet_buffer[*i].is_empty() { - key = Some(*k); - break; - } + /// If the assembler set is full, in which case an error is returned. + pub(crate) fn get( + &mut self, + key: &K, + expires_at: Instant, + ) -> Result<&mut PacketAssembler, AssemblerFullError> { + let mut empty_slot = None; + for slot in &mut self.assemblers { + if slot.key.as_ref() == Some(key) { + return Ok(slot); } - - if let Some(k) = key { - self.index_buffer.remove(&k); - } else { - break; + if slot.is_free() { + empty_slot = Some(slot) } } + + let slot = empty_slot.ok_or(AssemblerFullError)?; + slot.key = Some(*key); + slot.expires_at = expires_at; + Ok(slot) } - /// Mark all [`PacketAssembler`]s as discarded for which `f` returns `Ok(true)`. - /// This does not remove them from the buffer. - pub fn mark_discarded_when(&mut self, f: F) -> Result<()> - where - F: Fn(&mut PacketAssembler<'_>) -> Result, - { - for (_, i) in &mut self.index_buffer.iter() { - let frag = &mut self.packet_buffer[*i]; - if f(frag)? { + /// Remove all [`PacketAssembler`]s that are expired. + pub fn remove_expired(&mut self, timestamp: Instant) { + for frag in &mut self.assemblers { + if !frag.is_free() && frag.expires_at < timestamp { frag.reset(); } } - - Ok(()) - } - - /// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`. - pub fn remove_when(&mut self, f: F) -> Result<()> - where - F: Fn(&mut PacketAssembler<'_>) -> Result, - { - self.mark_discarded_when(f)?; - self.remove_discarded(); - - Ok(()) } } @@ -364,145 +228,103 @@ mod tests { id: usize, } - #[test] - fn packet_assembler_buffer_too_small() { - let mut storage = [0u8; 1]; - let mut p_assembler = PacketAssembler::new(&mut storage[..]); - - assert_eq!( - p_assembler.start(Some(2), Instant::from_secs(0), 0), - Err(Error::PacketAssemblerBufferTooSmall) - ); - assert_eq!(p_assembler.start(Some(1), Instant::from_secs(0), 0), Ok(())); - - let data = b"Hello World!"; - assert_eq!( - p_assembler.add(&data[..], data.len()), - Err(Error::PacketAssemblerBufferTooSmall) - ); - } - #[test] fn packet_assembler_overlap() { - let mut storage = [0u8; 5]; - let mut p_assembler = PacketAssembler::new(&mut storage[..]); + let mut p_assembler = PacketAssembler::::new(); - p_assembler - .start(Some(5), Instant::from_secs(0), 0) - .unwrap(); - let data = b"Rust"; + p_assembler.set_total_size(5).unwrap(); - p_assembler.add(&data[..], 0).unwrap(); + let data = b"Rust"; + p_assembler.add(&data[..], 0); + p_assembler.add(&data[..], 1); - assert_eq!(p_assembler.add(&data[..], 1), Ok(true)); + assert_eq!(p_assembler.assemble(), Some(&b"RRust"[..])) } #[test] fn packet_assembler_assemble() { - let mut storage = [0u8; 12]; - let mut p_assembler = PacketAssembler::new(&mut storage[..]); + let mut p_assembler = PacketAssembler::::new(); let data = b"Hello World!"; - p_assembler - .start(Some(data.len()), Instant::from_secs(0), 0) - .unwrap(); + p_assembler.set_total_size(data.len()).unwrap(); p_assembler.add(b"Hello ", 0).unwrap(); - assert_eq!( - p_assembler.assemble(), - Err(Error::PacketAssemblerIncomplete) - ); + assert_eq!(p_assembler.assemble(), None); p_assembler.add(b"World!", b"Hello ".len()).unwrap(); - assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..])); + assert_eq!(p_assembler.assemble(), Some(&b"Hello World!"[..])); } #[test] fn packet_assembler_out_of_order_assemble() { - let mut storage = [0u8; 12]; - let mut p_assembler = PacketAssembler::new(&mut storage[..]); + let mut p_assembler = PacketAssembler::::new(); let data = b"Hello World!"; - p_assembler - .start(Some(data.len()), Instant::from_secs(0), 0) - .unwrap(); + p_assembler.set_total_size(data.len()).unwrap(); p_assembler.add(b"World!", b"Hello ".len()).unwrap(); - assert_eq!( - p_assembler.assemble(), - Err(Error::PacketAssemblerIncomplete) - ); + assert_eq!(p_assembler.assemble(), None); p_assembler.add(b"Hello ", 0).unwrap(); - assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..])); + assert_eq!(p_assembler.assemble(), Some(&b"Hello World!"[..])); } #[test] fn packet_assembler_set() { let key = Key { id: 1 }; - let mut set = PacketAssemblerSet::<'_, _>::new(vec![], std::collections::BTreeMap::new()); - - if let Err(e) = set.get_packet_assembler_mut(&key) { - assert_eq!(e, Error::PacketAssemblerSetKeyNotFound); - } + let mut set = PacketAssemblerSet::new(); - assert!(set.reserve_with_key(&key).is_ok()); + assert!(set.get(&key, Instant::ZERO).is_ok()); } #[test] - fn packet_assembler_set_borrowed() { - let mut buf = [0u8, 127]; - let mut packet_assembler_cache = [PacketAssembler::<'_>::new(&mut buf[..])]; - let mut packet_index_cache = [None]; - - let key = Key { id: 1 }; - - let mut set = - PacketAssemblerSet::new(&mut packet_assembler_cache[..], &mut packet_index_cache[..]); - - if let Err(e) = set.get_packet_assembler_mut(&key) { - assert_eq!(e, Error::PacketAssemblerSetKeyNotFound); - } - - assert!(set.reserve_with_key(&key).is_ok()); + fn packet_assembler_set_full() { + let mut set = PacketAssemblerSet::new(); + set.get(&Key { id: 0 }, Instant::ZERO).unwrap(); + set.get(&Key { id: 1 }, Instant::ZERO).unwrap(); + set.get(&Key { id: 2 }, Instant::ZERO).unwrap(); + set.get(&Key { id: 3 }, Instant::ZERO).unwrap(); + assert!(set.get(&Key { id: 4 }, Instant::ZERO).is_err()); } #[test] fn packet_assembler_set_assembling_many() { - let mut buf = [0u8, 127]; - let mut packet_assembler_cache = [PacketAssembler::new(&mut buf[..])]; - let mut packet_index_cache = [None]; - - let mut set = - PacketAssemblerSet::new(&mut packet_assembler_cache[..], &mut packet_index_cache[..]); + let mut set = PacketAssemblerSet::new(); let key = Key { id: 0 }; - set.reserve_with_key(&key).unwrap(); - set.get_packet_assembler_mut(&key) - .unwrap() - .start(Some(0), Instant::from_secs(0), 0) - .unwrap(); - set.get_assembled_packet(&key).unwrap(); + let assr = set.get(&key, Instant::ZERO).unwrap(); + assert_eq!(assr.assemble(), None); + assr.set_total_size(0).unwrap(); + assr.assemble().unwrap(); + + // Test that `.assemble()` effectively deletes it. + let assr = set.get(&key, Instant::ZERO).unwrap(); + assert_eq!(assr.assemble(), None); + assr.set_total_size(0).unwrap(); + assr.assemble().unwrap(); let key = Key { id: 1 }; - set.reserve_with_key(&key).unwrap(); - set.get_packet_assembler_mut(&key) - .unwrap() - .start(Some(0), Instant::from_secs(0), 0) - .unwrap(); - set.get_assembled_packet(&key).unwrap(); + let assr = set.get(&key, Instant::ZERO).unwrap(); + assr.set_total_size(0).unwrap(); + assr.assemble().unwrap(); + + let key = Key { id: 2 }; + let assr = set.get(&key, Instant::ZERO).unwrap(); + assr.set_total_size(0).unwrap(); + assr.assemble().unwrap(); let key = Key { id: 2 }; - set.reserve_with_key(&key).unwrap(); - set.get_packet_assembler_mut(&key) - .unwrap() - .start(Some(0), Instant::from_secs(0), 0) - .unwrap(); - set.get_assembled_packet(&key).unwrap(); + let assr = set.get(&key, Instant::ZERO).unwrap(); + assr.set_total_size(2).unwrap(); + assr.add(&[0x00], 0).unwrap(); + assert_eq!(assr.assemble(), None); + let assr = set.get(&key, Instant::ZERO).unwrap(); + assr.add(&[0x01], 1).unwrap(); + assert_eq!(assr.assemble(), Some(&[0x00, 0x01][..])); } } diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index 4a9f86967..b633c0a46 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -15,7 +15,7 @@ impl<'i> InterfaceInner<'i> { &mut self, sockets: &mut SocketSet, frame: &'frame T, - _fragments: &'frame mut FragmentsBuffer<'i>, + _fragments: &'frame mut FragmentsBuffer, ) -> Option> { let eth_frame = check!(EthernetFrame::new_checked(frame)); diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index b95e5b1e5..fcbde4748 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -28,7 +28,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'payload T>, - _fragments: Option<&'output mut PacketAssemblerSet<'a, Ipv4FragKey>>, + _fragments: Option<&'output mut PacketAssemblerSet>, ) -> Option> { let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); if !self.is_unicast_v4(ipv4_repr.src_addr) { @@ -46,21 +46,11 @@ impl<'a> InterfaceInner<'a> { if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { let key = ipv4_packet.get_key(); - let f = match fragments.get_packet_assembler_mut(&key) { + let f = match fragments.get(&key, self.now + REASSEMBLY_TIMEOUT) { Ok(f) => f, Err(_) => { - let p = match fragments.reserve_with_key(&key) { - Ok(p) => p, - Err(Error::PacketAssemblerSetFull) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); - } - e => check!(e), - }; - - check!(p.start(None, self.now + REASSEMBLY_TIMEOUT, 0)); - - check!(fragments.get_packet_assembler_mut(&key)) + net_debug!("No available packet assembler for fragmented packet"); + return None; } }; @@ -72,20 +62,17 @@ impl<'a> InterfaceInner<'a> { )); } - match f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { - Ok(true) => { - // NOTE: according to the standard, the total length needs to be - // recomputed, as well as the checksum. However, we don't really use - // the IPv4 header after the packet is reassembled. - check!(fragments.get_assembled_packet(&key)) - } - Ok(false) => { - return None; - } - Err(e) => { - net_debug!("fragmentation error: {}", e); - return None; - } + if let Err(e) = f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) { + net_debug!("fragmentation error: {:?}", e); + return None; + } + + // NOTE: according to the standard, the total length needs to be + // recomputed, as well as the checksum. However, we don't really use + // the IPv4 header after the packet is reassembled. + match f.assemble() { + Some(payload) => payload, + None => return None, } } else { ipv4_packet.payload() diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 349fa737d..5cefd4ca2 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -39,18 +39,13 @@ const MAX_IP_ADDR_COUNT: usize = 5; #[cfg(feature = "proto-igmp")] const MAX_IPV4_MULTICAST_GROUPS: usize = 4; -pub(crate) struct FragmentsBuffer<'a> { +pub(crate) struct FragmentsBuffer { #[cfg(feature = "proto-ipv4-fragmentation")] - pub(crate) ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + pub(crate) ipv4_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + sixlowpan_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: Duration, - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - _lifetime: core::marker::PhantomData<&'a ()>, } pub(crate) struct OutPackets<'a> { @@ -245,7 +240,7 @@ use check; /// a `&mut [T]`, or `Vec` if a heap is available. pub struct Interface<'a> { inner: InterfaceInner<'a>, - fragments: FragmentsBuffer<'a>, + fragments: FragmentsBuffer, out_packets: OutPackets<'a>, } @@ -308,12 +303,12 @@ pub struct InterfaceBuilder<'a> { random_seed: u64, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + ipv4_fragments: PacketAssemblerSet, #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_out_buffer: ManagedSlice<'a, u8>, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, + sixlowpan_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_buffer_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -345,9 +340,6 @@ let hw_addr = // ... # EthernetAddress::default(); let neighbor_cache = // ... # NeighborCache::new(); -# #[cfg(feature = "proto-ipv4-fragmentation")] -# let ipv4_frag_cache = // ... -# ReassemblyBuffer::new(vec![], BTreeMap::new()); let ip_addrs = // ... # heapless::Vec::::new(); let builder = InterfaceBuilder::new() @@ -355,11 +347,6 @@ let builder = InterfaceBuilder::new() .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs); -# #[cfg(feature = "proto-ipv4-fragmentation")] -let builder = builder - .ipv4_reassembly_buffer(ipv4_frag_cache) - .ipv4_fragmentation_buffer(vec![]); - let iface = builder.finalize(&mut device); ``` "## @@ -386,12 +373,12 @@ let iface = builder.finalize(&mut device); random_seed: 0, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + ipv4_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + sixlowpan_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -512,7 +499,7 @@ let iface = builder.finalize(&mut device); /// Set the IPv4 reassembly buffer the interface will use. #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { + pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet) -> Self { self.ipv4_fragments = storage; self } @@ -541,7 +528,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer( mut self, - storage: PacketAssemblerSet<'a, SixlowpanFragKey>, + storage: PacketAssemblerSet, ) -> Self { self.sixlowpan_fragments = storage; self @@ -664,12 +651,6 @@ let iface = builder.finalize(&mut device); sixlowpan_fragments: self.sixlowpan_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, - - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - _lifetime: core::marker::PhantomData, }, out_packets: OutPackets { #[cfg(feature = "proto-ipv4-fragmentation")] @@ -1070,14 +1051,10 @@ impl<'a> Interface<'a> { self.inner.now = timestamp; #[cfg(feature = "proto-ipv4-fragmentation")] - self.fragments - .ipv4_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; + self.fragments.ipv4_fragments.remove_expired(timestamp); #[cfg(feature = "proto-sixlowpan-fragmentation")] - self.fragments - .sixlowpan_fragments - .remove_when(|frag| Ok(timestamp >= frag.expires_at()?))?; + self.fragments.sixlowpan_fragments.remove_expired(timestamp); #[cfg(feature = "proto-ipv4-fragmentation")] match self.ipv4_egress(device) { @@ -1719,7 +1696,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, - _fragments: &'frame mut FragmentsBuffer<'a>, + _fragments: &'frame mut FragmentsBuffer, ) -> Option> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 084685711..73f4f787e 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -21,7 +21,7 @@ impl<'a> InterfaceInner<'a> { &mut self, sockets: &mut SocketSet, sixlowpan_payload: &'payload T, - _fragments: &'output mut FragmentsBuffer<'a>, + _fragments: &'output mut FragmentsBuffer, ) -> Option> { let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); @@ -73,10 +73,7 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - _fragments: Option<( - &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, - Duration, - )>, + _fragments: Option<(&'output mut PacketAssemblerSet, Duration)>, ) -> Option> { let payload = match check!(SixlowpanPacket::dispatch(payload)) { #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] @@ -173,11 +170,10 @@ impl<'a> InterfaceInner<'a> { &mut self, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: Option<( - &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, - Duration, - )>, + fragments: Option<(&'output mut PacketAssemblerSet, Duration)>, ) -> Option<&'output [u8]> { + use crate::iface::fragmentation::AssemblerFullError; + let (fragments, timeout) = fragments.unwrap(); // We have a fragment header, which means we cannot process the 6LoWPAN packet, @@ -191,6 +187,19 @@ impl<'a> InterfaceInner<'a> { // The offset of this fragment in increments of 8 octets. let offset = frag.datagram_offset() as usize * 8; + // We reserve a spot in the packet assembler set and add the required + // information to the packet assembler. + // This information is the total size of the packet when it is fully assmbled. + // We also pass the header size, since this is needed when other fragments + // (other than the first one) are added. + let frag_slot = match fragments.get(&key, self.now + timeout) { + Ok(frag) => frag, + Err(AssemblerFullError) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + }; + if frag.is_first_fragment() { // The first fragment contains the total size of the IPv6 packet. // However, we received a packet that is compressed following the 6LoWPAN @@ -234,45 +243,28 @@ impl<'a> InterfaceInner<'a> { SixlowpanNextHeader::Uncompressed(_) => (), } - // We reserve a spot in the packet assembler set and add the required - // information to the packet assembler. - // This information is the total size of the packet when it is fully assmbled. - // We also pass the header size, since this is needed when other fragments - // (other than the first one) are added. - let frag_slot = match fragments.reserve_with_key(&key) { - Ok(frag) => frag, - Err(Error::PacketAssemblerSetFull) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); - } - e => check!(e), - }; - - check!(frag_slot.start( - Some( - frag.datagram_size() as usize - uncompressed_header_size - + compressed_header_size - ), - self.now + timeout, + let total_size = + frag.datagram_size() as usize - uncompressed_header_size + compressed_header_size; + check!(frag_slot.set_total_size(total_size)); + frag_slot.set_offset_correction( -((uncompressed_header_size - compressed_header_size) as isize), - )); + ); } - let frags = check!(fragments.get_packet_assembler_mut(&key)); - net_trace!("6LoWPAN: received packet fragment"); // Add the fragment to the packet assembler. - match frags.add(frag.payload(), offset) { - Ok(true) => { + if let Err(e) = frag_slot.add(frag.payload(), offset) { + net_debug!("fragmentation error: {:?}", e); + return None; + } + + match frag_slot.assemble() { + Some(payload) => { net_trace!("6LoWPAN: fragmented packet now complete"); - match fragments.get_assembled_packet(&key) { - Ok(packet) => Some(packet), - _ => unreachable!(), - } + Some(payload) } - Ok(false) => None, - Err(_) => None, + None => None, } } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 2e482fa8c..725878944 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; #[cfg(feature = "proto-igmp")] use std::vec::Vec; @@ -59,9 +58,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder - .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_fragmentation_buffer(vec![]); + let iface_builder = iface_builder.ipv4_fragmentation_buffer(vec![]); let iface = iface_builder.finalize(&mut device); @@ -92,14 +89,10 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .ip_addrs(ip_addrs); #[cfg(feature = "proto-sixlowpan-fragmentation")] - let iface_builder = iface_builder - .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .sixlowpan_fragmentation_buffer(vec![]); + let iface_builder = iface_builder.sixlowpan_fragmentation_buffer(vec![]); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder - .ipv4_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .ipv4_fragmentation_buffer(vec![]); + let iface_builder = iface_builder.ipv4_fragmentation_buffer(vec![]); let iface = iface_builder.finalize(&mut device); @@ -126,9 +119,7 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .ip_addrs(ip_addrs); #[cfg(feature = "proto-sixlowpan-fragmentation")] - let iface_builder = iface_builder - .sixlowpan_reassembly_buffer(PacketAssemblerSet::new(vec![], BTreeMap::new())) - .sixlowpan_fragmentation_buffer(vec![]); + let iface_builder = iface_builder.sixlowpan_fragmentation_buffer(vec![]); let iface = iface_builder.finalize(&mut device); diff --git a/src/lib.rs b/src/lib.rs index 4640dd804..f7753a517 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,18 +184,6 @@ pub enum Error { /// An incoming fragment arrived too late. ReassemblyTimeout, - /// The buffer of the assembler is to small and thus the final packet wont fit into it. - PacketAssemblerBufferTooSmall, - /// The packet assembler did not receive all the fragments for assembling the final packet. - PacketAssemblerIncomplete, - /// There are too many holes in the packet assembler (should be fixed in the future?). - PacketAssemblerTooManyHoles, - - /// The packet assembler set has no place for assembling a new stream of fragments. - PacketAssemblerSetFull, - /// The key was not found in the packet assembler set. - PacketAssemblerSetKeyNotFound, - /// An incoming packet was recognized but some parts are not supported by smoltcp. /// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC. NotSupported, @@ -221,18 +209,6 @@ impl fmt::Display for Error { Error::Malformed => write!(f, "malformed packet"), Error::Dropped => write!(f, "dropped by socket"), Error::ReassemblyTimeout => write!(f, "incoming fragment arrived too late"), - Error::PacketAssemblerBufferTooSmall => { - write!(f, "packet assembler buffer too small for final packet") - } - Error::PacketAssemblerIncomplete => write!(f, "packet assembler incomplete"), - Error::PacketAssemblerTooManyHoles => write!( - f, - "packet assembler has too many holes (internal smoltcp error)" - ), - Error::PacketAssemblerSetFull => write!(f, "packet assembler set is full"), - Error::PacketAssemblerSetKeyNotFound => { - write!(f, "packet assembler set does not find key") - } Error::NotSupported => write!(f, "not supported by smoltcp"), } } diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 9c9493f09..effa143a2 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -114,6 +114,10 @@ impl Assembler { Assembler { contigs } } + pub fn clear(&mut self) { + self.contigs.fill(Contig::empty()); + } + fn front(&self) -> Contig { self.contigs[0] } diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 2407730bb..5419935f9 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2140,6 +2140,7 @@ pub mod nhc { #[cfg(test)] mod test { use crate::phy::ChecksumCapabilities; + use crate::time::Duration; use super::*; @@ -2182,9 +2183,8 @@ mod test { use crate::wire::ieee802154::Frame as Ieee802154Frame; use crate::wire::ieee802154::Repr as Ieee802154Repr; use crate::wire::Ieee802154Address; - use std::collections::BTreeMap; - let mut frags_cache = ReassemblyBuffer::new(vec![], BTreeMap::new()); + let mut frags_cache = ReassemblyBuffer::new(); let frame1: &[u8] = &[ 0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, @@ -2219,20 +2219,13 @@ mod test { let uncompressed = 40 + 8; let compressed = 5 + 7; - frags_cache - .reserve_with_key(&key) - .unwrap() - .start( - Some(frag.datagram_size() as usize - uncompressed + compressed), - Instant::now() + crate::time::Duration::from_secs(60), - -((uncompressed - compressed) as isize), - ) + let assr = frags_cache + .get(&key, Instant::now() + Duration::from_secs(60)) .unwrap(); - frags_cache - .get_packet_assembler_mut(&key) - .unwrap() - .add(frag.payload(), 0) + assr.set_total_size(frag.datagram_size() as usize - uncompressed + compressed) .unwrap(); + assr.set_offset_correction(-((uncompressed - compressed) as isize)); + assr.add(frag.payload(), 0).unwrap(); let frame2: &[u8] = &[ 0x41, 0xcc, 0x93, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, @@ -2264,10 +2257,10 @@ mod test { let key = frag.get_key(&ieee802154_repr); - frags_cache - .get_packet_assembler_mut(&key) - .unwrap() - .add(frag.payload(), frag.datagram_offset() as usize * 8) + let assr = frags_cache + .get(&key, Instant::now() + Duration::from_secs(60)) + .unwrap(); + assr.add(frag.payload(), frag.datagram_offset() as usize * 8) .unwrap(); let frame3: &[u8] = &[ @@ -2299,13 +2292,13 @@ mod test { let key = frag.get_key(&ieee802154_repr); - frags_cache - .get_packet_assembler_mut(&key) - .unwrap() - .add(frag.payload(), frag.datagram_offset() as usize * 8) + let assr = frags_cache + .get(&key, Instant::now() + Duration::from_secs(60)) + .unwrap(); + assr.add(frag.payload(), frag.datagram_offset() as usize * 8) .unwrap(); - let assembled_packet = frags_cache.get_assembled_packet(&key).unwrap(); + let assembled_packet = assr.assemble().unwrap(); let sixlowpan_frame = SixlowpanPacket::dispatch(assembled_packet).unwrap(); From 93d5d8acd4cdd15dc7c6c17a979986df3aaf2af6 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 27 Dec 2022 16:57:05 +0100 Subject: [PATCH 469/566] Refactor reassembly for 6LoWPAN --- src/iface/fragmentation.rs | 36 ++++ src/iface/interface/sixlowpan.rs | 278 +++++++++++++++++-------------- src/wire/sixlowpan.rs | 8 +- src/wire/udp.rs | 24 +++ 4 files changed, 221 insertions(+), 125 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index d56ce83c9..ccffd5c5f 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -98,6 +98,42 @@ impl PacketAssembler { self.expires_at } + pub(crate) fn add_with_fn( + &mut self, + len: usize, + offset: usize, + f: impl Fn(&mut [u8]), + ) -> Result<(), AssemblerError> { + #[cfg(not(feature = "alloc"))] + if self.buffer.len() < offset + len { + return Err(AssemblerError); + } + + #[cfg(feature = "alloc")] + if self.buffer.len() < offset + len { + self.buffer.resize(offset + len, 0); + } + + f(&mut self.buffer[offset..][..len]); + + net_debug!( + "frag assembler: receiving {} octets at offset {}", + len, + offset + ); + + match self.assembler.add(offset, len) { + Ok(()) => { + net_debug!("assembler: {}", self.assembler); + Ok(()) + } + Err(_) => { + net_debug!("packet assembler: too many holes, dropping."); + Err(AssemblerError) + } + } + } + /// Add a fragment into the packet that is being reassembled. /// /// # Errors diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 73f4f787e..9aa018795 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -9,6 +9,7 @@ use super::SocketSet; #[cfg(feature = "proto-sixlowpan-fragmentation")] use super::SixlowpanOutPacket; +use crate::phy::ChecksumCapabilities; use crate::phy::TxToken; use crate::time::*; use crate::wire::*; @@ -75,6 +76,8 @@ impl<'a> InterfaceInner<'a> { payload: &'payload T, _fragments: Option<(&'output mut PacketAssemblerSet, Duration)>, ) -> Option> { + let (fragments, timeout) = _fragments.unwrap(); + let payload = match check!(SixlowpanPacket::dispatch(payload)) { #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] SixlowpanPacket::FragmentHeader => { @@ -83,86 +86,46 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-sixlowpan-fragmentation")] SixlowpanPacket::FragmentHeader => { - match self.process_sixlowpan_fragment(ieee802154_repr, payload, _fragments) { + match self.process_sixlowpan_fragment( + ieee802154_repr, + payload, + (fragments, timeout), + ) { Some(payload) => payload, None => return None, } } - SixlowpanPacket::IphcHeader => payload.as_ref(), - }; - - // At this point we should have a valid 6LoWPAN packet. - // The first header needs to be an IPHC header. - let iphc_packet = check!(SixlowpanIphcPacket::new_checked(payload)); - let iphc_repr = check!(SixlowpanIphcRepr::parse( - &iphc_packet, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - self.sixlowpan_address_context, - )); - - let payload = iphc_packet.payload(); - let mut ipv6_repr = Ipv6Repr { - src_addr: iphc_repr.src_addr, - dst_addr: iphc_repr.dst_addr, - hop_limit: iphc_repr.hop_limit, - next_header: IpProtocol::Unknown(0), - payload_len: 40, - }; - - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(payload)) { - SixlowpanNhcPacket::ExtHeader => { - net_debug!("Extension headers are currently not supported for 6LoWPAN"); - None - } - #[cfg(not(feature = "socket-udp"))] - SixlowpanNhcPacket::UdpHeader => { - net_debug!("UDP support is disabled, enable cargo feature `socket-udp`."); - None + SixlowpanPacket::IphcHeader => { + let frag_slot = match fragments.get( + &SixlowpanFragKey { + ll_src_addr: ieee802154_repr + .src_addr + .unwrap_or(Ieee802154Address::Absent), + ll_dst_addr: ieee802154_repr + .dst_addr + .unwrap_or(Ieee802154Address::Absent), + datagram_size: 0, + datagram_tag: ieee802154_repr.sequence_number.unwrap_or_default() as u16, + }, + self.now, + ) { + Ok(frag) => frag, + Err(_) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); } - #[cfg(feature = "socket-udp")] - SixlowpanNhcPacket::UdpHeader => { - let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(payload)); - ipv6_repr.next_header = IpProtocol::Udp; - ipv6_repr.payload_len += 8 + udp_packet.payload().len(); + }; - let udp_repr = check!(SixlowpanUdpNhcRepr::parse( - &udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - &self.checksum_caps(), - )); + self.decompress_sixlowpan(ieee802154_repr, payload.as_ref(), None, frag_slot); - self.process_udp( - sockets, - IpRepr::Ipv6(ipv6_repr), - udp_repr.0, - false, - udp_packet.payload(), - payload, - ) - } + match frag_slot.assemble() { + Some(payload) => payload, + None => unreachable!(), } } - SixlowpanNextHeader::Uncompressed(nxt_hdr) => match nxt_hdr { - IpProtocol::Icmpv6 => { - ipv6_repr.next_header = IpProtocol::Icmpv6; - self.process_icmpv6(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) - } - #[cfg(feature = "socket-tcp")] - IpProtocol::Tcp => { - ipv6_repr.next_header = nxt_hdr; - ipv6_repr.payload_len += payload.len(); - self.process_tcp(sockets, IpRepr::Ipv6(ipv6_repr), iphc_packet.payload()) - } - proto => { - net_debug!("6LoWPAN: {} currently not supported", proto); - None - } - }, - } + }; + + self.process_ipv6(sockets, &check!(Ipv6Packet::new_checked(payload))) } #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -170,11 +133,11 @@ impl<'a> InterfaceInner<'a> { &mut self, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: Option<(&'output mut PacketAssemblerSet, Duration)>, + fragments: (&'output mut PacketAssemblerSet, Duration), ) -> Option<&'output [u8]> { use crate::iface::fragmentation::AssemblerFullError; - let (fragments, timeout) = fragments.unwrap(); + let (fragments, timeout) = fragments; // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. @@ -208,55 +171,18 @@ impl<'a> InterfaceInner<'a> { // compression of the IP header and when UDP is used (because the UDP header // can also be compressed). Other headers are not compressed by 6LoWPAN. - let iphc = check!(SixlowpanIphcPacket::new_checked(frag.payload())); - let iphc_repr = check!(SixlowpanIphcRepr::parse( - &iphc, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - self.sixlowpan_address_context, - )); - - // The uncompressed header size always starts with 40, since this is the size - // of a IPv6 header. - let mut uncompressed_header_size = 40; - let mut compressed_header_size = iphc.header_len(); - - // We need to check if we have an UDP packet, since this header can also be - // compressed by 6LoWPAN. We currently don't support extension headers yet. - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { - SixlowpanNhcPacket::ExtHeader => { - net_debug!("6LoWPAN: extension headers not supported"); - return None; - } - SixlowpanNhcPacket::UdpHeader => { - let udp_packet = - check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); - - uncompressed_header_size += 8; - compressed_header_size += - 1 + udp_packet.ports_size() + udp_packet.checksum_size(); - } - } - } - SixlowpanNextHeader::Uncompressed(_) => (), - } - - let total_size = - frag.datagram_size() as usize - uncompressed_header_size + compressed_header_size; - check!(frag_slot.set_total_size(total_size)); - frag_slot.set_offset_correction( - -((uncompressed_header_size - compressed_header_size) as isize), + self.decompress_sixlowpan( + ieee802154_repr, + frag.payload(), + Some(frag.datagram_size() as usize), + frag_slot, ); - } - - net_trace!("6LoWPAN: received packet fragment"); - - // Add the fragment to the packet assembler. - if let Err(e) = frag_slot.add(frag.payload(), offset) { - net_debug!("fragmentation error: {:?}", e); - return None; + } else { + // Add the fragment to the packet assembler. + if let Err(e) = frag_slot.add(frag.payload(), offset) { + net_debug!("fragmentation error: {:?}", e); + return None; + } } match frag_slot.assemble() { @@ -268,6 +194,116 @@ impl<'a> InterfaceInner<'a> { } } + fn decompress_sixlowpan( + &self, + ieee802154_repr: &Ieee802154Repr, + iphc_payload: &[u8], + total_size: Option, + frag_slot: &mut crate::iface::PacketAssembler, + ) { + let iphc = check!(SixlowpanIphcPacket::new_checked(iphc_payload)); + let iphc_repr = check!(SixlowpanIphcRepr::parse( + &iphc, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + self.sixlowpan_address_context, + )); + + let mut decompressed_size = 40 + iphc.payload().len(); + + let next_header = match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + SixlowpanNhcPacket::ExtHeader => { + net_debug!("Extension headers are currently not supported for 6LoWPAN"); + IpProtocol::Unknown(0) + } + SixlowpanNhcPacket::UdpHeader => { + let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); + let udp_repr = check!(SixlowpanUdpNhcRepr::parse( + &udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + &crate::phy::ChecksumCapabilities::ignored(), + )); + + decompressed_size += 8; + decompressed_size -= udp_repr.header_len(); + IpProtocol::Udp + } + } + } + SixlowpanNextHeader::Uncompressed(proto) => proto, + }; + + let total_size = if let Some(size) = total_size { + size + } else { + decompressed_size + }; + + let ipv6_repr = Ipv6Repr { + src_addr: iphc_repr.src_addr, + dst_addr: iphc_repr.dst_addr, + next_header, + payload_len: total_size - 40, + hop_limit: iphc_repr.hop_limit, + }; + + // Set the total size of the buffer. + check!(frag_slot.set_total_size(total_size)); + + frag_slot + .add_with_fn(decompressed_size, 0, |buffer| { + // Emit the decompressed IPHC header (decompressed to an IPv6 header). + let mut ipv6_packet = + Ipv6Packet::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]); + ipv6_repr.emit(&mut ipv6_packet); + let buffer = &mut buffer[ipv6_repr.buffer_len()..]; + + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + SixlowpanNhcPacket::ExtHeader => todo!(), + SixlowpanNhcPacket::UdpHeader => { + // We need to uncompress the UDP packet and emit it to the + // buffer. + let udp_packet = + check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); + let udp_repr = check!(SixlowpanUdpNhcRepr::parse( + &udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + &ChecksumCapabilities::ignored(), + )); + + let mut udp_emit_packet = UdpPacket::new_unchecked( + &mut buffer[..udp_repr.0.header_len() + iphc.payload().len() + - udp_repr.header_len()], + ); + udp_repr.0.emit_header( + &mut udp_emit_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + ipv6_repr.payload_len - 8, + &ChecksumCapabilities::ignored(), + ); + + buffer[8..] + .copy_from_slice(&iphc.payload()[udp_repr.header_len()..]); + } + } + } + SixlowpanNextHeader::Uncompressed(_) => { + // For uncompressed headers we just copy the slice. + let len = iphc.payload().len(); + buffer[..len].copy_from_slice(iphc.payload()); + } + }; + }) + .unwrap() + } + #[cfg(feature = "medium-ieee802154")] pub(super) fn dispatch_ieee802154( &mut self, diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 5419935f9..7125711c6 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -230,10 +230,10 @@ pub mod frag { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Key { - ll_src_addr: Ieee802154Address, - ll_dst_addr: Ieee802154Address, - datagram_size: u16, - datagram_tag: u16, + pub(crate) ll_src_addr: Ieee802154Address, + pub(crate) ll_dst_addr: Ieee802154Address, + pub(crate) datagram_size: u16, + pub(crate) datagram_tag: u16, } /// A read/write wrapper around a 6LoWPAN Fragment header. diff --git a/src/wire/udp.rs b/src/wire/udp.rs index c19db05ff..f9c0186da 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -250,6 +250,30 @@ impl Repr { HEADER_LEN } + /// Emit a high-level representation into an User Datagram Protocol packet. + pub fn emit_header( + &self, + packet: &mut Packet<&mut T>, + src_addr: &IpAddress, + dst_addr: &IpAddress, + payload_len: usize, + checksum_caps: &ChecksumCapabilities, + ) where + T: AsRef<[u8]> + AsMut<[u8]>, + { + packet.set_src_port(self.src_port); + packet.set_dst_port(self.dst_port); + packet.set_len((HEADER_LEN + payload_len) as u16); + + if checksum_caps.udp.tx() { + packet.fill_checksum(src_addr, dst_addr) + } else { + // make sure we get a consistently zeroed checksum, + // since implementations might rely on it + packet.set_checksum(0); + } + } + /// Emit a high-level representation into an User Datagram Protocol packet. pub fn emit( &self, From 9a5e7e5aa7a3fbe660bceb36a7d4dda9a05ce91f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 30 Dec 2022 02:18:26 +0100 Subject: [PATCH 470/566] sixlowpan: do not use assembler for decompressing non-fragmented packets. --- src/iface/fragmentation.rs | 18 +-- src/iface/interface/mod.rs | 5 + src/iface/interface/sixlowpan.rs | 220 ++++++++++++++----------------- src/iface/interface/tests.rs | 20 +-- 4 files changed, 112 insertions(+), 151 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index ccffd5c5f..26b756d8c 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -19,10 +19,12 @@ const PACKET_ASSEMBLER_COUNT: usize = 4; /// Problem when assembling: something was out of bounds. #[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssemblerError; /// Packet assembler is full #[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssemblerFullError; /// Holds different fragments of one packet, used for assembling fragmented packets. @@ -98,23 +100,17 @@ impl PacketAssembler { self.expires_at } - pub(crate) fn add_with_fn( + pub(crate) fn add_with( &mut self, - len: usize, offset: usize, - f: impl Fn(&mut [u8]), + f: impl Fn(&mut [u8]) -> Result, ) -> Result<(), AssemblerError> { - #[cfg(not(feature = "alloc"))] - if self.buffer.len() < offset + len { + if self.buffer.len() < offset { return Err(AssemblerError); } - #[cfg(feature = "alloc")] - if self.buffer.len() < offset + len { - self.buffer.resize(offset + len, 0); - } - - f(&mut self.buffer[offset..][..len]); + let len = f(&mut self.buffer[offset..])?; + assert!(offset + len <= self.buffer.len()); net_debug!( "frag assembler: receiving {} octets at offset {}", diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 5cefd4ca2..8eec9259a 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -40,6 +40,8 @@ const MAX_IP_ADDR_COUNT: usize = 5; const MAX_IPV4_MULTICAST_GROUPS: usize = 4; pub(crate) struct FragmentsBuffer { + #[cfg(feature = "proto-sixlowpan")] + decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN], #[cfg(feature = "proto-ipv4-fragmentation")] pub(crate) ipv4_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -645,6 +647,9 @@ let iface = builder.finalize(&mut device); Interface { fragments: FragmentsBuffer { + #[cfg(feature = "proto-sixlowpan")] + decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN], + #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_fragments: self.ipv4_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 9aa018795..15f0166a7 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -3,7 +3,6 @@ use super::FragmentsBuffer; use super::InterfaceInner; use super::IpPacket; use super::OutPackets; -use super::PacketAssemblerSet; use super::SocketSet; #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -11,11 +10,14 @@ use super::SixlowpanOutPacket; use crate::phy::ChecksumCapabilities; use crate::phy::TxToken; -use crate::time::*; use crate::wire::*; use crate::Error; use crate::Result; +// Max len of non-fragmented packets after decompression (including ipv6 header and payload) +// TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size) +pub(crate) const MAX_DECOMPRESSED_LEN: usize = 1500; + impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( @@ -46,25 +48,7 @@ impl<'a> InterfaceInner<'a> { } match ieee802154_frame.payload() { - Some(payload) => { - #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - self.process_sixlowpan( - sockets, - &ieee802154_repr, - payload, - Some(( - &mut _fragments.sixlowpan_fragments, - _fragments.sixlowpan_fragments_cache_timeout, - )), - ) - } - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - { - self.process_sixlowpan(sockets, &ieee802154_repr, payload, None) - } - } + Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, _fragments), None => None, } } @@ -74,10 +58,8 @@ impl<'a> InterfaceInner<'a> { sockets: &mut SocketSet, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - _fragments: Option<(&'output mut PacketAssemblerSet, Duration)>, + f: &'output mut FragmentsBuffer, ) -> Option> { - let (fragments, timeout) = _fragments.unwrap(); - let payload = match check!(SixlowpanPacket::dispatch(payload)) { #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] SixlowpanPacket::FragmentHeader => { @@ -86,41 +68,23 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-sixlowpan-fragmentation")] SixlowpanPacket::FragmentHeader => { - match self.process_sixlowpan_fragment( - ieee802154_repr, - payload, - (fragments, timeout), - ) { + match self.process_sixlowpan_fragment(ieee802154_repr, payload, f) { Some(payload) => payload, None => return None, } } SixlowpanPacket::IphcHeader => { - let frag_slot = match fragments.get( - &SixlowpanFragKey { - ll_src_addr: ieee802154_repr - .src_addr - .unwrap_or(Ieee802154Address::Absent), - ll_dst_addr: ieee802154_repr - .dst_addr - .unwrap_or(Ieee802154Address::Absent), - datagram_size: 0, - datagram_tag: ieee802154_repr.sequence_number.unwrap_or_default() as u16, - }, - self.now, + match self.decompress_sixlowpan( + ieee802154_repr, + payload.as_ref(), + None, + &mut f.decompress_buf, ) { - Ok(frag) => frag, - Err(_) => { - net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); + Ok(len) => &f.decompress_buf[..len], + Err(e) => { + net_debug!("sixlowpan decompress failed: {:?}", e); + return None; } - }; - - self.decompress_sixlowpan(ieee802154_repr, payload.as_ref(), None, frag_slot); - - match frag_slot.assemble() { - Some(payload) => payload, - None => unreachable!(), } } }; @@ -133,11 +97,9 @@ impl<'a> InterfaceInner<'a> { &mut self, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, - fragments: (&'output mut PacketAssemblerSet, Duration), + f: &'output mut FragmentsBuffer, ) -> Option<&'output [u8]> { - use crate::iface::fragmentation::AssemblerFullError; - - let (fragments, timeout) = fragments; + use crate::iface::fragmentation::{AssemblerError, AssemblerFullError}; // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. @@ -155,11 +117,14 @@ impl<'a> InterfaceInner<'a> { // This information is the total size of the packet when it is fully assmbled. // We also pass the header size, since this is needed when other fragments // (other than the first one) are added. - let frag_slot = match fragments.get(&key, self.now + timeout) { + let frag_slot = match f + .sixlowpan_fragments + .get(&key, self.now + f.sixlowpan_fragments_cache_timeout) + { Ok(frag) => frag, Err(AssemblerFullError) => { net_debug!("No available packet assembler for fragmented packet"); - return Default::default(); + return None; } }; @@ -171,12 +136,21 @@ impl<'a> InterfaceInner<'a> { // compression of the IP header and when UDP is used (because the UDP header // can also be compressed). Other headers are not compressed by 6LoWPAN. - self.decompress_sixlowpan( - ieee802154_repr, - frag.payload(), - Some(frag.datagram_size() as usize), - frag_slot, - ); + // First segment tells us the total size. + let total_size = frag.datagram_size() as usize; + if frag_slot.set_total_size(total_size).is_err() { + net_debug!("No available packet assembler for fragmented packet"); + return None; + } + + // Decompress headers+payload into the assembler. + if let Err(e) = frag_slot.add_with(0, |buffer| { + self.decompress_sixlowpan(ieee802154_repr, frag.payload(), Some(total_size), buffer) + .map_err(|_| AssemblerError) + }) { + net_debug!("fragmentation error: {:?}", e); + return None; + } } else { // Add the fragment to the packet assembler. if let Err(e) = frag_slot.add(frag.payload(), offset) { @@ -199,33 +173,33 @@ impl<'a> InterfaceInner<'a> { ieee802154_repr: &Ieee802154Repr, iphc_payload: &[u8], total_size: Option, - frag_slot: &mut crate::iface::PacketAssembler, - ) { - let iphc = check!(SixlowpanIphcPacket::new_checked(iphc_payload)); - let iphc_repr = check!(SixlowpanIphcRepr::parse( + buffer: &mut [u8], + ) -> core::result::Result { + let iphc = SixlowpanIphcPacket::new_checked(iphc_payload)?; + let iphc_repr = SixlowpanIphcRepr::parse( &iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, self.sixlowpan_address_context, - )); + )?; let mut decompressed_size = 40 + iphc.payload().len(); let next_header = match iphc_repr.next_header { SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { + match SixlowpanNhcPacket::dispatch(iphc.payload())? { SixlowpanNhcPacket::ExtHeader => { net_debug!("Extension headers are currently not supported for 6LoWPAN"); IpProtocol::Unknown(0) } SixlowpanNhcPacket::UdpHeader => { - let udp_packet = check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); - let udp_repr = check!(SixlowpanUdpNhcRepr::parse( + let udp_packet = SixlowpanUdpNhcPacket::new_checked(iphc.payload())?; + let udp_repr = SixlowpanUdpNhcRepr::parse( &udp_packet, &iphc_repr.src_addr, &iphc_repr.dst_addr, &crate::phy::ChecksumCapabilities::ignored(), - )); + )?; decompressed_size += 8; decompressed_size -= udp_repr.header_len(); @@ -236,6 +210,12 @@ impl<'a> InterfaceInner<'a> { SixlowpanNextHeader::Uncompressed(proto) => proto, }; + if buffer.len() < decompressed_size { + net_debug!("sixlowpan decompress: buffer too short"); + return Err(crate::wire::Error); + } + let buffer = &mut buffer[..decompressed_size]; + let total_size = if let Some(size) = total_size { size } else { @@ -250,58 +230,50 @@ impl<'a> InterfaceInner<'a> { hop_limit: iphc_repr.hop_limit, }; - // Set the total size of the buffer. - check!(frag_slot.set_total_size(total_size)); - - frag_slot - .add_with_fn(decompressed_size, 0, |buffer| { - // Emit the decompressed IPHC header (decompressed to an IPv6 header). - let mut ipv6_packet = - Ipv6Packet::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]); - ipv6_repr.emit(&mut ipv6_packet); - let buffer = &mut buffer[ipv6_repr.buffer_len()..]; - - match iphc_repr.next_header { - SixlowpanNextHeader::Compressed => { - match check!(SixlowpanNhcPacket::dispatch(iphc.payload())) { - SixlowpanNhcPacket::ExtHeader => todo!(), - SixlowpanNhcPacket::UdpHeader => { - // We need to uncompress the UDP packet and emit it to the - // buffer. - let udp_packet = - check!(SixlowpanUdpNhcPacket::new_checked(iphc.payload())); - let udp_repr = check!(SixlowpanUdpNhcRepr::parse( - &udp_packet, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - &ChecksumCapabilities::ignored(), - )); - - let mut udp_emit_packet = UdpPacket::new_unchecked( - &mut buffer[..udp_repr.0.header_len() + iphc.payload().len() - - udp_repr.header_len()], - ); - udp_repr.0.emit_header( - &mut udp_emit_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - ipv6_repr.payload_len - 8, - &ChecksumCapabilities::ignored(), - ); - - buffer[8..] - .copy_from_slice(&iphc.payload()[udp_repr.header_len()..]); - } - } - } - SixlowpanNextHeader::Uncompressed(_) => { - // For uncompressed headers we just copy the slice. - let len = iphc.payload().len(); - buffer[..len].copy_from_slice(iphc.payload()); + // Emit the decompressed IPHC header (decompressed to an IPv6 header). + let mut ipv6_packet = Ipv6Packet::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]); + ipv6_repr.emit(&mut ipv6_packet); + let buffer = &mut buffer[ipv6_repr.buffer_len()..]; + + match iphc_repr.next_header { + SixlowpanNextHeader::Compressed => { + match SixlowpanNhcPacket::dispatch(iphc.payload())? { + SixlowpanNhcPacket::ExtHeader => todo!(), + SixlowpanNhcPacket::UdpHeader => { + // We need to uncompress the UDP packet and emit it to the + // buffer. + let udp_packet = SixlowpanUdpNhcPacket::new_checked(iphc.payload())?; + let udp_repr = SixlowpanUdpNhcRepr::parse( + &udp_packet, + &iphc_repr.src_addr, + &iphc_repr.dst_addr, + &ChecksumCapabilities::ignored(), + )?; + + let mut udp_emit_packet = UdpPacket::new_unchecked( + &mut buffer[..udp_repr.0.header_len() + iphc.payload().len() + - udp_repr.header_len()], + ); + udp_repr.0.emit_header( + &mut udp_emit_packet, + &iphc_repr.src_addr.into(), + &iphc_repr.dst_addr.into(), + ipv6_repr.payload_len - 8, + &ChecksumCapabilities::ignored(), + ); + + buffer[8..].copy_from_slice(&iphc.payload()[udp_repr.header_len()..]); } - }; - }) - .unwrap() + } + } + SixlowpanNextHeader::Uncompressed(_) => { + // For uncompressed headers we just copy the slice. + let len = iphc.payload().len(); + buffer[..len].copy_from_slice(iphc.payload()); + } + }; + + Ok(decompressed_size) } #[cfg(feature = "medium-ieee802154")] diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 725878944..19b8647e3 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1503,10 +1503,7 @@ fn test_echo_request_sixlowpan_128_bytes() { &mut sockets, &ieee802154_repr, &request_first_part_packet.into_inner(), - Some(( - &mut iface.fragments.sixlowpan_fragments, - iface.fragments.sixlowpan_fragments_cache_timeout, - )), + &mut iface.fragments ), None ); @@ -1530,10 +1527,7 @@ fn test_echo_request_sixlowpan_128_bytes() { &mut sockets, &ieee802154_repr, &request_second_part, - Some(( - &mut iface.fragments.sixlowpan_fragments, - iface.fragments.sixlowpan_fragments_cache_timeout, - )), + &mut iface.fragments, ); assert_eq!( @@ -1666,10 +1660,7 @@ fn test_sixlowpan_udp_with_fragmentation() { &mut sockets, &ieee802154_repr, udp_first_part, - Some(( - &mut iface.fragments.sixlowpan_fragments, - iface.fragments.sixlowpan_fragments_cache_timeout - )) + &mut iface.fragments ), None ); @@ -1688,10 +1679,7 @@ fn test_sixlowpan_udp_with_fragmentation() { &mut sockets, &ieee802154_repr, udp_second_part, - Some(( - &mut iface.fragments.sixlowpan_fragments, - iface.fragments.sixlowpan_fragments_cache_timeout - )) + &mut iface.fragments ), None ); From 413b205531fdc886213f5d135ea1afd6fc8ad1a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 30 Dec 2022 03:05:37 +0100 Subject: [PATCH 471/566] assembler: remove offset correction. --- src/iface/fragmentation.rs | 10 ---- src/wire/sixlowpan.rs | 98 ++++---------------------------------- 2 files changed, 9 insertions(+), 99 deletions(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 26b756d8c..6c3a3d75b 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -40,7 +40,6 @@ pub struct PacketAssembler { assembler: Assembler, total_size: Option, expires_at: Instant, - offset_correction: isize, } impl PacketAssembler { @@ -57,7 +56,6 @@ impl PacketAssembler { assembler: Assembler::new(), total_size: None, expires_at: Instant::ZERO, - offset_correction: 0, } } @@ -66,11 +64,6 @@ impl PacketAssembler { self.assembler.clear(); self.total_size = None; self.expires_at = Instant::ZERO; - self.offset_correction = 0; - } - - pub(crate) fn set_offset_correction(&mut self, correction: isize) { - self.offset_correction = correction; } /// Set the total size of the packet assembler. @@ -137,9 +130,6 @@ impl PacketAssembler { /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// place. pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result<(), AssemblerError> { - let offset = offset as isize + self.offset_correction; - let offset = if offset <= 0 { 0 } else { offset as usize }; - #[cfg(not(feature = "alloc"))] if self.buffer.len() < offset + data.len() { return Err(AssemblerError); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 7125711c6..7fca7fba9 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2139,9 +2139,6 @@ pub mod nhc { #[cfg(test)] mod test { - use crate::phy::ChecksumCapabilities; - use crate::time::Duration; - use super::*; #[test] @@ -2178,13 +2175,16 @@ mod test { #[test] fn sixlowpan_three_fragments() { - use crate::iface::ReassemblyBuffer; - use crate::time::Instant; use crate::wire::ieee802154::Frame as Ieee802154Frame; use crate::wire::ieee802154::Repr as Ieee802154Repr; use crate::wire::Ieee802154Address; - let mut frags_cache = ReassemblyBuffer::new(); + let key = frag::Key { + ll_src_addr: Ieee802154Address::Extended([50, 147, 130, 47, 40, 8, 62, 217]), + ll_dst_addr: Ieee802154Address::Extended([26, 11, 66, 66, 66, 66, 66, 66]), + datagram_size: 307, + datagram_tag: 63, + }; let frame1: &[u8] = &[ 0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, @@ -2214,18 +2214,7 @@ mod test { assert_eq!(frag.datagram_tag(), 0x003f); assert_eq!(frag.datagram_offset(), 0); - let key = frag.get_key(&ieee802154_repr); - - let uncompressed = 40 + 8; - let compressed = 5 + 7; - - let assr = frags_cache - .get(&key, Instant::now() + Duration::from_secs(60)) - .unwrap(); - assr.set_total_size(frag.datagram_size() as usize - uncompressed + compressed) - .unwrap(); - assr.set_offset_correction(-((uncompressed - compressed) as isize)); - assr.add(frag.payload(), 0).unwrap(); + assert_eq!(frag.get_key(&ieee802154_repr), key); let frame2: &[u8] = &[ 0x41, 0xcc, 0x93, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, @@ -2255,13 +2244,7 @@ mod test { assert_eq!(frag.datagram_tag(), 0x003f); assert_eq!(frag.datagram_offset(), 136 / 8); - let key = frag.get_key(&ieee802154_repr); - - let assr = frags_cache - .get(&key, Instant::now() + Duration::from_secs(60)) - .unwrap(); - assr.add(frag.payload(), frag.datagram_offset() as usize * 8) - .unwrap(); + assert_eq!(frag.get_key(&ieee802154_repr), key); let frame3: &[u8] = &[ 0x41, 0xcc, 0x94, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9, @@ -2290,69 +2273,6 @@ mod test { assert_eq!(frag.datagram_tag(), 0x003f); assert_eq!(frag.datagram_offset(), 232 / 8); - let key = frag.get_key(&ieee802154_repr); - - let assr = frags_cache - .get(&key, Instant::now() + Duration::from_secs(60)) - .unwrap(); - assr.add(frag.payload(), frag.datagram_offset() as usize * 8) - .unwrap(); - - let assembled_packet = assr.assemble().unwrap(); - - let sixlowpan_frame = SixlowpanPacket::dispatch(assembled_packet).unwrap(); - - let iphc = if let SixlowpanPacket::IphcHeader = sixlowpan_frame { - iphc::Packet::new_checked(assembled_packet).unwrap() - } else { - unreachable!() - }; - - let iphc_repr = iphc::Repr::parse( - &iphc, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - &[], - ) - .unwrap(); - - assert_eq!( - iphc_repr.dst_addr, - ipv6::Address::from_bytes(&[ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, - ]), - ); - assert_eq!( - iphc_repr.ll_dst_addr, - Some(Ieee802154Address::from_bytes(&[ - 0x1a, 0xb, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - ])), - ); - assert_eq!(iphc_repr.next_header, NextHeader::Compressed); - assert_eq!(iphc_repr.hop_limit, 64); - - let sixlowpan_frame = nhc::NhcPacket::dispatch(iphc.payload()).unwrap(); - - let udp_hdr = if let nhc::NhcPacket::UdpHeader = sixlowpan_frame { - nhc::UdpNhcPacket::new_checked(iphc.payload()).unwrap() - } else { - unreachable!() - }; - - let payload = udp_hdr.payload(); - assert_eq!(String::from_utf8_lossy(payload), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dui odio, iaculis vel rutrum at, tristique non nunc erat curae. \n"); - - let udp_repr = nhc::UdpNhcRepr::parse( - &udp_hdr, - &iphc_repr.src_addr, - &iphc_repr.dst_addr, - &ChecksumCapabilities::default(), - ) - .unwrap(); - - assert_eq!(udp_repr.src_port, 53855); - assert_eq!(udp_repr.dst_port, 6969); - assert_eq!(udp_hdr.checksum(), Some(0xb46b)); + assert_eq!(frag.get_key(&ieee802154_repr), key); } } From 1dc1451d8cd0dacdeaaf079edf77f30130d606c3 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Wed, 4 Jan 2023 20:23:27 +0100 Subject: [PATCH 472/566] Make IpEndpoints parse unspecified IPv6 IPs The previous solution only works for IP addresses or CIDRs, but not for IpEndpoint --- src/parsers.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/parsers.rs b/src/parsers.rs index bb17e35d9..16419ab5c 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -165,7 +165,6 @@ impl<'a> Parser<'a> { (head, tail): (&mut [u16; 8], &mut [u16; 6]), (head_idx, tail_idx): (&mut usize, &mut usize), mut use_tail: bool, - is_cidr: bool, ) -> Result<()> { let double_colon = match self.try_do(|p| p.accept_str(b"::")) { Some(_) if !use_tail && *head_idx < 7 => { @@ -221,7 +220,7 @@ impl<'a> Parser<'a> { // Tail or head section is too long Err(()) } - None if double_colon && (is_cidr || self.pos == self.data.len()) => { + None if double_colon => { // The address ends with "::". E.g. 1234:: or :: Ok(()) } @@ -242,12 +241,12 @@ impl<'a> Parser<'a> { Ok(()) } else { // Continue recursing - self.accept_ipv6_part((head, tail), (head_idx, tail_idx), use_tail, is_cidr) + self.accept_ipv6_part((head, tail), (head_idx, tail_idx), use_tail) } } #[cfg(feature = "proto-ipv6")] - fn accept_ipv6(&mut self, is_cidr: bool) -> Result { + fn accept_ipv6(&mut self) -> Result { // IPv6 addresses may contain a "::" to indicate a series of // 16 bit sections that evaluate to 0. E.g. // @@ -272,7 +271,6 @@ impl<'a> Parser<'a> { (&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, - is_cidr, )?; // We need to copy the tail portion (the portion following the "::") to the @@ -309,7 +307,7 @@ impl<'a> Parser<'a> { #[cfg(feature = "proto-ipv6")] #[allow(clippy::single_match)] - match self.try_do(|p| p.accept_ipv6(false)) { + match self.try_do(|p| p.accept_ipv6()) { Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)), None => (), } @@ -338,7 +336,7 @@ impl<'a> Parser<'a> { fn accept_ipv6_endpoint(&mut self) -> Result { if self.lookahead_char(b'[') { self.accept_char(b'[')?; - let ip = self.accept_ipv6(false)?; + let ip = self.accept_ipv6()?; self.accept_char(b']')?; self.accept_char(b':')?; let port = self.accept_number(5, 65535, false)?; @@ -348,7 +346,7 @@ impl<'a> Parser<'a> { port: port as u16, }) } else { - let ip = self.accept_ipv6(false)?; + let ip = self.accept_ipv6()?; Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0, @@ -401,7 +399,7 @@ impl FromStr for Ipv6Address { /// Parse a string representation of an IPv6 address. fn from_str(s: &str) -> Result { - Parser::new(s).until_eof(|p| p.accept_ipv6(false)) + Parser::new(s).until_eof(|p| p.accept_ipv6()) } } @@ -437,7 +435,7 @@ impl FromStr for Ipv6Cidr { fn from_str(s: &str) -> Result { // https://tools.ietf.org/html/rfc4291#section-2.3 Parser::new(s).until_eof(|p| { - let ip = p.accept_ipv6(true)?; + let ip = p.accept_ipv6()?; p.accept_char(b'/')?; let prefix_len = p.accept_number(3, 129, false)? as u8; Ok(Ipv6Cidr::new(ip, prefix_len)) @@ -756,5 +754,12 @@ mod test { port: 12345 }) ); + assert_eq!( + IpEndpoint::from_str("[::]:12345"), + Ok(IpEndpoint { + addr: IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), + port: 12345 + }) + ); } } From 5cd00222fcb91f5e190921825b4c10bbc282a6ea Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Fri, 13 Jan 2023 13:05:46 +0000 Subject: [PATCH 473/566] Add peek and peek_slice functions to RawSocket --- src/socket/raw.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/socket/raw.rs b/src/socket/raw.rs index f07a9596d..aeb189d95 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -247,6 +247,37 @@ impl<'a> Socket<'a> { Ok(length) } + /// Peek at a packet in the receive buffer and return a pointer to the + /// payload without removing the packet from the receive buffer. + /// This function otherwise behaves identically to [recv](#method.recv). + /// + /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. + pub fn peek(&mut self) -> Result<&[u8], RecvError> { + self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map( + |((), payload_buf)| { + net_trace!( + "raw:{}:{}: peek {} buffered octets", + self.ip_version, + self.ip_protocol, + payload_buf.len() + ); + payload_buf + }, + ) + } + + /// Peek at a packet in the receive buffer, copy the payload into the given slice, + /// and return the amount of octets copied without removing the packet from the receive buffer. + /// This function otherwise behaves identically to [recv_slice](#method.recv_slice). + /// + /// See also [peek](#method.peek). + pub fn peek_slice(&mut self, data: &mut [u8]) -> Result { + let buffer = self.peek()?; + let length = min(data.len(), buffer.len()); + data[..length].copy_from_slice(&buffer[..length]); + Ok(length) + } + pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool { if ip_repr.version() != self.ip_version { return false; From af73ab69a295e7725fb0eddaefecc8130e7c7d43 Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Fri, 13 Jan 2023 13:13:04 +0000 Subject: [PATCH 474/566] Appease rustfmt --- src/socket/raw.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/socket/raw.rs b/src/socket/raw.rs index aeb189d95..9c2ed2ba0 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -253,8 +253,10 @@ impl<'a> Socket<'a> { /// /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. pub fn peek(&mut self) -> Result<&[u8], RecvError> { - self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map( - |((), payload_buf)| { + self.rx_buffer + .peek() + .map_err(|_| RecvError::Exhausted) + .map(|((), payload_buf)| { net_trace!( "raw:{}:{}: peek {} buffered octets", self.ip_version, @@ -262,8 +264,7 @@ impl<'a> Socket<'a> { payload_buf.len() ); payload_buf - }, - ) + }) } /// Peek at a packet in the receive buffer, copy the payload into the given slice, From 512bba4b3224bafd75293093b3e18a5129e6ec8a Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Fri, 13 Jan 2023 13:54:26 +0000 Subject: [PATCH 475/566] Change rawsocket peek to look more like recv This fixes a borrowck error that the current implementation caused in my project, which was somehow not picked up by the CI/CD tests. --- src/socket/raw.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 9c2ed2ba0..455d8f4d3 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -253,18 +253,16 @@ impl<'a> Socket<'a> { /// /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. pub fn peek(&mut self) -> Result<&[u8], RecvError> { - self.rx_buffer - .peek() - .map_err(|_| RecvError::Exhausted) - .map(|((), payload_buf)| { - net_trace!( - "raw:{}:{}: peek {} buffered octets", - self.ip_version, - self.ip_protocol, - payload_buf.len() - ); - payload_buf - }) + let ((), packet_buf) = self.rx_buffer.peek().map_err(|_| RecvError::Exhausted)?; + + net_trace!( + "raw:{}:{}: receive {} buffered octets", + self.ip_version, + self.ip_protocol, + packet_buf.len() + ); + + Ok(packet_buf) } /// Peek at a packet in the receive buffer, copy the payload into the given slice, From 6197033192c4b012ed87ba3f36f532ee4fb30257 Mon Sep 17 00:00:00 2001 From: Nicholas Cyprus Date: Fri, 13 Jan 2023 20:40:51 +0000 Subject: [PATCH 476/566] Add tests for raw socket peek --- src/socket/raw.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 455d8f4d3..64f5ab2cd 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -566,6 +566,22 @@ mod test { assert!(socket.accepts(&$hdr)); socket.process(&mut cx, &$hdr, &buffer); } + + #[test] + fn test_peek_truncated_slice() { + let mut socket = $socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + + assert!(socket.accepts(&$hdr)); + socket.process(&mut cx, &$hdr, &$payload); + + let mut slice = [0; 4]; + assert_eq!(socket.peek_slice(&mut slice[..]), Ok(4)); + assert_eq!(&slice, &$packet[..slice.len()]); + assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4)); + assert_eq!(&slice, &$packet[..slice.len()]); + assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted)); + } } }; } @@ -694,6 +710,59 @@ mod test { } } + #[test] + fn test_peek_process() { + #[cfg(feature = "proto-ipv4")] + { + let mut socket = ipv4_locals::socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + + let mut cksumd_packet = ipv4_locals::PACKET_BYTES; + Ipv4Packet::new_unchecked(&mut cksumd_packet).fill_checksum(); + + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); + assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); + socket.process( + &mut cx, + &ipv4_locals::HEADER_REPR, + &ipv4_locals::PACKET_PAYLOAD, + ); + + assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); + socket.process( + &mut cx, + &ipv4_locals::HEADER_REPR, + &ipv4_locals::PACKET_PAYLOAD, + ); + assert_eq!(socket.peek(), Ok(&cksumd_packet[..])); + assert_eq!(socket.recv(), Ok(&cksumd_packet[..])); + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); + } + #[cfg(feature = "proto-ipv6")] + { + let mut socket = ipv6_locals::socket(buffer(1), buffer(0)); + let mut cx = Context::mock(); + + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); + assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); + socket.process( + &mut cx, + &ipv6_locals::HEADER_REPR, + &ipv6_locals::PACKET_PAYLOAD, + ); + + assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); + socket.process( + &mut cx, + &ipv6_locals::HEADER_REPR, + &ipv6_locals::PACKET_PAYLOAD, + ); + assert_eq!(socket.peek(), Ok(&ipv6_locals::PACKET_BYTES[..])); + assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..])); + assert_eq!(socket.peek(), Err(RecvError::Exhausted)); + } + } + #[test] fn test_doesnt_accept_wrong_proto() { #[cfg(feature = "proto-ipv4")] From 71438338ee74fb73beb6f13013a986d611887865 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 14 Jan 2023 21:53:27 +0100 Subject: [PATCH 477/566] wire/udp: make `emit_header` not able to calculate checksum. Currently it's not working properly, because you can call it when the UDP payload is not written yet to the buffer. I've changed it to not be able to, and clarified in docs that it's for internal-packet-use only. It's only used for 6lowpan decompression, and in that case we don't want to calculate and then later check the checksum. --- src/iface/interface/sixlowpan.rs | 10 ++-------- src/wire/udp.rs | 23 +++++++---------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 15f0166a7..84dacecba 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -250,17 +250,11 @@ impl<'a> InterfaceInner<'a> { &ChecksumCapabilities::ignored(), )?; - let mut udp_emit_packet = UdpPacket::new_unchecked( + let mut udp = UdpPacket::new_unchecked( &mut buffer[..udp_repr.0.header_len() + iphc.payload().len() - udp_repr.header_len()], ); - udp_repr.0.emit_header( - &mut udp_emit_packet, - &iphc_repr.src_addr.into(), - &iphc_repr.dst_addr.into(), - ipv6_repr.payload_len - 8, - &ChecksumCapabilities::ignored(), - ); + udp_repr.0.emit_header(&mut udp, ipv6_repr.payload_len - 8); buffer[8..].copy_from_slice(&iphc.payload()[udp_repr.header_len()..]); } diff --git a/src/wire/udp.rs b/src/wire/udp.rs index f9c0186da..8f0095155 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -251,27 +251,18 @@ impl Repr { } /// Emit a high-level representation into an User Datagram Protocol packet. - pub fn emit_header( - &self, - packet: &mut Packet<&mut T>, - src_addr: &IpAddress, - dst_addr: &IpAddress, - payload_len: usize, - checksum_caps: &ChecksumCapabilities, - ) where + /// + /// This never calculates the checksum, and is intended for internal-use only, + /// not for packets that are going to be actually sent over the network. For + /// example, when decompressing 6lowpan. + pub(crate) fn emit_header(&self, packet: &mut Packet<&mut T>, payload_len: usize) + where T: AsRef<[u8]> + AsMut<[u8]>, { packet.set_src_port(self.src_port); packet.set_dst_port(self.dst_port); packet.set_len((HEADER_LEN + payload_len) as u16); - - if checksum_caps.udp.tx() { - packet.fill_checksum(src_addr, dst_addr) - } else { - // make sure we get a consistently zeroed checksum, - // since implementations might rely on it - packet.set_checksum(0); - } + packet.set_checksum(0); } /// Emit a high-level representation into an User Datagram Protocol packet. From df3556eb3cdf58af389002237428fb7c59086838 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 30 Dec 2022 03:25:45 +0100 Subject: [PATCH 478/566] wire/sixlowpan: use wire error everywhere. --- src/wire/sixlowpan.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 7fca7fba9..310ffb127 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -220,10 +220,8 @@ pub mod frag { //! [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3 use super::{DISPATCH_FIRST_FRAGMENT_HEADER, DISPATCH_FRAGMENT_HEADER}; - use crate::{ - wire::{Ieee802154Address, Ieee802154Repr}, - Error, Result, - }; + use crate::wire::{Error, Result}; + use crate::wire::{Ieee802154Address, Ieee802154Repr}; use byteorder::{ByteOrder, NetworkEndian}; /// Key used for identifying all the link fragments that belong to the same packet. @@ -298,18 +296,18 @@ pub mod frag { let dispatch = packet.dispatch(); if dispatch != DISPATCH_FIRST_FRAGMENT_HEADER && dispatch != DISPATCH_FRAGMENT_HEADER { - return Err(Error::Malformed); + return Err(Error); } Ok(packet) } /// Ensure that no accessor method will panic if called. - /// Returns `Err(Error::Truncated)` if the buffer is too short. + /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.is_empty() { - return Err(Error::Truncated); + return Err(Error); } match self.dispatch() { @@ -317,13 +315,11 @@ pub mod frag { Ok(()) } DISPATCH_FIRST_FRAGMENT_HEADER if buffer.len() < FIRST_FRAGMENT_HEADER_SIZE => { - Err(Error::Truncated) + Err(Error) } DISPATCH_FRAGMENT_HEADER if buffer.len() >= NEXT_FRAGMENT_HEADER_SIZE => Ok(()), - DISPATCH_FRAGMENT_HEADER if buffer.len() < NEXT_FRAGMENT_HEADER_SIZE => { - Err(Error::Truncated) - } - _ => Err(Error::Unrecognized), + DISPATCH_FRAGMENT_HEADER if buffer.len() < NEXT_FRAGMENT_HEADER_SIZE => Err(Error), + _ => Err(Error), } } @@ -441,7 +437,7 @@ pub mod frag { tag, offset: packet.datagram_offset(), }), - _ => Err(Error::Malformed), + _ => Err(Error), } } From e782ada6a0d22d21544ed1eba0338233cae30fd3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 30 Dec 2022 03:31:33 +0100 Subject: [PATCH 479/566] iface/route: use own error type. --- src/iface/mod.rs | 2 +- src/iface/route.rs | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 2193bbfd1..96d01d46d 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -19,7 +19,7 @@ pub(crate) use self::neighbor::Answer as NeighborAnswer; pub use self::neighbor::Cache as NeighborCache; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] pub use self::neighbor::Neighbor; -pub use self::route::{Route, Routes}; +pub use self::route::{Route, RouteTableFull, Routes}; pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] diff --git a/src/iface/route.rs b/src/iface/route.rs index ee55c89c8..2df32c951 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -6,10 +6,13 @@ use crate::wire::{IpAddress, IpCidr}; use crate::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr}; -use crate::{Error, Result}; pub const MAX_ROUTE_COUNT: usize = 4; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct RouteTableFull; + /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -75,11 +78,14 @@ impl Routes { /// /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv4")] - pub fn add_default_ipv4_route(&mut self, gateway: Ipv4Address) -> Result> { + pub fn add_default_ipv4_route( + &mut self, + gateway: Ipv4Address, + ) -> Result, RouteTableFull> { let old = self.remove_default_ipv4_route(); self.storage .push(Route::new_ipv4_gateway(gateway)) - .map_err(|_| Error::Exhausted)?; + .map_err(|_| RouteTableFull)?; Ok(old) } @@ -87,11 +93,14 @@ impl Routes { /// /// On success, returns the previous default route, if any. #[cfg(feature = "proto-ipv6")] - pub fn add_default_ipv6_route(&mut self, gateway: Ipv6Address) -> Result> { + pub fn add_default_ipv6_route( + &mut self, + gateway: Ipv6Address, + ) -> Result, RouteTableFull> { let old = self.remove_default_ipv6_route(); self.storage .push(Route::new_ipv6_gateway(gateway)) - .map_err(|_| Error::Exhausted)?; + .map_err(|_| RouteTableFull)?; Ok(old) } From 0102a4b741655d7f6fd64110ceda5177580b3952 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 1 Jan 2023 21:59:13 +0100 Subject: [PATCH 480/566] phy: make consume infallible, move timestamp to receive/transmit. --- examples/tcpdump.rs | 17 ++- src/iface/interface/ethernet.rs | 2 +- src/iface/interface/ipv4.rs | 2 +- src/iface/interface/mod.rs | 30 +++--- src/iface/interface/sixlowpan.rs | 40 +++---- src/iface/interface/tests.rs | 31 +++--- src/phy/fault_injector.rs | 173 +++++++++++++------------------ src/phy/fuzz_injector.rs | 21 ++-- src/phy/loopback.rs | 13 ++- src/phy/mod.rs | 37 ++++--- src/phy/pcap_writer.rs | 73 +++++++------ src/phy/raw_socket.rs | 20 ++-- src/phy/tracer.rs | 52 ++++------ src/phy/tuntap_interface.rs | 21 ++-- 14 files changed, 244 insertions(+), 288 deletions(-) diff --git a/examples/tcpdump.rs b/examples/tcpdump.rs index 6bdf35975..2baf376e1 100644 --- a/examples/tcpdump.rs +++ b/examples/tcpdump.rs @@ -10,15 +10,12 @@ fn main() { let mut socket = RawSocket::new(ifname.as_ref(), smoltcp::phy::Medium::Ethernet).unwrap(); loop { phy_wait(socket.as_raw_fd(), None).unwrap(); - let (rx_token, _) = socket.receive().unwrap(); - rx_token - .consume(Instant::now(), |buffer| { - println!( - "{}", - PrettyPrinter::>::new("", &buffer) - ); - Ok(()) - }) - .unwrap(); + let (rx_token, _) = socket.receive(Instant::now()).unwrap(); + rx_token.consume(|buffer| { + println!( + "{}", + PrettyPrinter::>::new("", &buffer) + ); + }) } } diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index b633c0a46..1e67522df 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -69,7 +69,7 @@ impl<'i> InterfaceInner<'i> { F: FnOnce(EthernetFrame<&mut [u8]>), { let tx_len = EthernetFrame::<&[u8]>::buffer_len(buffer_len); - tx_token.consume(self.now, tx_len, |tx_buffer| { + tx_token.consume(tx_len, |tx_buffer| { debug_assert!(tx_buffer.as_ref().len() == tx_len); let mut frame = EthernetFrame::new_unchecked(tx_buffer); diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index fcbde4748..0e69df736 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -469,7 +469,7 @@ impl<'a> InterfaceInner<'a> { Ok(()) }; - tx_token.consume(self.now, tx_len, |mut tx_buffer| { + tx_token.consume(tx_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 8eec9259a..a4fc50fca 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -927,7 +927,7 @@ impl<'a> Interface<'a> { } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; + let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { @@ -963,7 +963,7 @@ impl<'a> Interface<'a> { Ok(false) } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet - let tx_token = device.transmit().ok_or(Error::Exhausted)?; + let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; Ok(true) } else { @@ -1161,8 +1161,8 @@ impl<'a> Interface<'a> { out_packets: _out_packets, } = self; - while let Some((rx_token, tx_token)) = device.receive() { - let res = rx_token.consume(inner.now, |frame| { + while let Some((rx_token, tx_token)) = device.receive(inner.now) { + rx_token.consume(|frame| { match inner.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { @@ -1195,12 +1195,7 @@ impl<'a> Interface<'a> { } } processed_any = true; - Ok(()) }); - - if let Err(err) = res { - net_debug!("Failed to consume RX token: {}", err); - } } processed_any @@ -1229,7 +1224,7 @@ impl<'a> Interface<'a> { let mut neighbor_addr = None; let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); - let t = device.transmit().ok_or_else(|| { + let t = device.transmit(inner.now).ok_or_else(|| { net_debug!("failed to transmit IP: {}", Error::Exhausted); Error::Exhausted })?; @@ -1328,7 +1323,7 @@ impl<'a> Interface<'a> { } if self.inner.now >= timeout => { if let Some(pkt) = self.inner.igmp_report_packet(version, group) { // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; + let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; } @@ -1352,7 +1347,8 @@ impl<'a> Interface<'a> { Some(addr) => { if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { // Send initial membership report - let tx_token = device.transmit().ok_or(Error::Exhausted)?; + let tx_token = + device.transmit(self.inner.now).ok_or(Error::Exhausted)?; self.inner.dispatch_ip(tx_token, pkt, None)?; } @@ -1402,7 +1398,7 @@ impl<'a> Interface<'a> { } = &self.out_packets.ipv4_out_packet; if *packet_len > *sent_bytes { - match device.transmit() { + match device.transmit(self.inner.now) { Some(tx_token) => self .inner .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet), @@ -1440,7 +1436,7 @@ impl<'a> Interface<'a> { } = &self.out_packets.sixlowpan_out_packet; if *packet_len > *sent_bytes { - match device.transmit() { + match device.transmit(self.inner.now) { Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( tx_token, &mut self.out_packets.sixlowpan_out_packet, @@ -2246,7 +2242,7 @@ impl<'a> InterfaceInner<'a> { } // Transmit the first packet. - tx_token.consume(self.now, tx_len, |mut tx_buffer| { + tx_token.consume(tx_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { emit_ethernet(&ip_repr, tx_buffer)?; @@ -2271,7 +2267,7 @@ impl<'a> InterfaceInner<'a> { } } else { // No fragmentation is required. - tx_token.consume(self.now, total_len, |mut tx_buffer| { + tx_token.consume(total_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { emit_ethernet(&ip_repr, tx_buffer)?; @@ -2285,7 +2281,7 @@ impl<'a> InterfaceInner<'a> { } // We don't support IPv6 fragmentation yet. #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => tx_token.consume(self.now, total_len, |mut tx_buffer| { + IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { emit_ethernet(&ip_repr, tx_buffer)?; diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 84dacecba..8c6f8a354 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -488,27 +488,22 @@ impl<'a> InterfaceInner<'a> { *sent_bytes = frag1_size; *datagram_offset = frag1_size + header_diff; - tx_token.consume( - self.now, - ieee_len + frag1.buffer_len() + frag1_size, - |mut tx_buf| { - // Add the IEEE header. - let mut ieee_packet = - Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); - ieee_repr.emit(&mut ieee_packet); - tx_buf = &mut tx_buf[ieee_len..]; - - // Add the first fragment header - let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); - frag1.emit(&mut frag1_packet); - tx_buf = &mut tx_buf[frag1.buffer_len()..]; - - // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); - - Ok(()) - }, - ) + tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| { + // Add the IEEE header. + let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); + ieee_repr.emit(&mut ieee_packet); + tx_buf = &mut tx_buf[ieee_len..]; + + // Add the first fragment header + let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf); + frag1.emit(&mut frag1_packet); + tx_buf = &mut tx_buf[frag1.buffer_len()..]; + + // Add the buffer part. + tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + + Ok(()) + }) } #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] @@ -520,7 +515,7 @@ impl<'a> InterfaceInner<'a> { } } else { // We don't need fragmentation, so we emit everything to the TX token. - tx_token.consume(self.now, total_size + ieee_len, |mut tx_buf| { + tx_token.consume(total_size + ieee_len, |mut tx_buf| { let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); ieee_repr.emit(&mut ieee_packet); tx_buf = &mut tx_buf[ieee_len..]; @@ -623,7 +618,6 @@ impl<'a> InterfaceInner<'a> { let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); tx_token.consume( - self.now, ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, |mut tx_buf| { let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 19b8647e3..32dde5888 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -9,7 +9,6 @@ use crate::iface::NeighborCache; use crate::phy::{ChecksumCapabilities, Loopback}; #[cfg(feature = "proto-igmp")] use crate::time::Instant; -use crate::{Error, Result}; #[allow(unused)] fn fill_slice(s: &mut [u8], val: u8) { @@ -129,12 +128,10 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { #[cfg(feature = "proto-igmp")] fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); - while let Some((rx, _tx)) = device.receive() { - rx.consume(timestamp, |pkt| { + while let Some((rx, _tx)) = device.receive(timestamp) { + rx.consume(|pkt| { pkts.push(pkt.to_vec()); - Ok(()) - }) - .unwrap(); + }); } pkts } @@ -144,11 +141,12 @@ fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { struct MockTxToken; impl TxToken for MockTxToken { - fn consume(self, _: Instant, _: usize, _: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - Err(Error::Unaddressable) + let mut junk = [0; 1536]; + f(&mut junk[..len]) } } @@ -1206,13 +1204,10 @@ fn test_handle_igmp() { ]; { // Transmit GENERAL_QUERY_BYTES into loopback - let tx_token = device.transmit().unwrap(); - tx_token - .consume(timestamp, GENERAL_QUERY_BYTES.len(), |buffer| { - buffer.copy_from_slice(GENERAL_QUERY_BYTES); - Ok(()) - }) - .unwrap(); + let tx_token = device.transmit(timestamp).unwrap(); + tx_token.consume(GENERAL_QUERY_BYTES.len(), |buffer| { + buffer.copy_from_slice(GENERAL_QUERY_BYTES); + }); } // Trigger processing until all packets received through the // loopback have been processed, including responses to @@ -1562,7 +1557,7 @@ fn test_echo_request_sixlowpan_128_bytes() { Instant::now(), ); - let tx_token = device.transmit().unwrap(); + let tx_token = device.transmit(Instant::now()).unwrap(); iface .inner .dispatch_ieee802154( @@ -1702,7 +1697,7 @@ fn test_sixlowpan_udp_with_fragmentation() { )) ); - let tx_token = device.transmit().unwrap(); + let tx_token = device.transmit(Instant::now()).unwrap(); iface .inner .dispatch_ieee802154( diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index d5e22c48f..64d217148 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -1,8 +1,5 @@ -use core::cell::RefCell; - use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::{Duration, Instant}; -use crate::{Error, Result}; // We use our own RNG to stay compatible with #![no_std]. // The use of the RNG below has a slight bias, but it doesn't matter. @@ -96,23 +93,24 @@ impl State { #[derive(Debug)] pub struct FaultInjector { inner: D, - state: RefCell, + state: State, config: Config, + rx_buf: [u8; MTU], } impl FaultInjector { /// Create a fault injector device, using the given random number generator seed. pub fn new(inner: D, seed: u32) -> FaultInjector { - let state = State { - rng_seed: seed, - refilled_at: Instant::from_millis(0), - tx_bucket: 0, - rx_bucket: 0, - }; FaultInjector { inner, - state: RefCell::new(state), + state: State { + rng_seed: seed, + refilled_at: Instant::from_millis(0), + tx_bucket: 0, + rx_bucket: 0, + }, config: Config::default(), + rx_buf: [0u8; MTU], } } @@ -190,13 +188,13 @@ impl FaultInjector { /// Set the interval for packet rate limiting, in milliseconds. pub fn set_bucket_interval(&mut self, interval: Duration) { - self.state.borrow_mut().refilled_at = Instant::from_millis(0); + self.state.refilled_at = Instant::from_millis(0); self.config.interval = interval } } impl Device for FaultInjector { - type RxToken<'a> = RxToken<'a, D::RxToken<'a>> + type RxToken<'a> = RxToken<'a> where Self: 'a; type TxToken<'a> = TxToken<'a, D::TxToken<'a>> @@ -211,117 +209,94 @@ impl Device for FaultInjector { caps } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let &mut Self { - ref mut inner, - ref state, - config, - } = self; - inner.receive().map(|(rx_token, tx_token)| { - let rx = RxToken { - state, - config, - token: rx_token, - corrupt: [0; MTU], - }; - let tx = TxToken { - state, - config, - token: tx_token, - junk: [0; MTU], - }; - (rx, tx) - }) + fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let (rx_token, tx_token) = self.inner.receive(timestamp)?; + + let len = super::RxToken::consume(rx_token, |buffer| { + if (self.config.max_size > 0 && buffer.len() > self.config.max_size) + || buffer.len() > self.rx_buf.len() + { + net_trace!("rx: dropping a packet that is too large"); + return None; + } + self.rx_buf[..buffer.len()].copy_from_slice(buffer); + Some(buffer.len()) + })?; + + let buf = &mut self.rx_buf[..len]; + + if self.state.maybe(self.config.drop_pct) { + net_trace!("rx: randomly dropping a packet"); + return None; + } + + if !self.state.maybe_receive(&self.config, timestamp) { + net_trace!("rx: dropping a packet because of rate limiting"); + return None; + } + + if self.state.maybe(self.config.corrupt_pct) { + net_trace!("rx: randomly corrupting a packet"); + self.state.corrupt(&mut buf[..]); + } + + let rx = RxToken { buf }; + let tx = TxToken { + state: &mut self.state, + config: self.config, + token: tx_token, + junk: [0; MTU], + timestamp, + }; + Some((rx, tx)) } - fn transmit(&mut self) -> Option> { - let &mut Self { - ref mut inner, - ref state, - config, - } = self; - inner.transmit().map(|token| TxToken { - state, - config, + fn transmit(&mut self, timestamp: Instant) -> Option> { + self.inner.transmit(timestamp).map(|token| TxToken { + state: &mut self.state, + config: self.config, token, junk: [0; MTU], + timestamp, }) } } #[doc(hidden)] -pub struct RxToken<'a, Rx: phy::RxToken> { - state: &'a RefCell, - config: Config, - token: Rx, - corrupt: [u8; MTU], +pub struct RxToken<'a> { + buf: &'a mut [u8], } -impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { - fn consume(self, timestamp: Instant, f: F) -> Result +impl<'a> phy::RxToken for RxToken<'a> { + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - if self.state.borrow_mut().maybe(self.config.drop_pct) { - net_trace!("rx: randomly dropping a packet"); - return Err(Error::Exhausted); - } - if !self - .state - .borrow_mut() - .maybe_receive(&self.config, timestamp) - { - net_trace!("rx: dropping a packet because of rate limiting"); - return Err(Error::Exhausted); - } - let Self { - token, - config, - state, - mut corrupt, - } = self; - token.consume(timestamp, |buffer| { - if config.max_size > 0 && buffer.as_ref().len() > config.max_size { - net_trace!("rx: dropping a packet that is too large"); - return Err(Error::Exhausted); - } - if state.borrow_mut().maybe(config.corrupt_pct) { - net_trace!("rx: randomly corrupting a packet"); - let mut corrupt = &mut corrupt[..buffer.len()]; - corrupt.copy_from_slice(buffer); - state.borrow_mut().corrupt(&mut corrupt); - f(corrupt) - } else { - f(buffer) - } - }) + f(self.buf) } } #[doc(hidden)] pub struct TxToken<'a, Tx: phy::TxToken> { - state: &'a RefCell, + state: &'a mut State, config: Config, token: Tx, junk: [u8; MTU], + timestamp: Instant, } impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { - fn consume(mut self, timestamp: Instant, len: usize, f: F) -> Result + fn consume(mut self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) { + let drop = if self.state.maybe(self.config.drop_pct) { net_trace!("tx: randomly dropping a packet"); true } else if self.config.max_size > 0 && len > self.config.max_size { net_trace!("tx: dropping a packet that is too large"); true - } else if !self - .state - .borrow_mut() - .maybe_transmit(&self.config, timestamp) - { + } else if !self.state.maybe_transmit(&self.config, self.timestamp) { net_trace!("tx: dropping a packet because of rate limiting"); true } else { @@ -332,16 +307,10 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { return f(&mut self.junk[..len]); } - let Self { - token, - state, - config, - .. - } = self; - token.consume(timestamp, len, |mut buf| { - if state.borrow_mut().maybe(config.corrupt_pct) { + self.token.consume(len, |mut buf| { + if self.state.maybe(self.config.corrupt_pct) { net_trace!("tx: corrupting a packet"); - state.borrow_mut().corrupt(&mut buf) + self.state.corrupt(&mut buf) } f(buf) }) diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 025517955..54bc0b9a4 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -1,6 +1,5 @@ use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::Instant; -use crate::Result; // This could be fixed once associated consts are stable. const MTU: usize = 1536; @@ -62,13 +61,13 @@ where caps } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let &mut Self { ref mut inner, ref fuzz_rx, ref fuzz_tx, } = self; - inner.receive().map(|(rx_token, tx_token)| { + inner.receive(timestamp).map(|(rx_token, tx_token)| { let rx = RxToken { fuzzer: fuzz_rx, token: rx_token, @@ -81,13 +80,13 @@ where }) } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, timestamp: Instant) -> Option> { let &mut Self { ref mut inner, fuzz_rx: _, ref fuzz_tx, } = self; - inner.transmit().map(|token| TxToken { + inner.transmit(timestamp).map(|token| TxToken { fuzzer: fuzz_tx, token: token, }) @@ -101,12 +100,12 @@ pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a> { } impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { - fn consume(self, timestamp: Instant, f: F) -> Result + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { let Self { fuzzer, token } = self; - token.consume(timestamp, |buffer| { + token.consume(|buffer| { fuzzer.fuzz_packet(buffer); f(buffer) }) @@ -120,12 +119,12 @@ pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> { } impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { - fn consume(self, timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { let Self { fuzzer, token } = self; - token.consume(timestamp, len, |buf| { + token.consume(len, |buf| { let result = f(buf); fuzzer.fuzz_packet(buf); result diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index 5bf035419..1f57c0ca4 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -3,7 +3,6 @@ use alloc::vec::Vec; use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; -use crate::Result; /// A loopback device. #[derive(Debug)] @@ -38,7 +37,7 @@ impl Device for Loopback { } } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.queue.pop_front().map(move |buffer| { let rx = RxToken { buffer }; let tx = TxToken { @@ -48,7 +47,7 @@ impl Device for Loopback { }) } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { Some(TxToken { queue: &mut self.queue, }) @@ -61,9 +60,9 @@ pub struct RxToken { } impl phy::RxToken for RxToken { - fn consume(mut self, _timestamp: Instant, f: F) -> Result + fn consume(mut self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { f(&mut self.buffer) } @@ -76,9 +75,9 @@ pub struct TxToken<'a> { } impl<'a> phy::TxToken for TxToken<'a> { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { let mut buffer = Vec::new(); buffer.resize(len, 0); diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 54b109da8..018c0eac2 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -42,12 +42,12 @@ impl phy::Device for StmPhy { type RxToken<'a> = StmPhyRxToken<'a> where Self: 'a; type TxToken<'a> = StmPhyTxToken<'a> where Self: 'a; - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { Some((StmPhyRxToken(&mut self.rx_buffer[..]), StmPhyTxToken(&mut self.tx_buffer[..]))) } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { Some(StmPhyTxToken(&mut self.tx_buffer[..])) } @@ -63,8 +63,8 @@ impl phy::Device for StmPhy { struct StmPhyRxToken<'a>(&'a mut [u8]); impl<'a> phy::RxToken for StmPhyRxToken<'a> { - fn consume(mut self, _timestamp: Instant, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + fn consume(mut self, f: F) -> R + where F: FnOnce(&mut [u8]) -> R { // TODO: receive packet into buffer let result = f(&mut self.0); @@ -76,8 +76,8 @@ impl<'a> phy::RxToken for StmPhyRxToken<'a> { struct StmPhyTxToken<'a>(&'a mut [u8]); impl<'a> phy::TxToken for StmPhyTxToken<'a> { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where F: FnOnce(&mut [u8]) -> Result + fn consume(self, len: usize, f: F) -> R + where F: FnOnce(&mut [u8]) -> R { let result = f(&mut self.0[..len]); println!("tx called {}", len); @@ -90,7 +90,6 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { )] use crate::time::Instant; -use crate::Result; #[cfg(all( any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), @@ -322,10 +321,16 @@ pub trait Device { /// on the contents of the received packet. For example, this makes it possible to /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes /// need to be sent back, without heap allocation. - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + /// + /// The timestamp must be a number of milliseconds, monotonically increasing since an + /// arbitrary moment in time, such as system startup. + fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; /// Construct a transmit token. - fn transmit(&mut self) -> Option>; + /// + /// The timestamp must be a number of milliseconds, monotonically increasing since an + /// arbitrary moment in time, such as system startup. + fn transmit(&mut self, timestamp: Instant) -> Option>; /// Get a description of device capabilities. fn capabilities(&self) -> DeviceCapabilities; @@ -337,12 +342,9 @@ pub trait RxToken { /// /// This method receives a packet and then calls the given closure `f` with the raw /// packet bytes as argument. - /// - /// The timestamp must be a number of milliseconds, monotonically increasing since an - /// arbitrary moment in time, such as system startup. - fn consume(self, timestamp: Instant, f: F) -> Result + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result; + F: FnOnce(&mut [u8]) -> R; } /// A token to transmit a single network packet. @@ -353,10 +355,7 @@ pub trait TxToken { /// closure `f` with a mutable reference to that buffer. The closure should construct /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure /// returns, the transmit buffer is sent out. - /// - /// The timestamp must be a number of milliseconds, monotonically increasing since an - /// arbitrary moment in time, such as system startup. - fn consume(self, timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result; + F: FnOnce(&mut [u8]) -> R; } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index edaf7844f..662ec41bd 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -6,7 +6,6 @@ use std::io::Write; use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::Instant; -use crate::Result; enum_with_unknown! { /// Captured packet header type. @@ -177,30 +176,37 @@ where self.lower.capabilities() } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let sink = &self.sink; let mode = self.mode; - self.lower.receive().map(move |(rx_token, tx_token)| { - let rx = RxToken { - token: rx_token, - sink, - mode, - }; - let tx = TxToken { - token: tx_token, - sink, - mode, - }; - (rx, tx) - }) + self.lower + .receive(timestamp) + .map(move |(rx_token, tx_token)| { + let rx = RxToken { + token: rx_token, + sink, + mode, + timestamp, + }; + let tx = TxToken { + token: tx_token, + sink, + mode, + timestamp, + }; + (rx, tx) + }) } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, timestamp: Instant) -> Option> { let sink = &self.sink; let mode = self.mode; - self.lower - .transmit() - .map(move |token| TxToken { token, sink, mode }) + self.lower.transmit(timestamp).map(move |token| TxToken { + token, + sink, + mode, + timestamp, + }) } } @@ -209,16 +215,17 @@ pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> { token: Rx, sink: &'a RefCell, mode: PcapMode, + timestamp: Instant, } impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> { - fn consume Result>(self, timestamp: Instant, f: F) -> Result { - let Self { token, sink, mode } = self; - token.consume(timestamp, |buffer| { - match mode { - PcapMode::Both | PcapMode::RxOnly => { - sink.borrow_mut().packet(timestamp, buffer.as_ref()) - } + fn consume R>(self, f: F) -> R { + self.token.consume(|buffer| { + match self.mode { + PcapMode::Both | PcapMode::RxOnly => self + .sink + .borrow_mut() + .packet(self.timestamp, buffer.as_ref()), PcapMode::TxOnly => (), } f(buffer) @@ -231,18 +238,20 @@ pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> { token: Tx, sink: &'a RefCell, mode: PcapMode, + timestamp: Instant, } impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> { - fn consume(self, timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let Self { token, sink, mode } = self; - token.consume(timestamp, len, |buffer| { + self.token.consume(len, |buffer| { let result = f(buffer); - match mode { - PcapMode::Both | PcapMode::TxOnly => sink.borrow_mut().packet(timestamp, buffer), + match self.mode { + PcapMode::Both | PcapMode::TxOnly => { + self.sink.borrow_mut().packet(self.timestamp, buffer) + } PcapMode::RxOnly => (), }; result diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 0cc45200d..beadfdac3 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -6,7 +6,6 @@ use std::vec::Vec; use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; use crate::time::Instant; -use crate::Result; /// A socket that captures or transmits the complete frame. #[derive(Debug)] @@ -70,7 +69,7 @@ impl Device for RawSocket { } } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; match lower.recv(&mut buffer[..]) { @@ -87,7 +86,7 @@ impl Device for RawSocket { } } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { Some(TxToken { lower: self.lower.clone(), }) @@ -100,9 +99,9 @@ pub struct RxToken { } impl phy::RxToken for RxToken { - fn consume(mut self, _timestamp: Instant, f: F) -> Result + fn consume(mut self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { f(&mut self.buffer[..]) } @@ -114,17 +113,20 @@ pub struct TxToken { } impl phy::TxToken for TxToken { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); match lower.send(&buffer[..]) { - Ok(_) => result, - Err(err) if err.kind() == io::ErrorKind::WouldBlock => Err(crate::Error::Exhausted), + Ok(_) => {} + Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + net_debug!("phy: tx failed due to WouldBlock") + } Err(err) => panic!("{}", err), } + result } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 934c6f0f0..5e9b7df08 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -2,10 +2,7 @@ use core::fmt; use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; -use crate::{ - wire::pretty_print::{PrettyIndent, PrettyPrint}, - Result, -}; +use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; /// A tracer device. /// @@ -56,38 +53,41 @@ impl Device for Tracer { self.inner.capabilities() } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let &mut Self { ref mut inner, writer, .. } = self; let medium = inner.capabilities().medium; - inner.receive().map(|(rx_token, tx_token)| { + inner.receive(timestamp).map(|(rx_token, tx_token)| { let rx = RxToken { token: rx_token, writer, medium, + timestamp, }; let tx = TxToken { token: tx_token, writer, medium, + timestamp, }; (rx, tx) }) } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, timestamp: Instant) -> Option> { let &mut Self { ref mut inner, writer, } = self; let medium = inner.capabilities().medium; - inner.transmit().map(|tx_token| TxToken { + inner.transmit(timestamp).map(|tx_token| TxToken { token: tx_token, medium, writer, + timestamp, }) } } @@ -97,24 +97,20 @@ pub struct RxToken { token: Rx, writer: fn(Instant, Packet), medium: Medium, + timestamp: Instant, } impl phy::RxToken for RxToken { - fn consume(self, timestamp: Instant, f: F) -> Result + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let Self { - token, - writer, - medium, - } = self; - token.consume(timestamp, |buffer| { - writer( - timestamp, + self.token.consume(|buffer| { + (self.writer)( + self.timestamp, Packet { buffer, - medium, + medium: self.medium, prefix: "<- ", }, ); @@ -128,25 +124,21 @@ pub struct TxToken { token: Tx, writer: fn(Instant, Packet), medium: Medium, + timestamp: Instant, } impl phy::TxToken for TxToken { - fn consume(self, timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { - let Self { - token, - writer, - medium, - } = self; - token.consume(timestamp, len, |buffer| { + self.token.consume(len, |buffer| { let result = f(buffer); - writer( - timestamp, + (self.writer)( + self.timestamp, Packet { buffer, - medium, + medium: self.medium, prefix: "-> ", }, ); diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index 7bc2aa0d9..7f7ae3c05 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -6,7 +6,6 @@ use std::vec::Vec; use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; use crate::time::Instant; -use crate::Result; /// A virtual TUN (IP) or TAP (Ethernet) interface. #[derive(Debug)] @@ -52,7 +51,7 @@ impl Device for TunTapInterface { } } - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; self.mtu]; match lower.recv(&mut buffer[..]) { @@ -69,7 +68,7 @@ impl Device for TunTapInterface { } } - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { Some(TxToken { lower: self.lower.clone(), }) @@ -82,9 +81,9 @@ pub struct RxToken { } impl phy::RxToken for RxToken { - fn consume(mut self, _timestamp: Instant, f: F) -> Result + fn consume(mut self, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { f(&mut self.buffer[..]) } @@ -96,14 +95,20 @@ pub struct TxToken { } impl phy::TxToken for TxToken { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> Result, + F: FnOnce(&mut [u8]) -> R, { let mut lower = self.lower.borrow_mut(); let mut buffer = vec![0; len]; let result = f(&mut buffer); - lower.send(&buffer[..]).unwrap(); + match lower.send(&buffer[..]) { + Ok(_) => {} + Err(err) if err.kind() == io::ErrorKind::WouldBlock => { + net_debug!("phy: tx failed due to WouldBlock") + } + Err(err) => panic!("{}", err), + } result } } From 836082cee6257da2beda81e86c987ae4e6b53e52 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 1 Jan 2023 22:06:40 +0100 Subject: [PATCH 481/566] Remove some self destructurings, not needed anymore in Rust 2021. --- src/iface/interface/mod.rs | 95 +++++++++++++++++++----------------- src/phy/fuzz_injector.rs | 10 ++-- src/storage/packet_buffer.rs | 18 ++----- 3 files changed, 59 insertions(+), 64 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index a4fc50fca..e9588ec2d 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1155,40 +1155,49 @@ impl<'a> Interface<'a> { D: Device + ?Sized, { let mut processed_any = false; - let Self { - inner, - fragments: ref mut _fragments, - out_packets: _out_packets, - } = self; - while let Some((rx_token, tx_token)) = device.receive(inner.now) { + while let Some((rx_token, tx_token)) = device.receive(self.inner.now) { rx_token.consume(|frame| { - match inner.caps.medium { + match self.inner.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch(tx_token, packet, Some(_out_packets)) { + if let Some(packet) = + self.inner + .process_ethernet(sockets, &frame, &mut self.fragments) + { + if let Err(err) = + self.inner + .dispatch(tx_token, packet, Some(&mut self.out_packets)) + { net_debug!("Failed to send response: {}", err); } } } #[cfg(feature = "medium-ip")] Medium::Ip => { - if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { - if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(_out_packets)) - { + if let Some(packet) = + self.inner.process_ip(sockets, &frame, &mut self.fragments) + { + if let Err(err) = self.inner.dispatch_ip( + tx_token, + packet, + Some(&mut self.out_packets), + ) { net_debug!("Failed to send response: {}", err); } } } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = inner.process_ieee802154(sockets, &frame, _fragments) + if let Some(packet) = + self.inner + .process_ieee802154(sockets, &frame, &mut self.fragments) { - if let Err(err) = - inner.dispatch_ip(tx_token, packet, Some(_out_packets)) - { + if let Err(err) = self.inner.dispatch_ip( + tx_token, + packet, + Some(&mut self.out_packets), + ) { net_debug!("Failed to send response: {}", err); } } @@ -1205,18 +1214,13 @@ impl<'a> Interface<'a> { where D: Device + ?Sized, { - let Self { - inner, - out_packets: _out_packets, - .. - } = self; let _caps = device.capabilities(); let mut emitted_any = false; for item in sockets.items_mut() { if !item .meta - .egress_permitted(inner.now, |ip_addr| inner.has_neighbor(&ip_addr)) + .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr)) { continue; } @@ -1233,7 +1237,7 @@ impl<'a> Interface<'a> { feature = "proto-ipv4-fragmentation", feature = "proto-sixlowpan-fragmentation" ))] - inner.dispatch_ip(t, response, Some(_out_packets))?; + inner.dispatch_ip(t, response, Some(&mut self.out_packets))?; #[cfg(not(any( feature = "proto-ipv4-fragmentation", @@ -1248,38 +1252,41 @@ impl<'a> Interface<'a> { let result = match &mut item.socket { #[cfg(feature = "socket-raw")] - Socket::Raw(socket) => socket.dispatch(inner, |inner, response| { + Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, response| { respond(inner, IpPacket::Raw(response)) }), #[cfg(feature = "socket-icmp")] - Socket::Icmp(socket) => socket.dispatch(inner, |inner, response| match response { - #[cfg(feature = "proto-ipv4")] - (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { - respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) - } - #[cfg(feature = "proto-ipv6")] - (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { - respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) - } - #[allow(unreachable_patterns)] - _ => unreachable!(), - }), + Socket::Icmp(socket) => { + socket.dispatch(&mut self.inner, |inner, response| match response { + #[cfg(feature = "proto-ipv4")] + (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { + respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) + } + #[cfg(feature = "proto-ipv6")] + (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { + respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) + } + #[allow(unreachable_patterns)] + _ => unreachable!(), + }) + } #[cfg(feature = "socket-udp")] - Socket::Udp(socket) => socket.dispatch(inner, |inner, response| { + Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, response| { respond(inner, IpPacket::Udp(response)) }), #[cfg(feature = "socket-tcp")] - Socket::Tcp(socket) => socket.dispatch(inner, |inner, response| { + Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, response| { respond(inner, IpPacket::Tcp(response)) }), #[cfg(feature = "socket-dhcpv4")] - Socket::Dhcpv4(socket) => socket.dispatch(inner, |inner, response| { + Socket::Dhcpv4(socket) => socket.dispatch(&mut self.inner, |inner, response| { respond(inner, IpPacket::Dhcpv4(response)) }), #[cfg(feature = "socket-dns")] - Socket::Dns(ref mut socket) => socket.dispatch(inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) - }), + Socket::Dns(ref mut socket) => socket + .dispatch(&mut self.inner, |inner, response| { + respond(inner, IpPacket::Udp(response)) + }), }; match result { @@ -1290,7 +1297,7 @@ impl<'a> Interface<'a> { // mechanism, we would spin on every socket that has yet to discover its // neighbor. item.meta.neighbor_missing( - inner.now, + self.inner.now, neighbor_addr.expect("non-IP response packet"), ); break; diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 54bc0b9a4..a4f4e19f9 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -104,9 +104,8 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { where F: FnOnce(&mut [u8]) -> R, { - let Self { fuzzer, token } = self; - token.consume(|buffer| { - fuzzer.fuzz_packet(buffer); + self.token.consume(|buffer| { + self.fuzzer.fuzz_packet(buffer); f(buffer) }) } @@ -123,10 +122,9 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { where F: FnOnce(&mut [u8]) -> R, { - let Self { fuzzer, token } = self; - token.consume(len, |buf| { + self.token.consume(len, |buf| { let result = f(buf); - fuzzer.fuzz_packet(buf); + self.fuzzer.fuzz_packet(buf); result }) } diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index 1f748af8e..eb8c0627f 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -164,15 +164,10 @@ impl<'a, H> PacketBuffer<'a, H> { } fn dequeue_padding(&mut self) { - let Self { - ref mut metadata_ring, - ref mut payload_ring, - } = *self; - - let _ = metadata_ring.dequeue_one_with(|metadata| { + let _ = self.metadata_ring.dequeue_one_with(|metadata| { if metadata.is_padding() { // note(discard): function does not use value of dequeued padding bytes - let _buf_dequeued = payload_ring.dequeue_many(metadata.size); + let _buf_dequeued = self.payload_ring.dequeue_many(metadata.size); Ok(()) // dequeue metadata } else { Err(()) // don't dequeue metadata @@ -188,18 +183,13 @@ impl<'a, H> PacketBuffer<'a, H> { { self.dequeue_padding(); - let Self { - ref mut metadata_ring, - ref mut payload_ring, - } = *self; - - metadata_ring.dequeue_one_with(move |metadata| { + self.metadata_ring.dequeue_one_with(|metadata| { let PacketMetadata { ref mut header, size, } = *metadata; - payload_ring + self.payload_ring .dequeue_many_with(|payload_buf| { debug_assert!(payload_buf.len() >= size); From 4a8cd44e2a031e6d9204c78ef39063d486765553 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 1 Jan 2023 22:35:17 +0100 Subject: [PATCH 482/566] wire: remove all uses of crate::{Error, Result}; --- src/wire/ieee802154.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 2c2b64899..67024bed9 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -839,7 +839,6 @@ impl Repr { #[cfg(test)] mod test { use super::*; - use crate::Result; #[test] fn test_broadcast() { From 0e1ba69ade9677e63633592a81fb136d6d40942f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 1 Jan 2023 22:35:35 +0100 Subject: [PATCH 483/566] socket: remove all uses of crate::{Error, Result}; --- src/socket/dhcpv4.rs | 3 +-- src/socket/icmp.rs | 20 +++++++++----------- src/socket/raw.rs | 17 ++++++++--------- src/socket/tcp.rs | 5 ++--- src/socket/udp.rs | 11 +++++------ 5 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 5e110602f..062c0daa2 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -701,7 +701,6 @@ mod test { use super::*; use crate::wire::EthernetAddress; - use crate::Error; // =========================================================================================// // Helper functions @@ -771,7 +770,7 @@ mod test { None => panic!("Too many reprs emitted"), } i += 1; - Ok::<_, Error>(()) + Ok::<_, ()>(()) }); } diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 060902ba3..b3abc03dd 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -619,7 +619,6 @@ mod tests_common { mod test_ipv4 { use super::tests_common::*; use crate::wire::{Icmpv4DstUnreachable, IpEndpoint, Ipv4Address}; - use crate::Error; const REMOTE_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 2]); const LOCAL_IPV4: Ipv4Address = Ipv4Address([192, 168, 1, 1]); @@ -696,9 +695,9 @@ mod test_ipv4 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); - Err(Error::Unaddressable) + Err(()) }), - Err(Error::Unaddressable) + Err(()) ); // buffer is not taken off of the tx queue due to the error assert!(!socket.can_send()); @@ -707,7 +706,7 @@ mod test_ipv4 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV4_REPR); assert_eq!(icmp_repr, ECHOV4_REPR.into()); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); @@ -743,7 +742,7 @@ mod test_ipv4 { hop_limit: 0x2a, }) ); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); @@ -861,7 +860,6 @@ mod test_ipv6 { use super::tests_common::*; use crate::wire::{Icmpv6DstUnreachable, IpEndpoint, Ipv6Address}; - use crate::Error; const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]); @@ -911,7 +909,7 @@ mod test_ipv6 { assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); // This buffer is too long @@ -944,9 +942,9 @@ mod test_ipv6 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); - Err(Error::Unaddressable) + Err(()) }), - Err(Error::Unaddressable) + Err(()) ); // buffer is not taken off of the tx queue due to the error assert!(!socket.can_send()); @@ -955,7 +953,7 @@ mod test_ipv6 { socket.dispatch(&mut cx, |_, (ip_repr, icmp_repr)| { assert_eq!(ip_repr, LOCAL_IPV6_REPR); assert_eq!(icmp_repr, ECHOV6_REPR.into()); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); @@ -996,7 +994,7 @@ mod test_ipv6 { hop_limit: 0x2a, }) ); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index f07a9596d..2da41d2ad 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -387,7 +387,6 @@ mod test { use crate::wire::{Ipv4Address, Ipv4Repr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Repr}; - use crate::Error; fn buffer(packets: usize) -> PacketBuffer<'static> { PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 48 * packets]) @@ -484,7 +483,7 @@ mod test { assert!(socket.can_send()); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); assert_eq!(socket.send_slice(&$packet[..]), Ok(())); @@ -495,9 +494,9 @@ mod test { socket.dispatch(&mut cx, |_, (ip_repr, ip_payload)| { assert_eq!(ip_repr, $hdr); assert_eq!(ip_payload, &$payload); - Err(Error::Unaddressable) + Err(()) }), - Err(Error::Unaddressable) + Err(()) ); assert!(!socket.can_send()); @@ -505,7 +504,7 @@ mod test { socket.dispatch(&mut cx, |_, (ip_repr, ip_payload)| { assert_eq!(ip_repr, $hdr); assert_eq!(ip_payload, &$payload); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); @@ -572,7 +571,7 @@ mod test { assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); let mut wrong_protocol = ipv4_locals::PACKET_BYTES; @@ -581,7 +580,7 @@ mod test { assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); } #[cfg(feature = "proto-ipv6")] @@ -595,7 +594,7 @@ mod test { assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); let mut wrong_protocol = ipv6_locals::PACKET_BYTES; @@ -604,7 +603,7 @@ mod test { assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); } } diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 0aa5f4f2b..e4fe47886 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2313,7 +2313,6 @@ impl<'a> fmt::Write for Socket<'a> { mod test { use super::*; use crate::wire::IpRepr; - use crate::Error; use core::i32; use std::ops::{Deref, DerefMut}; use std::vec::Vec; @@ -2465,7 +2464,7 @@ mod test { fn recv(socket: &mut TestSocket, timestamp: Instant, mut f: F) where - F: FnMut(Result), + F: FnMut(Result), { socket.cx.set_now(timestamp); @@ -6310,7 +6309,7 @@ mod test { assert_eq!( s.socket.dispatch(&mut s.cx, |_, (ip_repr, _)| { assert_eq!(ip_repr.hop_limit(), 0x2a); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 20e19d3f7..6d8cc55be 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -488,7 +488,6 @@ impl<'a> Socket<'a> { mod test { use super::*; use crate::wire::{IpRepr, UdpRepr}; - use crate::Error; fn buffer(packets: usize) -> PacketBuffer<'static> { PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets]) @@ -637,7 +636,7 @@ mod test { assert!(socket.can_send()); assert_eq!( socket.dispatch(&mut cx, |_, _| unreachable!()), - Ok::<_, Error>(()) + Ok::<_, ()>(()) ); assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(())); @@ -652,9 +651,9 @@ mod test { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); - Err(Error::Unaddressable) + Err(()) }), - Err(Error::Unaddressable) + Err(()) ); assert!(!socket.can_send()); @@ -663,7 +662,7 @@ mod test { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); @@ -759,7 +758,7 @@ mod test { hop_limit: 0x2a, }) ); - Ok::<_, Error>(()) + Ok::<_, ()>(()) }), Ok(()) ); From f4a823ad3713091910bc30c03aeb1ba084396868 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 1 Jan 2023 23:31:46 +0100 Subject: [PATCH 484/566] iface: move igmp code to a separate mod. --- src/iface/interface/igmp.rs | 233 ++++++++++++++++++++++++++++++++++++ src/iface/interface/ipv4.rs | 69 ----------- src/iface/interface/mod.rs | 167 +------------------------- 3 files changed, 239 insertions(+), 230 deletions(-) create mode 100644 src/iface/interface/igmp.rs diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs new file mode 100644 index 000000000..cf907f9d6 --- /dev/null +++ b/src/iface/interface/igmp.rs @@ -0,0 +1,233 @@ +use super::{check, IgmpReportState, Interface, InterfaceInner, IpPacket}; +use crate::phy::Device; +use crate::time::{Duration, Instant}; +use crate::wire::*; +use crate::{Error, Result}; + +impl<'a> Interface<'a> { + /// Add an address to a list of subscribed multicast IP addresses. + /// + /// 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>( + &mut self, + device: &mut D, + addr: T, + timestamp: Instant, + ) -> Result + where + D: Device + ?Sized, + { + self.inner.now = timestamp; + + match addr.into() { + IpAddress::Ipv4(addr) => { + let is_not_new = self + .inner + .ipv4_multicast_groups + .insert(addr, ()) + .map_err(|_| Error::Exhausted)? + .is_some(); + if is_not_new { + Ok(false) + } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) + { + // Send initial membership report + let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + Ok(true) + } else { + Ok(false) + } + } + // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] + _ => Err(Error::Unaddressable), + } + } + + /// Remove an address from the subscribed multicast IP addresses. + /// + /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` + /// indicates whether an immediate leave packet has been sent. + pub fn leave_multicast_group>( + &mut self, + device: &mut D, + addr: T, + timestamp: Instant, + ) -> Result + where + D: Device + ?Sized, + { + self.inner.now = timestamp; + + match addr.into() { + IpAddress::Ipv4(addr) => { + let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none(); + if was_not_present { + Ok(false) + } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { + // Send group leave packet + let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + Ok(true) + } else { + Ok(false) + } + } + // Multicast is not yet implemented for other address families + #[allow(unreachable_patterns)] + _ => Err(Error::Unaddressable), + } + } + + /// Check whether the interface listens to given destination multicast IP address. + pub fn has_multicast_group>(&self, addr: T) -> bool { + self.inner.has_multicast_group(addr) + } + + /// Depending on `igmp_report_state` and the therein contained + /// timeouts, send IGMP membership reports. + pub(crate) fn igmp_egress(&mut self, device: &mut D) -> Result + where + D: Device + ?Sized, + { + match self.inner.igmp_report_state { + IgmpReportState::ToSpecificQuery { + version, + timeout, + group, + } if self.inner.now >= timeout => { + if let Some(pkt) = self.inner.igmp_report_packet(version, group) { + // Send initial membership report + let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + } + + self.inner.igmp_report_state = IgmpReportState::Inactive; + Ok(true) + } + IgmpReportState::ToGeneralQuery { + version, + timeout, + interval, + next_index, + } if self.inner.now >= timeout => { + let addr = self + .inner + .ipv4_multicast_groups + .iter() + .nth(next_index) + .map(|(addr, ())| *addr); + + match addr { + Some(addr) => { + if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { + // Send initial membership report + let tx_token = + device.transmit(self.inner.now).ok_or(Error::Exhausted)?; + self.inner.dispatch_ip(tx_token, pkt, None)?; + } + + let next_timeout = (timeout + interval).max(self.inner.now); + self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { + version, + timeout: next_timeout, + interval, + next_index: next_index + 1, + }; + Ok(true) + } + + None => { + self.inner.igmp_report_state = IgmpReportState::Inactive; + Ok(false) + } + } + } + _ => Ok(false), + } + } +} + +impl<'a> InterfaceInner<'a> { + /// Check whether the interface listens to given destination multicast IP address. + /// + /// If built without feature `proto-igmp` this function will + /// always return `false`. + pub fn has_multicast_group>(&self, addr: T) -> bool { + match addr.into() { + IpAddress::Ipv4(key) => { + key == Ipv4Address::MULTICAST_ALL_SYSTEMS + || self.ipv4_multicast_groups.get(&key).is_some() + } + #[allow(unreachable_patterns)] + _ => false, + } + } + + /// Host duties of the **IGMPv2** protocol. + /// + /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. + /// 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. + pub(super) fn process_igmp<'frame>( + &mut self, + ipv4_repr: Ipv4Repr, + ip_payload: &'frame [u8], + ) -> Option> { + let igmp_packet = check!(IgmpPacket::new_checked(ip_payload)); + let igmp_repr = check!(IgmpRepr::parse(&igmp_packet)); + + // FIXME: report membership after a delay + match igmp_repr { + IgmpRepr::MembershipQuery { + group_addr, + version, + max_resp_time, + } => { + // General query + if group_addr.is_unspecified() + && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS + { + // Are we member in any groups? + if self.ipv4_multicast_groups.iter().next().is_some() { + let interval = match version { + IgmpVersion::Version1 => Duration::from_millis(100), + IgmpVersion::Version2 => { + // No dependence on a random generator + // (see [#24](https://github.com/m-labs/smoltcp/issues/24)) + // but at least spread reports evenly across max_resp_time. + let intervals = self.ipv4_multicast_groups.len() as u32 + 1; + max_resp_time / intervals + } + }; + self.igmp_report_state = IgmpReportState::ToGeneralQuery { + version, + timeout: self.now + interval, + interval, + next_index: 0, + }; + } + } else { + // Group-specific query + if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr { + // Don't respond immediately + let timeout = max_resp_time / 4; + self.igmp_report_state = IgmpReportState::ToSpecificQuery { + version, + timeout: self.now + timeout, + group: group_addr, + }; + } + } + } + // Ignore membership reports + IgmpRepr::MembershipReport { .. } => (), + // Ignore hosts leaving groups + IgmpRepr::LeaveGroup { .. } => (), + } + + None + } +} diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 0e69df736..091947d67 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -5,9 +5,6 @@ use super::IpPacket; use super::PacketAssemblerSet; use super::SocketSet; -#[cfg(feature = "proto-igmp")] -use super::IgmpReportState; - #[cfg(feature = "medium-ethernet")] use super::EthernetPacket; @@ -249,72 +246,6 @@ impl<'a> InterfaceInner<'a> { } } - /// Host duties of the **IGMPv2** protocol. - /// - /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. - /// 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")] - pub(super) fn process_igmp<'frame>( - &mut self, - ipv4_repr: Ipv4Repr, - ip_payload: &'frame [u8], - ) -> Option> { - let igmp_packet = check!(IgmpPacket::new_checked(ip_payload)); - let igmp_repr = check!(IgmpRepr::parse(&igmp_packet)); - - // FIXME: report membership after a delay - match igmp_repr { - IgmpRepr::MembershipQuery { - group_addr, - version, - max_resp_time, - } => { - // General query - if group_addr.is_unspecified() - && ipv4_repr.dst_addr == Ipv4Address::MULTICAST_ALL_SYSTEMS - { - // Are we member in any groups? - if self.ipv4_multicast_groups.iter().next().is_some() { - let interval = match version { - IgmpVersion::Version1 => Duration::from_millis(100), - IgmpVersion::Version2 => { - // No dependence on a random generator - // (see [#24](https://github.com/m-labs/smoltcp/issues/24)) - // but at least spread reports evenly across max_resp_time. - let intervals = self.ipv4_multicast_groups.len() as u32 + 1; - max_resp_time / intervals - } - }; - self.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, - timeout: self.now + interval, - interval, - next_index: 0, - }; - } - } else { - // Group-specific query - if self.has_multicast_group(group_addr) && ipv4_repr.dst_addr == group_addr { - // Don't respond immediately - let timeout = max_resp_time / 4; - self.igmp_report_state = IgmpReportState::ToSpecificQuery { - version, - timeout: self.now + timeout, - group: group_addr, - }; - } - } - } - // Ignore membership reports - IgmpRepr::MembershipReport { .. } => (), - // Ignore hosts leaving groups - IgmpRepr::LeaveGroup { .. } => (), - } - - None - } - pub(super) fn process_icmpv4<'frame>( &mut self, _sockets: &mut SocketSet, diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index e9588ec2d..6d0163b24 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -15,6 +15,9 @@ mod ipv4; #[cfg(feature = "proto-ipv6")] mod ipv6; +#[cfg(feature = "proto-igmp")] +mod igmp; + use core::cmp; use core::marker::PhantomData; use heapless::{LinearMap, Vec}; @@ -898,89 +901,6 @@ impl<'a> Interface<'a> { self.inner.hardware_addr = Some(addr); } - /// Add an address to a list of subscribed multicast IP addresses. - /// - /// 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>( - &mut self, - device: &mut D, - addr: T, - timestamp: Instant, - ) -> Result - where - D: Device + ?Sized, - { - self.inner.now = timestamp; - - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(addr) => { - let is_not_new = self - .inner - .ipv4_multicast_groups - .insert(addr, ()) - .map_err(|_| Error::Exhausted)? - .is_some(); - if is_not_new { - Ok(false) - } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) - { - // Send initial membership report - let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - Ok(true) - } else { - Ok(false) - } - } - // Multicast is not yet implemented for other address families - #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), - } - } - - /// Remove an address from the subscribed multicast IP addresses. - /// - /// Returns `Ok(leave_sent)` if the address was removed successfully, where `leave_sent` - /// indicates whether an immediate leave packet has been sent. - pub fn leave_multicast_group>( - &mut self, - device: &mut D, - addr: T, - timestamp: Instant, - ) -> Result - where - D: Device + ?Sized, - { - self.inner.now = timestamp; - - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(addr) => { - let was_not_present = self.inner.ipv4_multicast_groups.remove(&addr).is_none(); - if was_not_present { - Ok(false) - } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { - // Send group leave packet - let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - Ok(true) - } else { - Ok(false) - } - } - // Multicast is not yet implemented for other address families - #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), - } - } - - /// Check whether the interface listens to given destination multicast IP address. - pub fn has_multicast_group>(&self, addr: T) -> bool { - self.inner.has_multicast_group(addr) - } - /// Get the IP addresses of the interface. pub fn ip_addrs(&self) -> &[IpCidr] { self.inner.ip_addrs.as_ref() @@ -1315,70 +1235,6 @@ impl<'a> Interface<'a> { emitted_any } - /// Depending on `igmp_report_state` and the therein contained - /// timeouts, send IGMP membership reports. - #[cfg(feature = "proto-igmp")] - fn igmp_egress(&mut self, device: &mut D) -> Result - where - D: Device + ?Sized, - { - match self.inner.igmp_report_state { - IgmpReportState::ToSpecificQuery { - version, - timeout, - group, - } if self.inner.now >= timeout => { - if let Some(pkt) = self.inner.igmp_report_packet(version, group) { - // Send initial membership report - let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - } - - self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(true) - } - IgmpReportState::ToGeneralQuery { - version, - timeout, - interval, - next_index, - } if self.inner.now >= timeout => { - let addr = self - .inner - .ipv4_multicast_groups - .iter() - .nth(next_index) - .map(|(addr, ())| *addr); - - match addr { - Some(addr) => { - if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { - // Send initial membership report - let tx_token = - device.transmit(self.inner.now).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; - } - - let next_timeout = (timeout + interval).max(self.inner.now); - self.inner.igmp_report_state = IgmpReportState::ToGeneralQuery { - version, - timeout: next_timeout, - interval, - next_index: next_index + 1, - }; - Ok(true) - } - - None => { - self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(false) - } - } - } - _ => Ok(false), - } - } - /// Process fragments that still need to be sent for IPv4 packets. /// /// This function returns a boolean value indicating whether any packets were @@ -1683,20 +1539,9 @@ impl<'a> InterfaceInner<'a> { }) } - /// Check whether the interface listens to given destination multicast IP address. - /// - /// If built without feature `proto-igmp` this function will - /// always return `false`. - pub fn has_multicast_group>(&self, addr: T) -> bool { - match addr.into() { - #[cfg(feature = "proto-igmp")] - IpAddress::Ipv4(key) => { - key == Ipv4Address::MULTICAST_ALL_SYSTEMS - || self.ipv4_multicast_groups.get(&key).is_some() - } - #[allow(unreachable_patterns)] - _ => false, - } + #[cfg(not(feature = "proto-igmp"))] + fn has_multicast_group>(&self, addr: T) -> bool { + false } #[cfg(feature = "medium-ip")] From 050731b51984ae55293b08e1f9720f16cd1dcaf4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Jan 2023 00:14:21 +0100 Subject: [PATCH 485/566] iface: remove all uses of crate::{Error, Result}; --- examples/benchmark.rs | 8 +- examples/client.rs | 7 +- examples/dhcp_client.rs | 4 +- examples/dns.rs | 7 +- examples/httpclient.rs | 7 +- examples/loopback.rs | 7 +- examples/multicast.rs | 8 +- examples/ping.rs | 8 +- examples/server.rs | 7 +- examples/sixlowpan.rs | 12 +- examples/sixlowpan_benchmark.rs | 8 +- src/iface/interface/ethernet.rs | 13 +- src/iface/interface/igmp.rs | 70 ++++++++--- src/iface/interface/ipv4.rs | 18 +-- src/iface/interface/mod.rs | 206 ++++++++++++++----------------- src/iface/interface/sixlowpan.rs | 53 ++++---- src/iface/interface/tests.rs | 58 ++++----- src/wire/mod.rs | 18 +++ 18 files changed, 232 insertions(+), 287 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index ad3e6dfcd..ba6977c19 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -2,7 +2,6 @@ mod utils; -use log::debug; use std::cmp; use std::io::{Read, Write}; use std::net::TcpStream; @@ -114,12 +113,7 @@ fn main() { let mut processed = 0; while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); // tcp:1234: emit data let socket = sockets.get_mut::(tcp1_handle); diff --git a/examples/client.rs b/examples/client.rs index 4ef8d48bb..83da040a4 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -76,12 +76,7 @@ fn main() { let mut tcp_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); let socket = sockets.get_mut::(tcp_handle); if socket.is_active() && !tcp_active { diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 132dc0266..0437fd232 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -56,9 +56,7 @@ fn main() { loop { let timestamp = Instant::now(); - if let Err(e) = iface.poll(timestamp, &mut device, &mut sockets) { - debug!("poll error: {}", e); - } + iface.poll(timestamp, &mut device, &mut sockets); let event = sockets.get_mut::(dhcp_handle).poll(); match event { diff --git a/examples/dns.rs b/examples/dns.rs index 40aa785a2..53f971114 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -77,12 +77,7 @@ fn main() { let timestamp = Instant::now(); debug!("timestamp {:?}", timestamp); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); match sockets .get_mut::(dns_handle) diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 6c612eaea..a54ca8228 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -72,12 +72,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); let socket = sockets.get_mut::(tcp_handle); let cx = iface.context(); diff --git a/examples/loopback.rs b/examples/loopback.rs index 206730e1e..bc4a24bcf 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -121,12 +121,7 @@ fn main() { let mut did_connect = false; let mut done = false; while !done && clock.elapsed() < Instant::from_millis(10_000) { - match iface.poll(clock.elapsed(), &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(clock.elapsed(), &mut device, &mut sockets); let mut socket = sockets.get_mut::(server_handle); if !socket.is_active() && !socket.is_listening() { diff --git a/examples/multicast.rs b/examples/multicast.rs index 11c90c48a..886b92e6e 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -1,6 +1,5 @@ mod utils; -use log::debug; use std::os::unix::io::AsRawFd; use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; @@ -70,12 +69,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); let socket = sockets.get_mut::(raw_handle); diff --git a/examples/ping.rs b/examples/ping.rs index 76626d656..ce709713d 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -1,7 +1,6 @@ mod utils; use byteorder::{ByteOrder, NetworkEndian}; -use log::debug; use smoltcp::iface::SocketSet; use std::cmp; use std::collections::HashMap; @@ -150,12 +149,7 @@ fn main() { loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); let timestamp = Instant::now(); let socket = sockets.get_mut::(icmp_handle); diff --git a/examples/server.rs b/examples/server.rs index 6608f8b5b..bb0dc133e 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -100,12 +100,7 @@ fn main() { let mut tcp_6970_active = false; loop { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); // udp:6969: respond "hello" let socket = sockets.get_mut::(udp_handle); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 23cec9f7c..0013f0dba 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -115,17 +115,7 @@ fn main() { loop { let timestamp = Instant::now(); - - let mut poll = true; - while poll { - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(r) => poll = r, - Err(e) => { - debug!("poll error: {}", e); - break; - } - } - } + iface.poll(timestamp, &mut device, &mut sockets); // udp:6969: respond "hello" let socket = sockets.get_mut::(udp_handle); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 6ec01f306..11b2a62d7 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -43,7 +43,6 @@ mod utils; -use log::debug; use std::os::unix::io::AsRawFd; use std::str; @@ -188,12 +187,7 @@ fn main() { while !CLIENT_DONE.load(Ordering::SeqCst) { let timestamp = Instant::now(); - match iface.poll(timestamp, &mut device, &mut sockets) { - Ok(_) => {} - Err(e) => { - debug!("poll error: {}", e); - } - } + iface.poll(timestamp, &mut device, &mut sockets); // tcp:1234: emit data let socket = sockets.get_mut::(tcp1_handle); diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index 1e67522df..7ea279356 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -1,13 +1,13 @@ use super::check; +use super::DispatchError; use super::EthernetPacket; use super::FragmentsBuffer; use super::InterfaceInner; use super::SocketSet; +use core::result::Result; use crate::phy::TxToken; use crate::wire::*; -use crate::Error; -use crate::Result; impl<'i> InterfaceInner<'i> { #[cfg(feature = "medium-ethernet")] @@ -63,7 +63,7 @@ impl<'i> InterfaceInner<'i> { tx_token: Tx, buffer_len: usize, f: F, - ) -> Result<()> + ) -> Result<(), DispatchError> where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>), @@ -73,12 +73,7 @@ impl<'i> InterfaceInner<'i> { debug_assert!(tx_buffer.as_ref().len() == tx_len); let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - + let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); frame.set_src_addr(src_addr); f(frame); diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index cf907f9d6..8e7c3a15c 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -2,7 +2,20 @@ use super::{check, IgmpReportState, Interface, InterfaceInner, IpPacket}; use crate::phy::Device; use crate::time::{Duration, Instant}; use crate::wire::*; -use crate::{Error, Result}; + +use core::result::Result; + +/// Error type for `join_multicast_group`, `leave_multicast_group`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MulticastError { + /// The hardware device transmit buffer is full. Try again later. + Exhausted, + /// The table of joined multicast groups is already full. + GroupTableFull, + /// IPv6 multicast is not yet supported. + Ipv6NotSupported, +} impl<'a> Interface<'a> { /// Add an address to a list of subscribed multicast IP addresses. @@ -14,7 +27,7 @@ impl<'a> Interface<'a> { device: &mut D, addr: T, timestamp: Instant, - ) -> Result + ) -> Result where D: Device + ?Sized, { @@ -26,15 +39,20 @@ impl<'a> Interface<'a> { .inner .ipv4_multicast_groups .insert(addr, ()) - .map_err(|_| Error::Exhausted)? + .map_err(|_| MulticastError::GroupTableFull)? .is_some(); if is_not_new { Ok(false) } else if let Some(pkt) = self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report - let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; + let tx_token = device + .transmit(timestamp) + .ok_or(MulticastError::Exhausted)?; + + // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. + self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + Ok(true) } else { Ok(false) @@ -42,7 +60,7 @@ impl<'a> Interface<'a> { } // Multicast is not yet implemented for other address families #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), + _ => Err(MulticastError::Ipv6NotSupported), } } @@ -55,7 +73,7 @@ impl<'a> Interface<'a> { device: &mut D, addr: T, timestamp: Instant, - ) -> Result + ) -> Result where D: Device + ?Sized, { @@ -68,8 +86,13 @@ impl<'a> Interface<'a> { Ok(false) } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet - let tx_token = device.transmit(timestamp).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; + let tx_token = device + .transmit(timestamp) + .ok_or(MulticastError::Exhausted)?; + + // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. + self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + Ok(true) } else { Ok(false) @@ -77,7 +100,7 @@ impl<'a> Interface<'a> { } // Multicast is not yet implemented for other address families #[allow(unreachable_patterns)] - _ => Err(Error::Unaddressable), + _ => Err(MulticastError::Ipv6NotSupported), } } @@ -88,7 +111,7 @@ impl<'a> Interface<'a> { /// Depending on `igmp_report_state` and the therein contained /// timeouts, send IGMP membership reports. - pub(crate) fn igmp_egress(&mut self, device: &mut D) -> Result + pub(crate) fn igmp_egress(&mut self, device: &mut D) -> bool where D: Device + ?Sized, { @@ -100,12 +123,16 @@ impl<'a> Interface<'a> { } if self.inner.now >= timeout => { if let Some(pkt) = self.inner.igmp_report_packet(version, group) { // Send initial membership report - let tx_token = device.transmit(self.inner.now).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; + if let Some(tx_token) = device.transmit(self.inner.now) { + // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. + self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + } else { + return false; + } } self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(true) + true } IgmpReportState::ToGeneralQuery { version, @@ -124,9 +151,12 @@ impl<'a> Interface<'a> { Some(addr) => { if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { // Send initial membership report - let tx_token = - device.transmit(self.inner.now).ok_or(Error::Exhausted)?; - self.inner.dispatch_ip(tx_token, pkt, None)?; + if let Some(tx_token) = device.transmit(self.inner.now) { + // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. + self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + } else { + return false; + } } let next_timeout = (timeout + interval).max(self.inner.now); @@ -136,16 +166,16 @@ impl<'a> Interface<'a> { interval, next_index: next_index + 1, }; - Ok(true) + true } None => { self.inner.igmp_report_state = IgmpReportState::Inactive; - Ok(false) + false } } } - _ => Ok(false), + _ => false, } } } diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 091947d67..653c13454 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -18,7 +18,8 @@ use crate::socket::icmp; use crate::socket::AnySocket; use crate::phy::{Medium, TxToken}; -use crate::{time::*, wire::*, Error, Result}; +use crate::time::{Duration, Instant}; +use crate::wire::*; impl<'a> InterfaceInner<'a> { pub(super) fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( @@ -348,7 +349,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, out_packet: &mut Ipv4OutPacket, - ) -> Result<()> { + ) { let Ipv4OutPacket { buffer, packet_len, @@ -381,12 +382,7 @@ impl<'a> InterfaceInner<'a> { let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - + let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); frame.set_src_addr(src_addr); frame.set_dst_addr(*dst_hardware_addr); @@ -396,14 +392,12 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-ipv6")] IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), } - - Ok(()) }; tx_token.consume(tx_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; + emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer); tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; } @@ -424,8 +418,6 @@ impl<'a> InterfaceInner<'a> { // Update the frag offset for the next fragment. *frag_offset += payload_len as u16; - - Ok(()) }) } diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 6d0163b24..f2f83a161 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -20,6 +20,7 @@ mod igmp; use core::cmp; use core::marker::PhantomData; +use core::result::Result; use heapless::{LinearMap, Vec}; use managed::ManagedSlice; @@ -36,7 +37,6 @@ use crate::socket::dns; use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; -use crate::{Error, Result}; const MAX_IP_ADDR_COUNT: usize = 5; #[cfg(feature = "proto-igmp")] @@ -953,23 +953,12 @@ impl<'a> Interface<'a> { /// This function returns a boolean value indicating whether any packets were /// processed or emitted, and thus, whether the readiness of any socket might /// have changed. - /// - /// # Errors - /// This method will routinely return errors in response to normal network - /// activity as well as certain boundary conditions such as buffer exhaustion. - /// These errors are provided as an aid for troubleshooting, and are meant - /// to be logged and ignored. - /// - /// As a special case, `Err(Error::Unrecognized)` is returned in response to - /// packets containing any unsupported protocol, option, or form, which is - /// a very common occurrence and on a production system it should not even - /// be logged. pub fn poll( &mut self, timestamp: Instant, device: &mut D, sockets: &mut SocketSet<'_>, - ) -> Result + ) -> bool where D: Device + ?Sized, { @@ -982,42 +971,35 @@ impl<'a> Interface<'a> { self.fragments.sixlowpan_fragments.remove_expired(timestamp); #[cfg(feature = "proto-ipv4-fragmentation")] - match self.ipv4_egress(device) { - Ok(true) => return Ok(true), - Err(e) => { - net_debug!("failed to transmit: {}", e); - return Err(e); - } - _ => (), + if self.ipv4_egress(device) { + return true; } #[cfg(feature = "proto-sixlowpan-fragmentation")] - match self.sixlowpan_egress(device) { - Ok(true) => return Ok(true), - Err(e) => { - net_debug!("failed to transmit: {}", e); - return Err(e); - } - _ => (), + if self.sixlowpan_egress(device) { + return true; } let mut readiness_may_have_changed = false; loop { - let processed_any = self.socket_ingress(device, sockets); - let emitted_any = self.socket_egress(device, sockets); + let mut did_something = false; + did_something |= self.socket_ingress(device, sockets); + did_something |= self.socket_egress(device, sockets); #[cfg(feature = "proto-igmp")] - self.igmp_egress(device)?; + { + did_something |= self.igmp_egress(device); + } - if processed_any || emitted_any { + if did_something { readiness_may_have_changed = true; } else { break; } } - Ok(readiness_may_have_changed) + readiness_may_have_changed } /// Return a _soft deadline_ for calling [poll] the next time. @@ -1089,7 +1071,7 @@ impl<'a> Interface<'a> { self.inner .dispatch(tx_token, packet, Some(&mut self.out_packets)) { - net_debug!("Failed to send response: {}", err); + net_debug!("Failed to send response: {:?}", err); } } } @@ -1103,7 +1085,7 @@ impl<'a> Interface<'a> { packet, Some(&mut self.out_packets), ) { - net_debug!("Failed to send response: {}", err); + net_debug!("Failed to send response: {:?}", err); } } } @@ -1118,7 +1100,7 @@ impl<'a> Interface<'a> { packet, Some(&mut self.out_packets), ) { - net_debug!("Failed to send response: {}", err); + net_debug!("Failed to send response: {:?}", err); } } } @@ -1136,6 +1118,11 @@ impl<'a> Interface<'a> { { let _caps = device.capabilities(); + enum EgressError { + Exhausted, + Dispatch(DispatchError), + } + let mut emitted_any = false; for item in sockets.items_mut() { if !item @@ -1149,21 +1136,25 @@ impl<'a> Interface<'a> { let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); let t = device.transmit(inner.now).ok_or_else(|| { - net_debug!("failed to transmit IP: {}", Error::Exhausted); - Error::Exhausted + net_debug!("failed to transmit IP: device exhausted"); + EgressError::Exhausted })?; #[cfg(any( feature = "proto-ipv4-fragmentation", feature = "proto-sixlowpan-fragmentation" ))] - inner.dispatch_ip(t, response, Some(&mut self.out_packets))?; + inner + .dispatch_ip(t, response, Some(&mut self.out_packets)) + .map_err(EgressError::Dispatch)?; #[cfg(not(any( feature = "proto-ipv4-fragmentation", feature = "proto-sixlowpan-fragmentation" )))] - inner.dispatch_ip(t, response, None)?; + inner + .dispatch_ip(t, response, None) + .map_err(EgressError::Dispatch)?; emitted_any = true; @@ -1210,8 +1201,8 @@ impl<'a> Interface<'a> { }; match result { - Err(Error::Exhausted) => break, // Device buffer full. - Err(Error::Unaddressable) => { + Err(EgressError::Exhausted) => break, // Device buffer full. + Err(EgressError::Dispatch(_)) => { // `NeighborCache` already takes care of rate limiting the neighbor discovery // requests from the socket. However, without an additional rate limiting // mechanism, we would spin on every socket that has yet to discover its @@ -1220,14 +1211,6 @@ impl<'a> Interface<'a> { self.inner.now, neighbor_addr.expect("non-IP response packet"), ); - break; - } - Err(err) => { - net_debug!( - "{}: cannot dispatch egress packet: {}", - item.meta.handle, - err - ); } Ok(()) => {} } @@ -1241,7 +1224,7 @@ impl<'a> Interface<'a> { /// processed or emitted, and thus, whether the readiness of any socket might /// have changed. #[cfg(feature = "proto-ipv4-fragmentation")] - fn ipv4_egress(&mut self, device: &mut D) -> Result + fn ipv4_egress(&mut self, device: &mut D) -> bool where D: Device + ?Sized, { @@ -1251,7 +1234,7 @@ impl<'a> Interface<'a> { } if self.out_packets.ipv4_out_packet.is_empty() { - return Ok(false); + return false; } let Ipv4OutPacket { @@ -1261,16 +1244,13 @@ impl<'a> Interface<'a> { } = &self.out_packets.ipv4_out_packet; if *packet_len > *sent_bytes { - match device.transmit(self.inner.now) { - Some(tx_token) => self - .inner - .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet), - None => Err(Error::Exhausted), + if let Some(tx_token) = device.transmit(self.inner.now) { + self.inner + .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet); + return true; } - .map(|_| true) - } else { - Ok(false) } + false } /// Process fragments that still need to be sent for 6LoWPAN packets. @@ -1279,7 +1259,7 @@ impl<'a> Interface<'a> { /// processed or emitted, and thus, whether the readiness of any socket might /// have changed. #[cfg(feature = "proto-sixlowpan-fragmentation")] - fn sixlowpan_egress(&mut self, device: &mut D) -> Result + fn sixlowpan_egress(&mut self, device: &mut D) -> bool where D: Device + ?Sized, { @@ -1289,7 +1269,7 @@ impl<'a> Interface<'a> { } if self.out_packets.sixlowpan_out_packet.is_empty() { - return Ok(false); + return false; } let SixlowpanOutPacket { @@ -1299,17 +1279,15 @@ impl<'a> Interface<'a> { } = &self.out_packets.sixlowpan_out_packet; if *packet_len > *sent_bytes { - match device.transmit(self.inner.now) { - Some(tx_token) => self.inner.dispatch_ieee802154_out_packet( + if let Some(tx_token) = device.transmit(self.inner.now) { + self.inner.dispatch_ieee802154_out_packet( tx_token, &mut self.out_packets.sixlowpan_out_packet, - ), - None => Err(Error::Exhausted), + ); + return true; } - .map(|_| true) - } else { - Ok(false) } + false } } @@ -1729,7 +1707,7 @@ impl<'a> InterfaceInner<'a> { tx_token: Tx, packet: EthernetPacket, _out_packet: Option<&mut OutPackets<'_>>, - ) -> Result<()> + ) -> Result<(), DispatchError> where Tx: TxToken, { @@ -1759,22 +1737,19 @@ impl<'a> InterfaceInner<'a> { self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr)) } - fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { + fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option { // Send directly. if self.in_same_network(addr) || addr.is_broadcast() { - return Ok(*addr); + return Some(*addr); } // Route via a router. - match self.routes.lookup(addr, timestamp) { - Some(router_addr) => Ok(router_addr), - None => Err(Error::Unaddressable), - } + self.routes.lookup(addr, timestamp) } fn has_neighbor(&self, addr: &IpAddress) -> bool { match self.route(addr, self.now) { - Ok(_routed_addr) => match self.caps.medium { + Some(_routed_addr) => match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => self .neighbor_cache @@ -1792,7 +1767,7 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ip")] Medium::Ip => true, }, - Err(_) => false, + None => false, } } @@ -1802,7 +1777,7 @@ impl<'a> InterfaceInner<'a> { tx_token: Tx, src_addr: &IpAddress, dst_addr: &IpAddress, - ) -> Result<(HardwareAddress, Tx)> + ) -> Result<(HardwareAddress, Tx), DispatchError> where Tx: TxToken, { @@ -1852,7 +1827,9 @@ impl<'a> InterfaceInner<'a> { return Ok((hardware_addr, tx_token)); } - let dst_addr = self.route(dst_addr, self.now)?; + let dst_addr = self + .route(dst_addr, self.now) + .ok_or(DispatchError::NoRoute)?; match self .neighbor_cache @@ -1861,7 +1838,7 @@ impl<'a> InterfaceInner<'a> { .lookup(&dst_addr, self.now) { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), - NeighborAnswer::RateLimited => return Err(Error::Unaddressable), + NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending), _ => (), // XXX } @@ -1872,12 +1849,7 @@ impl<'a> InterfaceInner<'a> { "address {} not in neighbor cache, sending ARP request", dst_addr ); - let src_hardware_addr = - if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; + let src_hardware_addr = self.hardware_addr.unwrap().ethernet_or_panic(); let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, @@ -1887,12 +1859,17 @@ impl<'a> InterfaceInner<'a> { target_protocol_addr: dst_addr, }; - self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_ethertype(EthernetProtocol::Arp); + if let Err(e) = + self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| { + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_ethertype(EthernetProtocol::Arp); - arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) - })?; + arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut())) + }) + { + net_debug!("Failed to dispatch ARP request: {:?}", e); + return Err(DispatchError::NeighborPending); + } } #[cfg(feature = "proto-ipv6")] @@ -1918,15 +1895,19 @@ impl<'a> InterfaceInner<'a> { solicit, )); - self.dispatch_ip(tx_token, packet, None)?; + if let Err(e) = self.dispatch_ip(tx_token, packet, None) { + net_debug!("Failed to dispatch NDISC solicit: {:?}", e); + return Err(DispatchError::NeighborPending); + } } #[allow(unreachable_patterns)] _ => (), } + // The request got dispatched, limit the rate on the cache. self.neighbor_cache.as_mut().unwrap().limit_rate(self.now); - Err(Error::Unaddressable) + Err(DispatchError::NeighborPending) } fn flush_cache(&mut self) { @@ -1941,7 +1922,7 @@ impl<'a> InterfaceInner<'a> { tx_token: Tx, packet: IpPacket, _out_packet: Option<&mut OutPackets<'_>>, - ) -> Result<()> { + ) -> Result<(), DispatchError> { let mut ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); @@ -1949,16 +1930,12 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "medium-ieee802154")] if matches!(self.caps.medium, Medium::Ieee802154) { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), - _ => unreachable!(), - }; + let (addr, tx_token) = + self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())?; + let addr = addr.ieee802154_or_panic(); - return self.dispatch_ieee802154(dst_hardware_addr, tx_token, packet, _out_packet); + self.dispatch_ieee802154(addr, tx_token, packet, _out_packet); + return Ok(()); } // Dispatch IP/Ethernet: @@ -1999,12 +1976,7 @@ impl<'a> InterfaceInner<'a> { let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { - addr - } else { - return Err(Error::Malformed); - }; - + let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); frame.set_src_addr(src_addr); frame.set_dst_addr(dst_hardware_addr); @@ -2056,10 +2028,10 @@ impl<'a> InterfaceInner<'a> { if buffer.len() < first_frag_ip_len { net_debug!( - "Fragmentation buffer is too small, at least {} needed", + "Fragmentation buffer is too small, at least {} needed. Dropping", first_frag_ip_len ); - return Err(Error::Exhausted); + return Ok(()); } #[cfg(feature = "medium-ethernet")] @@ -2146,3 +2118,15 @@ impl<'a> InterfaceInner<'a> { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum DispatchError { + /// No route to dispatch this packet. Retrying won't help unless + /// configuration is changed. + NoRoute, + /// We do have a route to dispatch this packet, but we haven't discovered + /// the neighbor for it yet. Discovery has been initiated, dispatch + /// should be retried later. + NeighborPending, +} diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 8c6f8a354..ec429f11b 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -11,8 +11,6 @@ use super::SixlowpanOutPacket; use crate::phy::ChecksumCapabilities; use crate::phy::TxToken; use crate::wire::*; -use crate::Error; -use crate::Result; // Max len of non-fragmented packets after decompression (including ipv6 header and payload) // TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size) @@ -277,24 +275,21 @@ impl<'a> InterfaceInner<'a> { tx_token: Tx, packet: IpPacket, _out_packet: Option<&mut OutPackets>, - ) -> Result<()> { + ) { // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to // fragment it. - let ll_src_a = self.hardware_addr.map_or_else( - || Err(Error::Malformed), - |addr| match addr { - HardwareAddress::Ieee802154(addr) => Ok(addr), - _ => Err(Error::Malformed), - }, - )?; + let ll_src_a = self.hardware_addr.unwrap().ieee802154_or_panic(); let ip_repr = packet.ip_repr(); let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), #[allow(unreachable_patterns)] - _ => return Err(Error::Unaddressable), + _ => { + net_debug!("dispatch_ieee802154: dropping because src or dst addrs are not ipv6."); + return; + } }; // Create the IEEE802.15.4 header. @@ -325,7 +320,10 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-udp")] IpPacket::Udp(_) => SixlowpanNextHeader::Compressed, #[allow(unreachable_patterns)] - _ => return Err(Error::Unrecognized), + _ => { + net_debug!("dispatch_ieee802154: dropping, unhandled protocol."); + return; + } }, hop_limit: ip_repr.hop_limit(), ecn: None, @@ -340,7 +338,6 @@ impl<'a> InterfaceInner<'a> { let mut _compressed_headers_len = iphc_repr.buffer_len(); let mut _uncompressed_headers_len = ip_repr.header_len(); - #[allow(unreachable_patterns)] match packet { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udpv6_repr, payload)) => { @@ -357,7 +354,8 @@ impl<'a> InterfaceInner<'a> { IpPacket::Icmpv6((_, icmp_repr)) => { total_size += icmp_repr.buffer_len(); } - _ => return Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + _ => unreachable!(), } let ieee_len = ieee_repr.buffer_len(); @@ -390,10 +388,10 @@ impl<'a> InterfaceInner<'a> { managed::ManagedSlice::Borrowed(buffer) => { if buffer.len() < total_size { net_debug!( - "6LoWPAN: Fragmentation buffer is too small, at least {} needed", + "dispatch_ieee802154: dropping, fragmentation buffer is too small, at least {} needed", total_size ); - return Err(Error::Exhausted); + return; } } #[cfg(feature = "alloc")] @@ -409,7 +407,6 @@ impl<'a> InterfaceInner<'a> { let b = &mut buffer[iphc_repr.buffer_len()..]; - #[allow(unreachable_patterns)] match packet { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udpv6_repr, payload)) => { @@ -447,7 +444,8 @@ impl<'a> InterfaceInner<'a> { &self.caps.checksum, ); } - _ => return Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + _ => unreachable!(), } *packet_len = total_size; @@ -501,9 +499,7 @@ impl<'a> InterfaceInner<'a> { // Add the buffer part. tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); - - Ok(()) - }) + }); } #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] @@ -511,7 +507,7 @@ impl<'a> InterfaceInner<'a> { net_debug!( "Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support." ); - Ok(()) + return; } } else { // We don't need fragmentation, so we emit everything to the TX token. @@ -525,7 +521,6 @@ impl<'a> InterfaceInner<'a> { iphc_repr.emit(&mut iphc_packet); tx_buf = &mut tx_buf[iphc_repr.buffer_len()..]; - #[allow(unreachable_patterns)] match packet { #[cfg(feature = "socket-udp")] IpPacket::Udp((_, udpv6_repr, payload)) => { @@ -563,10 +558,10 @@ impl<'a> InterfaceInner<'a> { &self.caps.checksum, ); } - _ => return Err(Error::Unrecognized), + #[allow(unreachable_patterns)] + _ => unreachable!(), } - Ok(()) - }) + }); } } @@ -578,7 +573,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, out_packet: &mut SixlowpanOutPacket, - ) -> Result<()> { + ) { let SixlowpanOutPacket { buffer, packet_len, @@ -634,9 +629,7 @@ impl<'a> InterfaceInner<'a> { *sent_bytes += frag_size; *datagram_offset += frag_size; - - Ok(()) }, - ) + ); } } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 32dde5888..d40787f15 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -898,7 +898,7 @@ fn test_handle_other_arp_request() { &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), &IpAddress::Ipv4(remote_ip_addr) ), - Err(Error::Unaddressable) + Err(DispatchError::NeighborPending) ); } @@ -1558,15 +1558,12 @@ fn test_echo_request_sixlowpan_128_bytes() { ); let tx_token = device.transmit(Instant::now()).unwrap(); - iface - .inner - .dispatch_ieee802154( - Ieee802154Address::default(), - tx_token, - result.unwrap(), - Some(&mut iface.out_packets), - ) - .unwrap(); + iface.inner.dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + result.unwrap(), + Some(&mut iface.out_packets), + ); assert_eq!( device.queue[0], @@ -1698,28 +1695,25 @@ fn test_sixlowpan_udp_with_fragmentation() { ); let tx_token = device.transmit(Instant::now()).unwrap(); - iface - .inner - .dispatch_ieee802154( - Ieee802154Address::default(), - tx_token, - IpPacket::Udp(( - IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::default(), - dst_addr: Ipv6Address::default(), - next_header: IpProtocol::Udp, - payload_len: udp_data.len(), - hop_limit: 64, - }), - UdpRepr { - src_port: 1234, - dst_port: 1234, - }, - udp_data, - )), - Some(&mut iface.out_packets), - ) - .unwrap(); + iface.inner.dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + IpPacket::Udp(( + IpRepr::Ipv6(Ipv6Repr { + src_addr: Ipv6Address::default(), + dst_addr: Ipv6Address::default(), + next_header: IpProtocol::Udp, + payload_len: udp_data.len(), + hop_limit: 64, + }), + UdpRepr { + src_port: 1234, + dst_port: 1234, + }, + udp_data, + )), + Some(&mut iface.out_packets), + ); iface.poll(Instant::now(), &mut device, &mut sockets); diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 9e8a52972..5929424b3 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -312,6 +312,24 @@ impl HardwareAddress { HardwareAddress::Ieee802154(addr) => addr.is_broadcast(), } } + + #[cfg(feature = "medium-ethernet")] + pub(crate) fn ethernet_or_panic(&self) -> EthernetAddress { + match self { + HardwareAddress::Ethernet(addr) => *addr, + #[allow(unreachable_patterns)] + _ => panic!("HardwareAddress is not Ethernet."), + } + } + + #[cfg(feature = "medium-ieee802154")] + pub(crate) fn ieee802154_or_panic(&self) -> Ieee802154Address { + match self { + HardwareAddress::Ieee802154(addr) => *addr, + #[allow(unreachable_patterns)] + _ => panic!("HardwareAddress is not Ethernet."), + } + } } #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] From 84cea137a2da2f2f728bad25241039af6ae2fec5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Jan 2023 00:15:14 +0100 Subject: [PATCH 486/566] Remove crate::{Error, Result}. --- src/lib.rs | 77 -------------------------------------------------- src/phy/mod.rs | 1 - 2 files changed, 78 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f7753a517..03d148879 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,8 +124,6 @@ compile_error!("If you enable the socket feature, you must enable at least one o #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You must enable at most one of the following features: defmt, log"); -use core::fmt; - #[macro_use] mod macros; mod parsers; @@ -144,78 +142,3 @@ pub mod socket; pub mod storage; pub mod time; pub mod wire; - -/// The error type for the networking stack. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[non_exhaustive] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// An operation cannot proceed because a buffer is empty or full. - Exhausted, - /// An operation is not permitted in the current state. - Illegal, - /// An endpoint or address of a remote host could not be translated to a lower level address. - /// E.g. there was no an Ethernet address corresponding to an IPv4 address in the ARP cache, - /// or a TCP connection attempt was made to an unspecified endpoint. - Unaddressable, - - /// The operation is finished. - /// E.g. when reading from a TCP socket, there's no more data to read because the remote - /// has closed the connection. - Finished, - - /// An incoming packet could not be parsed because some of its fields were out of bounds - /// of the received data. - Truncated, - /// An incoming packet had an incorrect checksum and was dropped. - Checksum, - /// An incoming packet could not be recognized and was dropped. - /// E.g. an Ethernet packet with an unknown EtherType. - Unrecognized, - /// An incoming IP packet has been split into several IP fragments and was dropped, - /// since IP reassembly is not supported. - Fragmented, - /// An incoming packet was recognized but was self-contradictory. - /// E.g. a TCP packet with both SYN and FIN flags set. - Malformed, - /// An incoming packet was recognized but contradicted internal state. - /// E.g. a TCP packet addressed to a socket that doesn't exist. - Dropped, - /// An incoming fragment arrived too late. - ReassemblyTimeout, - - /// An incoming packet was recognized but some parts are not supported by smoltcp. - /// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC. - NotSupported, -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -/// The result type for the networking stack. -pub type Result = core::result::Result; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Exhausted => write!(f, "buffer space exhausted"), - Error::Illegal => write!(f, "illegal operation"), - Error::Unaddressable => write!(f, "unaddressable destination"), - Error::Finished => write!(f, "operation finished"), - Error::Truncated => write!(f, "truncated packet"), - Error::Checksum => write!(f, "checksum error"), - Error::Unrecognized => write!(f, "unrecognized packet"), - Error::Fragmented => write!(f, "fragmented packet"), - Error::Malformed => write!(f, "malformed packet"), - Error::Dropped => write!(f, "dropped by socket"), - Error::ReassemblyTimeout => write!(f, "incoming fragment arrived too late"), - Error::NotSupported => write!(f, "not supported by smoltcp"), - } - } -} - -impl From for Error { - fn from(_: wire::Error) -> Self { - Error::Malformed - } -} diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 018c0eac2..cb76fb779 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -20,7 +20,6 @@ An implementation of the [Device](trait.Device.html) trait for a simple hardware Ethernet controller could look as follows: ```rust -use smoltcp::Result; use smoltcp::phy::{self, DeviceCapabilities, Device, Medium}; use smoltcp::time::Instant; From 6da8854cdfd926668929929f8a0dd24d42bea176 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 01:12:10 +0100 Subject: [PATCH 487/566] assembler: fix shift left when coalescing multiple contigs. --- src/storage/assembler.rs | 93 ++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index effa143a2..1c2b892f1 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -229,11 +229,16 @@ impl Assembler { } let shift = j - i - 1; if shift != 0 { - for x in i + 1..self.contigs.len() - shift { + for x in i + 1..self.contigs.len() { if !self.contigs[x].has_data() { break; } - self.contigs[x] = self.contigs[x + shift]; + + self.contigs[x] = self + .contigs + .get(x + shift) + .copied() + .unwrap_or_else(Contig::empty); } } @@ -598,49 +603,53 @@ mod test { fn test_random() { use rand::Rng; - for _ in 0..1000 { - println!("==="); - let mut assr = Assembler::new(); - let mut map = [false; 128]; - - for _ in 0..30 { - let offset = rand::thread_rng().gen_range(0..80); - let size = rand::thread_rng().gen_range(0..47); - - println!("add {}..{} {}", offset, offset + size, size); - // Real impl - let res = assr.add(offset, size); - - // Bitmap impl - let mut map2 = map; - map2[offset..][..size].fill(true); - - let mut contigs = vec![]; - let mut hole: usize = 0; - let mut data: usize = 0; - for b in map2 { - if b { - data += 1; - } else { - if data != 0 { - contigs.push((hole, data)); - hole = 0; - data = 0; + const MAX_INDEX: usize = 256; + + for max_size in [2, 5, 10, 100] { + for _ in 0..300 { + //println!("==="); + let mut assr = Assembler::new(); + let mut map = [false; MAX_INDEX]; + + for _ in 0..60 { + let offset = rand::thread_rng().gen_range(0..MAX_INDEX - max_size - 1); + let size = rand::thread_rng().gen_range(1..=max_size); + + //println!("add {}..{} {}", offset, offset + size, size); + // Real impl + let res = assr.add(offset, size); + + // Bitmap impl + let mut map2 = map; + map2[offset..][..size].fill(true); + + let mut contigs = vec![]; + let mut hole: usize = 0; + let mut data: usize = 0; + for b in map2 { + if b { + data += 1; + } else { + if data != 0 { + contigs.push((hole, data)); + hole = 0; + data = 0; + } + hole += 1; } - hole += 1; } - } - // Compare. - let wanted_res = if contigs.len() > CONTIG_COUNT { - Err(TooManyHolesError) - } else { - Ok(()) - }; - assert_eq!(res, wanted_res); - if res.is_ok() { - map = map2; - assert_eq!(assr, Assembler::from(contigs)); + // Compare. + let wanted_res = if contigs.len() > CONTIG_COUNT { + Err(TooManyHolesError) + } else { + Ok(()) + }; + assert_eq!(res, wanted_res); + if res.is_ok() { + map = map2; + assert_eq!(assr, Assembler::from(contigs)); + } } } } From 8bd28ab66a00688ef6669e00aa465ece456913c5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 01:51:07 +0100 Subject: [PATCH 488/566] tcp: ensure we always accept the segment at offset=0 even if the assembler is full. Fixes #452 --- src/socket/tcp.rs | 42 +++++++++++-------------- src/storage/assembler.rs | 68 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index e4fe47886..52e036ade 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1798,30 +1798,26 @@ impl<'a> Socket<'a> { let assembler_was_empty = self.assembler.is_empty(); // Try adding payload octets to the assembler. - match self.assembler.add(payload_offset, payload_len) { - Ok(()) => { - // Place payload octets into the buffer. - tcp_trace!( - "rx buffer: receiving {} octets at offset {}", - payload_len, - payload_offset - ); - let len_written = self - .rx_buffer - .write_unallocated(payload_offset, repr.payload); - debug_assert!(len_written == payload_len); - } - Err(_) => { - net_debug!( - "assembler: too many holes to add {} octets at offset {}", - payload_len, - payload_offset - ); - return None; - } - } + let Ok(contig_len) = self.assembler.add_then_remove_front(payload_offset, payload_len) else { + net_debug!( + "assembler: too many holes to add {} octets at offset {}", + payload_len, + payload_offset + ); + return None; + }; + + // Place payload octets into the buffer. + tcp_trace!( + "rx buffer: receiving {} octets at offset {}", + payload_len, + payload_offset + ); + let len_written = self + .rx_buffer + .write_unallocated(payload_offset, repr.payload); + debug_assert!(len_written == payload_len); - let contig_len = self.assembler.remove_front(); if contig_len != 0 { // Enqueue the contiguous data octets in front of the buffer. tcp_trace!( diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 1c2b892f1..c0075bdfb 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -269,6 +269,29 @@ impl Assembler { } } + /// Add a segment, then remove_front. + /// + /// This is equivalent to calling `add` then `remove_front` individually, + /// except it's guaranteed to not fail when offset = 0. + /// This is required for TCP: we must never drop the next expected segment, or + /// the protocol might get stuck. + pub fn add_then_remove_front( + &mut self, + offset: usize, + size: usize, + ) -> Result { + // This is the only case where a segment at offset=0 would cause the + // total amount of contigs to rise (and therefore can potentially cause + // a TooManyHolesError). Handle it in a way that is guaranteed to succeed. + if offset == 0 && size < self.contigs[0].hole_size { + self.contigs[0].hole_size -= size; + return Ok(size); + } + + self.add(offset, size)?; + Ok(self.remove_front()) + } + /// Iterate over all of the contiguous data ranges. /// /// This is used in calculating what data ranges have been received. The offset indicates the @@ -598,6 +621,51 @@ mod test { assert_eq!(assr.add(1, 1), Ok(())); } + #[test] + fn test_add_then_remove_front() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add_then_remove_front(10, 10), Ok(0)); + assert_eq!(assr, contigs![(10, 10), (30, 10)]); + } + + #[test] + fn test_add_then_remove_front_at_front() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add_then_remove_front(0, 10), Ok(10)); + assert_eq!(assr, contigs![(40, 10)]); + } + + #[test] + fn test_add_then_remove_front_at_front_touch() { + let mut assr = Assembler::new(); + assert_eq!(assr.add(50, 10), Ok(())); + assert_eq!(assr.add_then_remove_front(0, 50), Ok(60)); + assert_eq!(assr, contigs![]); + } + + #[test] + fn test_add_then_remove_front_at_front_full() { + let mut assr = Assembler::new(); + for c in 1..=CONTIG_COUNT { + assert_eq!(assr.add(c * 10, 3), Ok(())); + } + // Maximum of allowed holes is reached + let assr_before = assr.clone(); + assert_eq!(assr.add_then_remove_front(1, 3), Err(TooManyHolesError)); + assert_eq!(assr_before, assr); + } + + #[test] + fn test_add_then_remove_front_at_front_full_offset_0() { + let mut assr = Assembler::new(); + for c in 1..=CONTIG_COUNT { + assert_eq!(assr.add(c * 10, 3), Ok(())); + } + assert_eq!(assr.add_then_remove_front(0, 3), Ok(3)); + } + // Test against an obviously-correct but inefficient bitmap impl. #[test] fn test_random() { From 9913564e4b6b91daf191afdd1993213b043538a6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 01:51:34 +0100 Subject: [PATCH 489/566] assembler: fix comments --- src/storage/assembler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index c0075bdfb..9a997f2a8 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -170,7 +170,7 @@ impl Assembler { Ok(&mut self.contigs[at]) } - /// Add a new contiguous range to the assembler, and return `Ok(bool)`, + /// Add a new contiguous range to the assembler, /// or return `Err(TooManyHolesError)` if too many discontiguities are already recorded. pub fn add(&mut self, mut offset: usize, size: usize) -> Result<(), TooManyHolesError> { if size == 0 { @@ -256,8 +256,8 @@ impl Assembler { Ok(()) } - /// Remove a contiguous range from the front of the assembler and `Some(data_size)`, - /// or return `None` if there is no such range. + /// Remove a contiguous range from the front of the assembler. + /// If no such range, return 0. pub fn remove_front(&mut self) -> usize { let front = self.front(); if front.has_hole() || !front.has_data() { From c9f3a1c6ffa0b1044a3296cfe150bb757efcb0b3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 00:18:01 +0100 Subject: [PATCH 490/566] wire/ndisc: do not error on unrecognized options. Fixes #546 --- src/wire/ndisc.rs | 128 ++++++++++++++++++---------------------- src/wire/ndiscoption.rs | 2 +- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index ad4839f5b..7ea92447c 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -6,8 +6,7 @@ use crate::time::Duration; use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::Ipv6Address; use crate::wire::RawHardwareAddress; -use crate::wire::{Ipv6Packet, Ipv6Repr}; -use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; +use crate::wire::{NdiscOption, NdiscOptionRepr}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; bitflags! { @@ -226,41 +225,56 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC packet and return a high-level representation of the /// packet. + #[allow(clippy::single_match)] pub fn parse(packet: &Packet<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized, { + fn foreach_option<'a>( + payload: &'a [u8], + mut f: impl FnMut(NdiscOptionRepr<'a>) -> Result<()>, + ) -> Result<()> { + let mut offset = 0; + while payload.len() > offset { + let pkt = NdiscOption::new_checked(&payload[offset..])?; + + // If an option doesn't parse, ignore it and still parse the others. + if let Ok(opt) = NdiscOptionRepr::parse(&pkt) { + f(opt)?; + } + + let len = pkt.data_len() as usize * 8; + if len == 0 { + return Err(Error); + } + offset += len; + } + Ok(()) + } + match packet.msg_type() { Message::RouterSolicit => { - let lladdr = if !packet.payload().is_empty() { - let opt = NdiscOption::new_checked(packet.payload())?; - match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { - return Err(Error); - } + let mut lladdr = None; + foreach_option(packet.payload(), |opt| { + match opt { + NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), + _ => {} } - } else { - None - }; + Ok(()) + })?; Ok(Repr::RouterSolicit { lladdr }) } Message::RouterAdvert => { - let mut offset = 0; let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); - while packet.payload().len() - offset > 0 { - let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?; - let opt = NdiscOptionRepr::parse(&pkt)?; + foreach_option(packet.payload(), |opt| { match opt { NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info), - _ => { - return Err(Error); - } + _ => {} } - offset += opt.buffer_len(); - } + Ok(()) + })?; Ok(Repr::RouterAdvert { hop_limit: packet.current_hop_limit(), flags: packet.router_flags(), @@ -273,34 +287,28 @@ impl<'a> Repr<'a> { }) } Message::NeighborSolicit => { - let lladdr = if !packet.payload().is_empty() { - let opt = NdiscOption::new_checked(packet.payload())?; - match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { - return Err(Error); - } + let mut lladdr = None; + foreach_option(packet.payload(), |opt| { + match opt { + NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), + _ => {} } - } else { - None - }; + Ok(()) + })?; Ok(Repr::NeighborSolicit { target_addr: packet.target_addr(), lladdr, }) } Message::NeighborAdvert => { - let lladdr = if !packet.payload().is_empty() { - let opt = NdiscOption::new_checked(packet.payload())?; - match opt.option_type() { - NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), - _ => { - return Err(Error); - } + let mut lladdr = None; + foreach_option(packet.payload(), |opt| { + match opt { + NdiscOptionRepr::TargetLinkLayerAddr(addr) => lladdr = Some(addr), + _ => {} } - } else { - None - }; + Ok(()) + })?; Ok(Repr::NeighborAdvert { flags: packet.neighbor_flags(), target_addr: packet.target_addr(), @@ -308,38 +316,16 @@ impl<'a> Repr<'a> { }) } Message::Redirect => { - let mut offset = 0; let (mut lladdr, mut redirected_hdr) = (None, None); - while packet.payload().len() - offset > 0 { - let opt = NdiscOption::new_checked(&packet.payload()[offset..])?; - match opt.option_type() { - NdiscOptionType::SourceLinkLayerAddr => { - let addr = opt.link_layer_addr(); - offset += NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len(); - lladdr = Some(addr); - } - NdiscOptionType::RedirectedHeader => { - let opt_data = opt.data(); - - if opt.data_len() < 6 || opt_data.len() < offset + 8 { - return Err(Error); - }; - - let ip_packet = Ipv6Packet::new_checked(&opt_data[offset + 8..])?; - let ip_repr = Ipv6Repr::parse(&ip_packet)?; - let data = ip_packet.payload(); - let redirected = NdiscRedirectedHeader { - header: ip_repr, - data, - }; - offset += NdiscOptionRepr::RedirectedHeader(redirected).buffer_len(); - redirected_hdr = Some(redirected); - } - _ => { - return Err(Error); - } + + foreach_option(packet.payload(), |opt| { + match opt { + NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), + NdiscOptionRepr::RedirectedHeader(rh) => redirected_hdr = Some(rh), + _ => {} } - } + Ok(()) + })?; Ok(Repr::Redirect { target_addr: packet.target_addr(), dest_addr: packet.dest_addr(), diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index e72c5a619..51b883fbe 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -427,7 +427,7 @@ pub enum Repr<'a> { impl<'a> Repr<'a> { /// Parse an NDISC Option and return a high-level representation. - pub fn parse(opt: &'a NdiscOption<&'a T>) -> Result> + pub fn parse(opt: &NdiscOption<&'a T>) -> Result> where T: AsRef<[u8]> + ?Sized, { From 4fd767efedf6351e3d4895710f05ce3aee3d2bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Mon, 16 Jan 2023 06:51:06 +0100 Subject: [PATCH 491/566] Allow specifying different server/client DHCP ports The default linux dhclient utility allows to use DHCP with non-standard ports. This change adds support for those usecases. --- src/iface/interface/ipv4.rs | 14 +++--- src/socket/dhcpv4.rs | 89 ++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 653c13454..69096c291 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -90,15 +90,15 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "socket-dhcpv4")] { if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { - // First check for source and dest ports, then do `UdpRepr::parse` if they match. - // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) let udp_packet = check!(UdpPacket::new_checked(ip_payload)); - if udp_packet.src_port() == DHCP_SERVER_PORT - && udp_packet.dst_port() == DHCP_CLIENT_PORT + if let Some(dhcp_socket) = sockets + .items_mut() + .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) { - if let Some(dhcp_socket) = sockets - .items_mut() - .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) + // First check for source and dest ports, then do `UdpRepr::parse` if they match. + // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`) + if udp_packet.src_port() == dhcp_socket.server_port + && udp_packet.dst_port() == dhcp_socket.client_port { let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let udp_repr = check!(UdpRepr::parse( diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 062c0daa2..ad99679e2 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -149,6 +149,12 @@ pub struct Socket<'a> { /// Ignore NAKs. ignore_naks: bool, + /// Server port config + pub(crate) server_port: u16, + + /// Client port config + pub(crate) client_port: u16, + /// A buffer contains options additional to be added to outgoing DHCP /// packets. outgoing_options: &'a [DhcpOption<'a>], @@ -186,6 +192,8 @@ impl<'a> Socket<'a> { receive_packet_buffer: None, #[cfg(feature = "async")] waker: WakerRegistration::new(), + server_port: DHCP_SERVER_PORT, + client_port: DHCP_CLIENT_PORT, } } @@ -247,6 +255,15 @@ impl<'a> Socket<'a> { self.ignore_naks = ignore_naks; } + /// Set the server/client port + /// + /// Allows you to specify the ports used by DHCP. + /// This is meant to support esoteric usecases allowed by the dhclient program. + pub fn set_ports(&mut self, server_port: u16, client_port: u16) { + self.server_port = server_port; + self.client_port = client_port; + } + pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt { let t = match &self.state { ClientState::Discovering(state) => state.retry_at, @@ -266,7 +283,7 @@ impl<'a> Socket<'a> { let src_ip = ip_repr.src_addr; // This is enforced in interface.rs. - assert!(repr.src_port == DHCP_SERVER_PORT && repr.dst_port == DHCP_CLIENT_PORT); + assert!(repr.src_port == self.server_port && repr.dst_port == self.client_port); let dhcp_packet = match DhcpPacket::new_checked(payload) { Ok(dhcp_packet) => dhcp_packet, @@ -528,8 +545,8 @@ impl<'a> Socket<'a> { }; let udp_repr = UdpRepr { - src_port: DHCP_CLIENT_PORT, - dst_port: DHCP_SERVER_PORT, + src_port: self.client_port, + dst_port: self.server_port, }; let mut ipv4_repr = Ipv4Repr { @@ -842,12 +859,24 @@ mod test { }; const UDP_SEND: UdpRepr = UdpRepr { - src_port: 68, - dst_port: 67, + src_port: DHCP_CLIENT_PORT, + dst_port: DHCP_SERVER_PORT, }; const UDP_RECV: UdpRepr = UdpRepr { - src_port: 67, - dst_port: 68, + src_port: DHCP_SERVER_PORT, + dst_port: DHCP_CLIENT_PORT, + }; + + const DIFFERENT_CLIENT_PORT: u16 = 6800; + const DIFFERENT_SERVER_PORT: u16 = 6700; + + const UDP_SEND_DIFFERENT_PORT: UdpRepr = UdpRepr { + src_port: DIFFERENT_CLIENT_PORT, + dst_port: DIFFERENT_SERVER_PORT, + }; + const UDP_RECV_DIFFERENT_PORT: UdpRepr = UdpRepr { + src_port: DIFFERENT_SERVER_PORT, + dst_port: DIFFERENT_CLIENT_PORT, }; const DHCP_DEFAULT: DhcpRepr = DhcpRepr { @@ -956,6 +985,17 @@ mod test { } } + fn socket_different_port() -> TestSocket { + let mut s = Socket::new(); + s.set_ports(DIFFERENT_SERVER_PORT, DIFFERENT_CLIENT_PORT); + + assert_eq!(s.poll(), Some(Event::Deconfigured)); + TestSocket { + socket: s, + cx: Context::mock(), + } + } + fn socket_bound() -> TestSocket { let mut s = socket(); s.state = ClientState::Renewing(RenewState { @@ -1011,6 +1051,41 @@ mod test { } } + #[test] + fn test_bind_different_ports() { + let mut s = socket_different_port(); + + recv!(s, [(IP_BROADCAST, UDP_SEND_DIFFERENT_PORT, DHCP_DISCOVER)]); + assert_eq!(s.poll(), None); + send!(s, (IP_RECV, UDP_RECV_DIFFERENT_PORT, dhcp_offer())); + assert_eq!(s.poll(), None); + recv!(s, [(IP_BROADCAST, UDP_SEND_DIFFERENT_PORT, DHCP_REQUEST)]); + assert_eq!(s.poll(), None); + send!(s, (IP_RECV, UDP_RECV_DIFFERENT_PORT, dhcp_ack())); + + assert_eq!( + s.poll(), + Some(Event::Configured(Config { + server: ServerInfo { + address: SERVER_IP, + identifier: SERVER_IP, + }, + address: Ipv4Cidr::new(MY_IP, 24), + dns_servers: Vec::from_slice(DNS_IPS).unwrap(), + router: Some(SERVER_IP), + packet: None, + })) + ); + + match &s.state { + ClientState::Renewing(r) => { + assert_eq!(r.renew_at, Instant::from_secs(500)); + assert_eq!(r.expires_at, Instant::from_secs(1000)); + } + _ => panic!("Invalid state"), + } + } + #[test] fn test_discover_retransmit() { let mut s = socket(); From 0af56fac5884149d35bf6e4225b4d5ddc8d58311 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 15 Jan 2023 22:52:46 +0100 Subject: [PATCH 492/566] dhcpv4: use let-else to get eth addr. --- src/socket/dhcpv4.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index ad99679e2..0ab595604 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -299,12 +299,12 @@ impl<'a> Socket<'a> { return; } }; - let hardware_addr = match cx.hardware_addr() { - Some(HardwareAddress::Ethernet(addr)) => addr, - _ => return, + + let Some(HardwareAddress::Ethernet(ethernet_addr)) = cx.hardware_addr() else { + panic!("using DHCPv4 socket with a non-ethernet hardware address."); }; - if dhcp_repr.client_hardware_address != hardware_addr { + if dhcp_repr.client_hardware_address != ethernet_addr { return; } if dhcp_repr.transaction_id != self.transaction_id { @@ -503,9 +503,7 @@ impl<'a> Socket<'a> { { // note: Dhcpv4Socket is only usable in ethernet mediums, so the // unwrap can never fail. - let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr() { - addr - } else { + let Some(HardwareAddress::Ethernet(ethernet_addr)) = cx.hardware_addr() else { panic!("using DHCPv4 socket with a non-ethernet hardware address."); }; From c015cc358eb20eb3e8e35299008571148cfa6ea1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 02:04:41 +0100 Subject: [PATCH 493/566] iface: use owned frag buffer, remove frag from builder. --- examples/client.rs | 10 --- examples/server.rs | 10 --- examples/sixlowpan.rs | 7 --- examples/sixlowpan_benchmark.rs | 3 +- src/iface/fragmentation.rs | 24 ++----- src/iface/interface/mod.rs | 105 +++++++------------------------ src/iface/interface/sixlowpan.rs | 12 +--- src/iface/interface/tests.rs | 12 ---- 8 files changed, 30 insertions(+), 153 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index 83da040a4..dd560875b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -46,16 +46,6 @@ fn main() { let medium = device.capabilities().medium; let builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); - #[cfg(feature = "proto-ipv4-fragmentation")] - let mut ipv4_out_packet_cache = [0u8; 1280]; - #[cfg(feature = "proto-ipv4-fragmentation")] - let builder = builder.ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut sixlowpan_out_packet_cache = [0u8; 1280]; - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let builder = builder.sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); - let builder = if medium == Medium::Ethernet { builder .hardware_addr(ethernet_addr.into()) diff --git a/examples/server.rs b/examples/server.rs index bb0dc133e..001a2074d 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -73,16 +73,6 @@ fn main() { .as_secs(), ); - #[cfg(feature = "proto-ipv4-fragmentation")] - let mut ipv4_out_packet_cache = [0u8; 10_000]; - #[cfg(feature = "proto-ipv4-fragmentation")] - let builder = builder.ipv4_fragmentation_buffer(&mut ipv4_out_packet_cache[..]); - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut sixlowpan_out_packet_cache = [0u8; 1280]; - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut builder = builder.sixlowpan_fragmentation_buffer(&mut sixlowpan_out_packet_cache[..]); - if medium == Medium::Ethernet { builder = builder .hardware_addr(ethernet_addr.into()) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 0013f0dba..c0cd77348 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -95,13 +95,6 @@ fn main() { .hardware_addr(ieee802154_addr.into()) .neighbor_cache(neighbor_cache); - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut out_packet_buffer = [0u8; 1280]; - #[cfg(feature = "proto-sixlowpan-fragmentation")] - { - builder = builder.sixlowpan_fragmentation_buffer(&mut out_packet_buffer[..]); - } - let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 11b2a62d7..71daf5653 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -172,8 +172,7 @@ fn main() { .pan_id(Ieee802154Pan(0xbeef)); builder = builder .hardware_addr(ieee802154_addr.into()) - .neighbor_cache(neighbor_cache) - .sixlowpan_fragmentation_buffer(vec![]); + .neighbor_cache(neighbor_cache); let mut iface = builder.finalize(&mut device); let mut sockets = SocketSet::new(vec![]); diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 6c3a3d75b..a02d815c7 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -111,16 +111,8 @@ impl PacketAssembler { offset ); - match self.assembler.add(offset, len) { - Ok(()) => { - net_debug!("assembler: {}", self.assembler); - Ok(()) - } - Err(_) => { - net_debug!("packet assembler: too many holes, dropping."); - Err(AssemblerError) - } - } + self.assembler.add(offset, len); + Ok(()) } /// Add a fragment into the packet that is being reassembled. @@ -149,16 +141,8 @@ impl PacketAssembler { offset ); - match self.assembler.add(offset, data.len()) { - Ok(()) => { - net_debug!("assembler: {}", self.assembler); - Ok(()) - } - Err(_) => { - net_debug!("packet assembler: too many holes, dropping."); - Err(AssemblerError) - } - } + self.assembler.add(offset, data.len()); + Ok(()) } /// Get an immutable slice of the underlying packet data, if reassembly complete. diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index f2f83a161..bf2bd5651 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -22,7 +22,6 @@ use core::cmp; use core::marker::PhantomData; use core::result::Result; use heapless::{LinearMap, Vec}; -use managed::ManagedSlice; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] use super::fragmentation::PacketAssemblerSet; @@ -41,6 +40,7 @@ use crate::wire::*; const MAX_IP_ADDR_COUNT: usize = 5; #[cfg(feature = "proto-igmp")] const MAX_IPV4_MULTICAST_GROUPS: usize = 4; +const FRAGMENTATION_BUFFER_SIZE: usize = 1500; pub(crate) struct FragmentsBuffer { #[cfg(feature = "proto-sixlowpan")] @@ -53,17 +53,14 @@ pub(crate) struct FragmentsBuffer { sixlowpan_fragments_cache_timeout: Duration, } -pub(crate) struct OutPackets<'a> { +pub(crate) struct OutPackets { #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket<'a>, + ipv4_out_packet: Ipv4OutPacket, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket<'a>, - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - _lifetime: core::marker::PhantomData<&'a ()>, + sixlowpan_out_packet: SixlowpanOutPacket, } -impl<'a> OutPackets<'a> { +impl OutPackets { #[cfg(any( feature = "proto-ipv4-fragmentation", feature = "proto-sixlowpan-fragmentation" @@ -86,9 +83,9 @@ impl<'a> OutPackets<'a> { #[allow(unused)] #[cfg(feature = "proto-ipv4-fragmentation")] -pub(crate) struct Ipv4OutPacket<'a> { +pub(crate) struct Ipv4OutPacket { /// The buffer that holds the unfragmented 6LoWPAN packet. - buffer: ManagedSlice<'a, u8>, + buffer: [u8; FRAGMENTATION_BUFFER_SIZE], /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. packet_len: usize, /// The amount of bytes that already have been transmitted. @@ -106,10 +103,10 @@ pub(crate) struct Ipv4OutPacket<'a> { } #[cfg(feature = "proto-ipv4-fragmentation")] -impl<'a> Ipv4OutPacket<'a> { - pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { +impl Ipv4OutPacket { + pub(crate) fn new() -> Self { Self { - buffer, + buffer: [0u8; FRAGMENTATION_BUFFER_SIZE], packet_len: 0, sent_bytes: 0, repr: Ipv4Repr { @@ -158,9 +155,9 @@ impl<'a> Ipv4OutPacket<'a> { #[allow(unused)] #[cfg(feature = "proto-sixlowpan")] -pub(crate) struct SixlowpanOutPacket<'a> { +pub(crate) struct SixlowpanOutPacket { /// The buffer that holds the unfragmented 6LoWPAN packet. - buffer: ManagedSlice<'a, u8>, + buffer: [u8; FRAGMENTATION_BUFFER_SIZE], /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. packet_len: usize, /// The amount of bytes that already have been transmitted. @@ -182,10 +179,10 @@ pub(crate) struct SixlowpanOutPacket<'a> { } #[cfg(feature = "proto-sixlowpan-fragmentation")] -impl<'a> SixlowpanOutPacket<'a> { - pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { +impl SixlowpanOutPacket { + pub(crate) fn new() -> Self { Self { - buffer, + buffer: [0u8; FRAGMENTATION_BUFFER_SIZE], packet_len: 0, datagram_size: 0, datagram_tag: 0, @@ -246,7 +243,7 @@ use check; pub struct Interface<'a> { inner: InterfaceInner<'a>, fragments: FragmentsBuffer, - out_packets: OutPackets<'a>, + out_packets: OutPackets, } /// The device independent part of an Ethernet network interface. @@ -307,17 +304,8 @@ pub struct InterfaceBuilder<'a> { ipv4_multicast_groups: LinearMap, random_seed: u64, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_buffer: ManagedSlice<'a, u8>, - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_buffer_timeout: Duration, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: ManagedSlice<'a, u8>, #[cfg(feature = "proto-sixlowpan")] sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], @@ -377,17 +365,8 @@ let iface = builder.finalize(&mut device); ipv4_multicast_groups: LinearMap::new(), random_seed: 0, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet::new(), - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), #[cfg(feature = "proto-sixlowpan")] sixlowpan_address_context: &[], @@ -502,23 +481,6 @@ let iface = builder.finalize(&mut device); self } - /// Set the IPv4 reassembly buffer the interface will use. - #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet) -> Self { - self.ipv4_fragments = storage; - self - } - - /// Set the IPv4 fragments buffer the interface will use. - #[cfg(feature = "proto-ipv4-fragmentation")] - pub fn ipv4_fragmentation_buffer(mut self, storage: T) -> Self - where - T: Into>, - { - self.ipv4_out_buffer = storage.into(); - self - } - /// Set the address contexts the interface will use. #[cfg(feature = "proto-sixlowpan")] pub fn sixlowpan_address_context( @@ -529,16 +491,6 @@ let iface = builder.finalize(&mut device); self } - /// Set the 6LoWPAN reassembly buffer the interface will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_reassembly_buffer( - mut self, - storage: PacketAssemblerSet, - ) -> Self { - self.sixlowpan_fragments = storage; - self - } - /// Set the timeout value the 6LoWPAN reassembly buffer will use. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { @@ -549,16 +501,6 @@ let iface = builder.finalize(&mut device); self } - /// Set the 6LoWPAN fragments buffer the interface will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_fragmentation_buffer(mut self, storage: T) -> Self - where - T: Into>, - { - self.sixlowpan_out_buffer = storage.into(); - self - } - /// Create a network interface using the previously provided configuration. /// /// # Panics @@ -654,20 +596,17 @@ let iface = builder.finalize(&mut device); decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN], #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: self.ipv4_fragments, + ipv4_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: self.sixlowpan_fragments, + sixlowpan_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, }, out_packets: OutPackets { #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket::new(self.ipv4_out_buffer), + ipv4_out_packet: Ipv4OutPacket::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new(self.sixlowpan_out_buffer), - - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] - _lifetime: core::marker::PhantomData, + sixlowpan_out_packet: SixlowpanOutPacket::new(), }, inner: InterfaceInner { phantom: PhantomData, @@ -1706,7 +1645,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, packet: EthernetPacket, - _out_packet: Option<&mut OutPackets<'_>>, + _out_packet: Option<&mut OutPackets>, ) -> Result<(), DispatchError> where Tx: TxToken, @@ -1921,7 +1860,7 @@ impl<'a> InterfaceInner<'a> { &mut self, tx_token: Tx, packet: IpPacket, - _out_packet: Option<&mut OutPackets<'_>>, + _out_packet: Option<&mut OutPackets>, ) -> Result<(), DispatchError> { let mut ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index ec429f11b..dbd7c0b60 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -384,18 +384,12 @@ impl<'a> InterfaceInner<'a> { .. } = &mut _out_packet.unwrap().sixlowpan_out_packet; - match buffer { - managed::ManagedSlice::Borrowed(buffer) => { - if buffer.len() < total_size { - net_debug!( + if buffer.len() < total_size { + net_debug!( "dispatch_ieee802154: dropping, fragmentation buffer is too small, at least {} needed", total_size ); - return; - } - } - #[cfg(feature = "alloc")] - managed::ManagedSlice::Owned(buffer) => buffer.resize(total_size, 0), + return; } *ll_dst_addr = ll_dst_a; diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index d40787f15..476ee94fd 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -56,9 +56,6 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder.ipv4_fragmentation_buffer(vec![]); - let iface = iface_builder.finalize(&mut device); (iface, SocketSet::new(vec![]), device) @@ -87,12 +84,6 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .neighbor_cache(NeighborCache::new()) .ip_addrs(ip_addrs); - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let iface_builder = iface_builder.sixlowpan_fragmentation_buffer(vec![]); - - #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = iface_builder.ipv4_fragmentation_buffer(vec![]); - let iface = iface_builder.finalize(&mut device); (iface, SocketSet::new(vec![]), device) @@ -117,9 +108,6 @@ fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { .neighbor_cache(NeighborCache::new()) .ip_addrs(ip_addrs); - #[cfg(feature = "proto-sixlowpan-fragmentation")] - let iface_builder = iface_builder.sixlowpan_fragmentation_buffer(vec![]); - let iface = iface_builder.finalize(&mut device); (iface, SocketSet::new(vec![]), device) From bc7fe74d55f45c35f6644ec1100b200085ba093e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 02:13:47 +0100 Subject: [PATCH 494/566] sixlowpan: make address context owned. --- src/iface/interface/mod.rs | 14 ++++++++------ src/iface/interface/sixlowpan.rs | 2 +- src/iface/interface/tests.rs | 2 +- src/wire/sixlowpan.rs | 25 ++++++------------------- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index bf2bd5651..7ca9a2413 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -41,6 +41,8 @@ const MAX_IP_ADDR_COUNT: usize = 5; #[cfg(feature = "proto-igmp")] const MAX_IPV4_MULTICAST_GROUPS: usize = 4; const FRAGMENTATION_BUFFER_SIZE: usize = 1500; +#[cfg(feature = "proto-sixlowpan")] +const SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4; pub(crate) struct FragmentsBuffer { #[cfg(feature = "proto-sixlowpan")] @@ -271,7 +273,7 @@ pub struct InterfaceInner<'a> { #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: u16, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + sixlowpan_address_context: Vec, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: Vec, @@ -308,7 +310,7 @@ pub struct InterfaceBuilder<'a> { sixlowpan_reassembly_buffer_timeout: Duration, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + sixlowpan_address_context: Vec, } impl<'a> InterfaceBuilder<'a> { @@ -369,7 +371,7 @@ let iface = builder.finalize(&mut device); sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], + sixlowpan_address_context: Vec::new(), } } @@ -485,7 +487,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan")] pub fn sixlowpan_address_context( mut self, - sixlowpan_address_context: &'a [SixlowpanAddressContext<'a>], + sixlowpan_address_context: Vec, ) -> Self { self.sixlowpan_address_context = sixlowpan_address_context; self @@ -633,7 +635,7 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], + sixlowpan_address_context: Vec::new(), rand, }, } @@ -1353,7 +1355,7 @@ impl<'a> InterfaceInner<'a> { tag: 1, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: &[], + sixlowpan_address_context: Vec::new(), #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: 1, diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index dbd7c0b60..6996b6df5 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -178,7 +178,7 @@ impl<'a> InterfaceInner<'a> { &iphc, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, - self.sixlowpan_address_context, + &self.sixlowpan_address_context, )?; let mut decompressed_size = 40 + iphc.payload().len(); diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 476ee94fd..1990c5424 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1455,7 +1455,7 @@ fn test_echo_request_sixlowpan_128_bytes() { &request_first_part_iphc_packet, ieee802154_repr.src_addr, ieee802154_repr.dst_addr, - iface.inner.sixlowpan_address_context, + &iface.inner.sixlowpan_address_context, ) .unwrap(); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 310ffb127..6614ad3d6 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -2,24 +2,17 @@ //! IEEE802.154-based networks. //! //! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282 -use core::ops::Deref; use super::{Error, Result}; use crate::wire::ieee802154::Address as LlAddress; use crate::wire::ipv6; use crate::wire::IpProtocol; +const ADDRESS_CONTEXT_LENGTH: usize = 8; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct AddressContext<'a>(pub &'a [u8]); - -impl<'a> Deref for AddressContext<'a> { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - self.0 - } -} +pub struct AddressContext(pub [u8; ADDRESS_CONTEXT_LENGTH]); /// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with /// and without context information. The decompression with context information is not yet @@ -68,7 +61,7 @@ impl<'a> UnresolvedAddress<'a> { pub fn resolve( self, ll_address: Option, - addr_context: &[AddressContext<'_>], + addr_context: &[AddressContext], ) -> Result { let mut bytes = [0; 16]; @@ -78,13 +71,7 @@ impl<'a> UnresolvedAddress<'a> { } let context = addr_context[index]; - let len = context.len(); - - if len > 8 { - return Err(Error); - } - - bytes[..len].copy_from_slice(&context); + bytes[..ADDRESS_CONTEXT_LENGTH].copy_from_slice(&context.0); Ok(()) }; @@ -1181,7 +1168,7 @@ pub mod iphc { packet: &Packet<&T>, ll_src_addr: Option, ll_dst_addr: Option, - addr_context: &[AddressContext<'_>], + addr_context: &[AddressContext], ) -> Result { // Ensure basic accessors will work. packet.check_len()?; From b73c943eee0554a3e7e4632b38ae68a922edb7e9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Jan 2023 02:19:39 +0100 Subject: [PATCH 495/566] iface: remove lifetimes. --- examples/dhcp_client.rs | 2 +- src/iface/interface/ethernet.rs | 2 +- src/iface/interface/igmp.rs | 4 ++-- src/iface/interface/ipv4.rs | 2 +- src/iface/interface/ipv6.rs | 2 +- src/iface/interface/mod.rs | 29 ++++++++++------------------- src/iface/interface/sixlowpan.rs | 2 +- src/iface/interface/tests.rs | 8 ++++---- src/socket/dhcpv4.rs | 2 +- src/socket/tcp.rs | 2 +- 10 files changed, 23 insertions(+), 32 deletions(-) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 0437fd232..83443ee63 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -90,7 +90,7 @@ fn main() { } } -fn set_ipv4_addr(iface: &mut Interface<'_>, cidr: Ipv4Cidr) { +fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) { iface.update_ip_addrs(|addrs| { let dest = addrs.iter_mut().next().unwrap(); *dest = IpCidr::Ipv4(cidr); diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index 7ea279356..d208a34d9 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -9,7 +9,7 @@ use core::result::Result; use crate::phy::TxToken; use crate::wire::*; -impl<'i> InterfaceInner<'i> { +impl InterfaceInner { #[cfg(feature = "medium-ethernet")] pub(super) fn process_ethernet<'frame, T: AsRef<[u8]>>( &mut self, diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index 8e7c3a15c..8ac6497a4 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -17,7 +17,7 @@ pub enum MulticastError { Ipv6NotSupported, } -impl<'a> Interface<'a> { +impl Interface { /// Add an address to a list of subscribed multicast IP addresses. /// /// Returns `Ok(announce_sent)` if the address was added successfully, where `annouce_sent` @@ -180,7 +180,7 @@ impl<'a> Interface<'a> { } } -impl<'a> InterfaceInner<'a> { +impl InterfaceInner { /// Check whether the interface listens to given destination multicast IP address. /// /// If built without feature `proto-igmp` this function will diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 69096c291..25ebfc854 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -21,7 +21,7 @@ use crate::phy::{Medium, TxToken}; use crate::time::{Duration, Instant}; use crate::wire::*; -impl<'a> InterfaceInner<'a> { +impl InterfaceInner { pub(super) fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index 529db9878..220ee13b2 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -10,7 +10,7 @@ use crate::socket::AnySocket; use crate::wire::*; -impl<'a> InterfaceInner<'a> { +impl InterfaceInner { #[cfg(feature = "proto-ipv6")] pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 7ca9a2413..95e69012b 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -19,7 +19,6 @@ mod ipv6; mod igmp; use core::cmp; -use core::marker::PhantomData; use core::result::Result; use heapless::{LinearMap, Vec}; @@ -242,8 +241,8 @@ use check; /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be /// a `&mut [T]`, or `Vec` if a heap is available. -pub struct Interface<'a> { - inner: InterfaceInner<'a>, +pub struct Interface { + inner: InterfaceInner, fragments: FragmentsBuffer, out_packets: OutPackets, } @@ -255,13 +254,11 @@ pub struct Interface<'a> { /// the `device` mutably until they're used, which makes it impossible to call other /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. -pub struct InterfaceInner<'a> { +pub struct InterfaceInner { caps: DeviceCapabilities, now: Instant, rand: Rand, - phantom: PhantomData<&'a mut ()>, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] neighbor_cache: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -288,9 +285,7 @@ pub struct InterfaceInner<'a> { } /// A builder structure used for creating a network interface. -pub struct InterfaceBuilder<'a> { - phantom: PhantomData<&'a mut ()>, - +pub struct InterfaceBuilder { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Option, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -313,7 +308,7 @@ pub struct InterfaceBuilder<'a> { sixlowpan_address_context: Vec, } -impl<'a> InterfaceBuilder<'a> { +impl InterfaceBuilder { /// Create a builder used for creating a network interface using the /// given device and address. #[cfg_attr( @@ -349,8 +344,6 @@ let iface = builder.finalize(&mut device); #[allow(clippy::new_without_default)] pub fn new() -> Self { InterfaceBuilder { - phantom: PhantomData, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: None, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -449,7 +442,7 @@ let iface = builder.finalize(&mut device); /// [routes]. /// /// [routes]: struct.Interface.html#method.routes - pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a> + pub fn routes(mut self, routes: T) -> Self where T: Into, { @@ -514,7 +507,7 @@ let iface = builder.finalize(&mut device); /// /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self, device: &mut D) -> Interface<'a> + pub fn finalize(self, device: &mut D) -> Interface where D: Device + ?Sized, { @@ -611,7 +604,6 @@ let iface = builder.finalize(&mut device); sixlowpan_out_packet: SixlowpanOutPacket::new(), }, inner: InterfaceInner { - phantom: PhantomData, now: Instant::from_secs(0), caps, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -792,11 +784,11 @@ enum IgmpReportState { }, } -impl<'a> Interface<'a> { +impl Interface { /// Get the socket context. /// /// The context is needed for some socket methods. - pub fn context(&mut self) -> &mut InterfaceInner<'a> { + pub fn context(&mut self) -> &mut InterfaceInner { &mut self.inner } @@ -1232,7 +1224,7 @@ impl<'a> Interface<'a> { } } -impl<'a> InterfaceInner<'a> { +impl InterfaceInner { #[allow(unused)] // unused depending on which sockets are enabled pub(crate) fn now(&self) -> Instant { self.now @@ -1304,7 +1296,6 @@ impl<'a> InterfaceInner<'a> { #[cfg(test)] pub(crate) fn mock() -> Self { Self { - phantom: PhantomData, caps: DeviceCapabilities { #[cfg(feature = "medium-ethernet")] medium: crate::phy::Medium::Ethernet, diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 6996b6df5..9bbdbe244 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -16,7 +16,7 @@ use crate::wire::*; // TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size) pub(crate) const MAX_DECOMPRESSED_LEN: usize = 1500; -impl<'a> InterfaceInner<'a> { +impl InterfaceInner { #[cfg(feature = "medium-ieee802154")] pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 1990c5424..c3dcf13be 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -24,7 +24,7 @@ const MEDIUM: Medium = Medium::Ip; #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] const MEDIUM: Medium = Medium::Ieee802154; -fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { +fn create<'a>(medium: Medium) -> (Interface, SocketSet<'a>, Loopback) { match medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => create_ethernet(), @@ -37,7 +37,7 @@ fn create<'a>(medium: Medium) -> (Interface<'a>, SocketSet<'a>, Loopback) { #[cfg(feature = "medium-ip")] #[allow(unused)] -fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { +fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); let mut ip_addrs = heapless::Vec::::new(); @@ -62,7 +62,7 @@ fn create_ip<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { } #[cfg(feature = "medium-ethernet")] -fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { +fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); let mut ip_addrs = heapless::Vec::::new(); @@ -90,7 +90,7 @@ fn create_ethernet<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { } #[cfg(feature = "medium-ieee802154")] -fn create_ieee802154<'a>() -> (Interface<'a>, SocketSet<'a>, Loopback) { +fn create_ieee802154<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); let mut ip_addrs = heapless::Vec::::new(); diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 0ab595604..e88d783b4 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -722,7 +722,7 @@ mod test { struct TestSocket { socket: Socket<'static>, - cx: Context<'static>, + cx: Context, } impl Deref for TestSocket { diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 52e036ade..8c9700412 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2415,7 +2415,7 @@ mod test { struct TestSocket { socket: Socket<'static>, - cx: Context<'static>, + cx: Context, } impl Deref for TestSocket { From 5740b765749b95c18aace5de8dc21cab75ba33d4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 14:25:54 +0100 Subject: [PATCH 496/566] iface: remove builder. --- examples/benchmark.rs | 27 +- examples/client.rs | 55 +-- examples/dhcp_client.rs | 23 +- examples/dns.rs | 60 ++-- examples/httpclient.rs | 57 ++-- examples/loopback.rs | 22 +- examples/multicast.rs | 55 ++- examples/ping.rs | 65 ++-- examples/server.rs | 62 ++-- examples/sixlowpan.rs | 45 ++- examples/sixlowpan_benchmark.rs | 40 +-- src/iface/interface/ipv4.rs | 6 +- src/iface/interface/mod.rs | 574 +++++++++++++------------------- src/iface/interface/tests.rs | 105 +++--- src/iface/mod.rs | 12 +- src/socket/tcp.rs | 18 +- 16 files changed, 543 insertions(+), 683 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index ba6977c19..9f908987f 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -9,7 +9,7 @@ use std::os::unix::io::AsRawFd; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; @@ -80,8 +80,6 @@ fn main() { _ => panic!("invalid mode"), }; - let neighbor_cache = NeighborCache::new(); - let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer); @@ -90,19 +88,18 @@ fn main() { let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) - .unwrap(); - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); } - let mut iface = builder.finalize(&mut device); + + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + }); let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); diff --git a/examples/client.rs b/examples/client.rs index dd560875b..8543ae065 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -4,11 +4,11 @@ use log::debug; use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; fn main() { utils::setup_logging(""); @@ -28,33 +28,38 @@ fn main() { let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let port = u16::from_str(&matches.free[1]).expect("invalid port format"); - let neighbor_cache = NeighborCache::new(); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) + .unwrap(); + iface + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) + .unwrap(); + + // Create sockets let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); - - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)) - .unwrap(); - let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); - let mut routes = Routes::new(); - routes.add_default_ipv4_route(default_v4_gw).unwrap(); - - let medium = device.capabilities().medium; - let builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); - - let builder = if medium == Medium::Ethernet { - builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache) - } else { - builder - }; - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 83443ee63..96654241a 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -4,7 +4,7 @@ mod utils; use log::*; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::socket::dhcpv4; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr}; @@ -27,22 +27,15 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(); - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)) - .unwrap(); - - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); } - let mut iface = builder.finalize(&mut device); + let mut iface = Interface::new(config, &mut device); + // Create sockets let mut dhcp_socket = dhcpv4::Socket::new(); // Set a ridiculously short max lease time to show DHCP renews work properly. diff --git a/examples/dns.rs b/examples/dns.rs index 53f971114..1c30ab1d3 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -7,14 +7,12 @@ extern crate smoltcp; mod utils; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::Device; use smoltcp::phy::{wait as phy_wait, Medium}; use smoltcp::socket::dns::{self, GetQueryResultError}; use smoltcp::time::Instant; -use smoltcp::wire::{ - DnsQueryType, EthernetAddress, HardwareAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address, -}; +use smoltcp::wire::{DnsQueryType, EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; use std::os::unix::io::AsRawFd; fn main() { @@ -32,39 +30,41 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let name = &matches.free[0]; - let neighbor_cache = NeighborCache::new(); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) + .unwrap(); + iface + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) + .unwrap(); + + // Create sockets let servers = &[ Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into(), ]; let dns_socket = dns::Socket::new(servers, vec![]); - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) - .unwrap(); - ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); - let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes = Routes::new(); - routes.add_default_ipv4_route(default_v4_gw).unwrap(); - routes.add_default_ipv6_route(default_v6_gw).unwrap(); - - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(HardwareAddress::Ethernet(ethernet_addr)) - .neighbor_cache(neighbor_cache); - } - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let dns_handle = sockets.add(dns_socket); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index a54ca8228..1e5a21864 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -5,7 +5,7 @@ use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; use url::Url; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; @@ -28,37 +28,38 @@ fn main() { let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let url = Url::parse(&matches.free[1]).expect("invalid url format"); - let neighbor_cache = NeighborCache::new(); - - let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); - let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); - let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) .unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + iface + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) .unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); - let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes = Routes::new(); - routes.add_default_ipv4_route(default_v4_gw).unwrap(); - routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); - } - let mut iface = builder.finalize(&mut device); + // Create sockets + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1024]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let mut sockets = SocketSet::new(vec![]); let tcp_handle = sockets.add(tcp_socket); diff --git a/examples/loopback.rs b/examples/loopback.rs index bc4a24bcf..1a4dee883 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -9,7 +9,7 @@ mod utils; use core::str; use log::{debug, error, info}; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{Loopback, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; @@ -82,16 +82,18 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true) }; - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - let mut iface = InterfaceBuilder::new() - .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(NeighborCache::new()) - .ip_addrs(ip_addrs) - .finalize(&mut device); + // Create interface + let mut config = Config::new(); + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + }); + + // Create sockets let server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but // on embedded systems that smoltcp targets it is far better to allocate the data diff --git a/examples/multicast.rs b/examples/multicast.rs index 886b92e6e..639fa432f 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -2,13 +2,13 @@ mod utils; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; -use smoltcp::phy::wait as phy_wait; +use smoltcp::iface::{Config, Interface, SocketSet}; +use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{raw, udp}; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address, - Ipv4Packet, + Ipv4Packet, Ipv6Address, }; const MDNS_PORT: u16 = 5353; @@ -26,26 +26,36 @@ fn main() { let fd = device.as_raw_fd(); let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(); - let local_addr = Ipv4Address::new(192, 168, 69, 2); - - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs.push(ip_addr).unwrap(); - let mut iface = InterfaceBuilder::new() - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(&mut device); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } - let now = Instant::now(); - // Join a multicast group to receive mDNS traffic + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) + .unwrap(); iface - .join_multicast_group(&mut device, Ipv4Address::from_bytes(&MDNS_GROUP), now) + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) .unwrap(); + // Create sockets let mut sockets = SocketSet::new(vec![]); // Must fit at least one IGMP packet @@ -67,6 +77,15 @@ fn main() { let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let udp_handle = sockets.add(udp_socket); + // Join a multicast group to receive mDNS traffic + iface + .join_multicast_group( + &mut device, + Ipv4Address::from_bytes(&MDNS_GROUP), + Instant::now(), + ) + .unwrap(); + loop { let timestamp = Instant::now(); iface.poll(timestamp, &mut device, &mut sockets); diff --git a/examples/ping.rs b/examples/ping.rs index ce709713d..beac56257 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -1,13 +1,13 @@ mod utils; use byteorder::{ByteOrder, NetworkEndian}; -use smoltcp::iface::SocketSet; +use smoltcp::iface::{Interface, SocketSet}; use std::cmp; use std::collections::HashMap; use std::os::unix::io::AsRawFd; use std::str::FromStr; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes}; +use smoltcp::iface::Config; use smoltcp::phy::wait as phy_wait; use smoltcp::phy::Device; use smoltcp::socket::icmp; @@ -88,7 +88,7 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); let device_caps = device.capabilities(); - let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); + let remote_addr = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let count = matches .opt_str("count") .map(|s| usize::from_str(&s).unwrap()) @@ -104,39 +104,38 @@ fn main() { .unwrap_or(5), ); - let neighbor_cache = NeighborCache::new(); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } - let remote_addr = address; + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) + .unwrap(); + iface + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) + .unwrap(); + // Create sockets let icmp_rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]); let icmp_tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 256]); let icmp_socket = icmp::Socket::new(icmp_rx_buffer, icmp_tx_buffer); - - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); - let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) - .unwrap(); - ip_addrs.push(IpCidr::new(src_ipv6, 64)).unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); - let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); - let mut routes = Routes::new(); - routes.add_default_ipv4_route(default_v4_gw).unwrap(); - routes.add_default_ipv6_route(default_v6_gw).unwrap(); - - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); - } - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let icmp_handle = sockets.add(icmp_socket); @@ -185,7 +184,7 @@ fn main() { remote_addr ); icmp_repr.emit( - &src_ipv6, + &iface.ipv6_addr().unwrap().into_address(), &remote_addr, &mut icmp_packet, &device_caps.checksum, @@ -219,7 +218,7 @@ fn main() { let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); let icmp_repr = Icmpv6Repr::parse( &remote_addr, - &src_ipv6, + &iface.ipv6_addr().unwrap().into_address(), &icmp_packet, &device_caps.checksum, ) diff --git a/examples/server.rs b/examples/server.rs index 001a2074d..f2bbc8032 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -4,11 +4,11 @@ use log::debug; use std::fmt::Write; use std::os::unix::io::AsRawFd; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::{tcp, udp}; use smoltcp::time::{Duration, Instant}; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; fn main() { utils::setup_logging(""); @@ -23,8 +23,35 @@ fn main() { let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + if device.capabilities().medium == Medium::Ethernet { + config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + } + + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::new(192, 168, 69, 100)) + .unwrap(); + iface + .routes_mut() + .add_default_ipv6_route(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100)) + .unwrap(); + // Create sockets let udp_rx_buffer = udp::PacketBuffer::new( vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], vec![0; 65535], @@ -51,35 +78,6 @@ fn main() { let tcp4_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp4_socket = tcp::Socket::new(tcp4_rx_buffer, tcp4_tx_buffer); - let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) - .unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - - let medium = device.capabilities().medium; - let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - - builder = builder.random_seed( - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(), - ); - - if medium == Medium::Ethernet { - builder = builder - .hardware_addr(ethernet_addr.into()) - .neighbor_cache(neighbor_cache); - } - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); let tcp1_handle = sockets.add(tcp1_socket); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index c0cd77348..964a8562d 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -46,12 +46,12 @@ use log::debug; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; use smoltcp::time::Instant; -use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp::wire::{Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; fn main() { utils::setup_logging(""); @@ -62,13 +62,28 @@ fn main() { let mut matches = utils::parse_options(&opts, free); let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap(); - let fd = device.as_raw_fd(); let mut device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); - let neighbor_cache = NeighborCache::new(); - + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + config.hardware_addr = + Some(Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into()); + config.pan_id = Some(Ieee802154Pan(0xbeef)); + + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )) + .unwrap(); + }); + + // Create sockets let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1280]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); @@ -77,26 +92,6 @@ fn main() { let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); - let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ - 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - ]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new( - IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), - 64, - )) - .unwrap(); - - let mut builder = InterfaceBuilder::new() - .ip_addrs(ip_addrs) - .pan_id(Ieee802154Pan(0xbeef)); - builder = builder - .hardware_addr(ieee802154_addr.into()) - .neighbor_cache(neighbor_cache); - - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); let tcp_handle = sockets.add(tcp_socket); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 71daf5653..8b9cf842d 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -46,10 +46,10 @@ mod utils; use std::os::unix::io::AsRawFd; use std::str; -use smoltcp::iface::{InterfaceBuilder, NeighborCache, SocketSet}; +use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; use smoltcp::socket::tcp; -use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp::wire::{Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; //For benchmark use smoltcp::time::{Duration, Instant}; @@ -146,7 +146,22 @@ fn main() { _ => panic!("invalid mode"), }; - let neighbor_cache = NeighborCache::new(); + // Create interface + let mut config = Config::new(); + config.random_seed = rand::random(); + config.hardware_addr = + Some(Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into()); + config.pan_id = Some(Ieee802154Pan(0xbeef)); + + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), + 64, + )) + .unwrap(); + }); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); @@ -156,25 +171,6 @@ fn main() { let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]); let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); - let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([ - 0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - ]); - let mut ip_addrs = heapless::Vec::::new(); - ip_addrs - .push(IpCidr::new( - IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242), - 64, - )) - .unwrap(); - - let mut builder = InterfaceBuilder::new() - .ip_addrs(ip_addrs) - .pan_id(Ieee802154Pan(0xbeef)); - builder = builder - .hardware_addr(ieee802154_addr.into()) - .neighbor_cache(neighbor_cache); - let mut iface = builder.finalize(&mut device); - let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); let tcp2_handle = sockets.add(tcp2_socket); diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 25ebfc854..e9e31c8b8 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -324,7 +324,7 @@ impl InterfaceInner { } else if self.is_broadcast_v4(ipv4_repr.dst_addr) { // Only reply to broadcasts for echo replies and not other ICMP messages match icmp_repr { - Icmpv4Repr::EchoReply { .. } => match self.ipv4_address() { + Icmpv4Repr::EchoReply { .. } => match self.ipv4_addr() { Some(src_addr) => { let ipv4_reply_repr = Ipv4Repr { src_addr, @@ -427,7 +427,7 @@ impl InterfaceInner { version: IgmpVersion, group_addr: Ipv4Address, ) -> Option> { - let iface_addr = self.ipv4_address()?; + let iface_addr = self.ipv4_addr()?; let igmp_repr = IgmpRepr::MembershipReport { group_addr, version, @@ -452,7 +452,7 @@ impl InterfaceInner { &self, group_addr: Ipv4Address, ) -> Option> { - self.ipv4_address().map(|iface_addr| { + self.ipv4_addr().map(|iface_addr| { let igmp_repr = IgmpRepr::LeaveGroup { group_addr }; IpPacket::Igmp(( Ipv4Repr { diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 95e69012b..05d766d75 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -24,10 +24,10 @@ use heapless::{LinearMap, Vec}; #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] use super::fragmentation::PacketAssemblerSet; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache}; use super::socket_set::SocketSet; use crate::iface::Routes; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] -use crate::iface::{NeighborAnswer, NeighborCache}; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::rand::Rand; #[cfg(feature = "socket-dns")] @@ -284,353 +284,46 @@ pub struct InterfaceInner { igmp_report_state: IgmpReportState, } -/// A builder structure used for creating a network interface. -pub struct InterfaceBuilder { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: Option, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option, - #[cfg(feature = "medium-ieee802154")] - pan_id: Option, - ip_addrs: Vec, - #[cfg(feature = "proto-ipv4")] - any_ip: bool, - routes: Routes, - /// Does not share storage with `ipv6_multicast_groups` to avoid IPv6 size overhead. - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: LinearMap, - random_seed: u64, - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_buffer_timeout: Duration, - - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: Vec, -} - -impl InterfaceBuilder { - /// Create a builder used for creating a network interface using the - /// given device and address. - #[cfg_attr( - all(feature = "medium-ethernet", not(feature = "proto-sixlowpan")), - doc = r##" -# Examples - -``` -# use std::collections::BTreeMap; -#[cfg(feature = "proto-ipv4-fragmentation")] -use smoltcp::iface::ReassemblyBuffer; -use smoltcp::iface::{InterfaceBuilder, NeighborCache}; -# use smoltcp::phy::{Loopback, Medium}; -use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; - -let mut device = // ... -# Loopback::new(Medium::Ethernet); -let hw_addr = // ... -# EthernetAddress::default(); -let neighbor_cache = // ... -# NeighborCache::new(); -let ip_addrs = // ... -# heapless::Vec::::new(); -let builder = InterfaceBuilder::new() - .hardware_addr(hw_addr.into()) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs); - -let iface = builder.finalize(&mut device); -``` - "## - )] - #[allow(clippy::new_without_default)] - pub fn new() -> Self { - InterfaceBuilder { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: None, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: None, - - #[cfg(feature = "medium-ieee802154")] - pan_id: None, - - ip_addrs: Vec::new(), - #[cfg(feature = "proto-ipv4")] - any_ip: false, - routes: Routes::new(), - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: LinearMap::new(), - random_seed: 0, - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60), - - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: Vec::new(), - } - } - - /// Set the random seed for this interface. +/// Configuration structure used for creating a network interface. +#[non_exhaustive] +pub struct Config { + /// Random seed. /// /// It is strongly recommended that the random seed is different on each boot, /// to avoid problems with TCP port/sequence collisions. /// /// The seed doesn't have to be cryptographically secure. - pub fn random_seed(mut self, random_seed: u64) -> Self { - self.random_seed = random_seed; - self - } + pub random_seed: u64, - /// Set the Hardware address the interface will use. See also - /// [hardware_addr]. + /// Set the Hardware address the interface will use. /// /// # Panics - /// This function panics if the address is not unicast. - /// - /// [hardware_addr]: struct.Interface.html#method.hardware_addr + /// Creating the interface panics if the address is not unicast. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn hardware_addr(mut self, addr: HardwareAddress) -> Self { - InterfaceInner::check_hardware_addr(&addr); - self.hardware_addr = Some(addr); - self - } + pub hardware_addr: Option, /// Set the IEEE802.15.4 PAN ID the interface will use. /// /// **NOTE**: we use the same PAN ID for destination and source. #[cfg(feature = "medium-ieee802154")] - pub fn pan_id(mut self, pan_id: Ieee802154Pan) -> Self { - self.pan_id = Some(pan_id); - self - } - - /// Set the IP addresses the interface will use. See also - /// [ip_addrs]. - /// - /// # Panics - /// This function panics if any of the addresses are not unicast. - /// - /// [ip_addrs]: struct.Interface.html#method.ip_addrs - pub fn ip_addrs(mut self, ip_addrs: T) -> Self - where - T: Into>, - { - let ip_addrs = ip_addrs.into(); - InterfaceInner::check_ip_addrs(&ip_addrs); - self.ip_addrs = ip_addrs; - self - } - - /// Enable or disable the AnyIP capability, allowing packets to be received - /// locally on IPv4 addresses other than the interface's configured [ip_addrs]. - /// When AnyIP is enabled and a route prefix in [routes] specifies one of - /// the interface's [ip_addrs] as its gateway, the interface will accept - /// packets addressed to that prefix. - /// - /// # IPv6 - /// - /// This option is not available or required for IPv6 as packets sent to - /// the interface are not filtered by IPv6 address. - /// - /// [routes]: struct.Interface.html#method.routes - /// [ip_addrs]: struct.Interface.html#method.ip_addrs - #[cfg(feature = "proto-ipv4")] - pub fn any_ip(mut self, enabled: bool) -> Self { - self.any_ip = enabled; - self - } - - /// Set the IP routes the interface will use. See also - /// [routes]. - /// - /// [routes]: struct.Interface.html#method.routes - pub fn routes(mut self, routes: T) -> Self - where - T: Into, - { - self.routes = routes.into(); - self - } - - /// Provide storage for multicast groups. - /// - /// Join multicast groups by calling [`join_multicast_group()`] on an `Interface`. - /// Using [`join_multicast_group()`] will send initial membership reports. - /// - /// A previously destroyed interface can be recreated by reusing the multicast group - /// storage, i.e. providing a non-empty storage to `ipv4_multicast_groups()`. - /// Note that this way initial membership reports are **not** sent. - /// - /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group - #[cfg(feature = "proto-igmp")] - pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self - where - T: Into>, - { - self.ipv4_multicast_groups = ipv4_multicast_groups.into(); - self - } - - /// Set the Neighbor Cache the interface will use. - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache) -> Self { - self.neighbor_cache = Some(neighbor_cache); - self - } - - /// Set the address contexts the interface will use. - #[cfg(feature = "proto-sixlowpan")] - pub fn sixlowpan_address_context( - mut self, - sixlowpan_address_context: Vec, - ) -> Self { - self.sixlowpan_address_context = sixlowpan_address_context; - self - } - - /// Set the timeout value the 6LoWPAN reassembly buffer will use. - #[cfg(feature = "proto-sixlowpan-fragmentation")] - pub fn sixlowpan_reassembly_buffer_timeout(mut self, timeout: Duration) -> Self { - if timeout > Duration::from_secs(60) { - net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); - } - self.sixlowpan_reassembly_buffer_timeout = timeout; - self - } - - /// Create a network interface using the previously provided configuration. - /// - /// # Panics - /// If a required option is not provided, this function will panic. Required - /// options are: - /// - /// - [ethernet_addr] - /// - [neighbor_cache] - /// - /// [ethernet_addr]: #method.ethernet_addr - /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self, device: &mut D) -> Interface - where - D: Device + ?Sized, - { - let caps = device.capabilities(); + pub pan_id: Option, +} - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let (hardware_addr, neighbor_cache) = match caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => ( - Some( - self.hardware_addr - .expect("hardware_addr required option was not set"), - ), - Some( - self.neighbor_cache - .expect("neighbor_cache required option was not set"), - ), - ), - #[cfg(feature = "medium-ip")] - Medium::Ip => { - assert!( - self.hardware_addr.is_none(), - "hardware_addr is set, but device medium is IP" - ); - assert!( - self.neighbor_cache.is_none(), - "neighbor_cache is set, but device medium is IP" - ); - (None, None) - } +impl Config { + pub fn new() -> Self { + Config { + random_seed: 0, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr: None, #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => ( - Some( - self.hardware_addr - .expect("hardware_addr required option was not set"), - ), - Some( - self.neighbor_cache - .expect("neighbor_cache required option was not set"), - ), - ), - }; - - let mut rand = Rand::new(self.random_seed); - - #[cfg(feature = "medium-ieee802154")] - let mut sequence_no; - #[cfg(feature = "medium-ieee802154")] - loop { - sequence_no = (rand.rand_u32() & 0xff) as u8; - if sequence_no != 0 { - break; - } - } - - #[cfg(feature = "proto-sixlowpan")] - let mut tag; - - #[cfg(feature = "proto-sixlowpan")] - loop { - tag = rand.rand_u16(); - if tag != 0 { - break; - } - } - - #[cfg(feature = "proto-ipv4")] - let mut ipv4_id; - - #[cfg(feature = "proto-ipv4")] - loop { - ipv4_id = rand.rand_u16(); - if ipv4_id != 0 { - break; - } + pan_id: None, } + } +} - Interface { - fragments: FragmentsBuffer { - #[cfg(feature = "proto-sixlowpan")] - decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN], - - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout, - }, - out_packets: OutPackets { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new(), - }, - inner: InterfaceInner { - now: Instant::from_secs(0), - caps, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr, - ip_addrs: self.ip_addrs, - #[cfg(feature = "proto-ipv4")] - any_ip: self.any_ip, - routes: self.routes, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: self.ipv4_multicast_groups, - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState::Inactive, - #[cfg(feature = "medium-ieee802154")] - sequence_no, - #[cfg(feature = "medium-ieee802154")] - pan_id: self.pan_id, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - tag, - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_id, - #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: Vec::new(), - rand, - }, - } +impl Default for Config { + fn default() -> Self { + Self::new() } } @@ -785,6 +478,129 @@ enum IgmpReportState { } impl Interface { + /// Create a network interface using the previously provided configuration. + /// + /// # Panics + /// If a required option is not provided, this function will panic. Required + /// options are: + /// + /// - [ethernet_addr] + /// - [neighbor_cache] + /// + /// [ethernet_addr]: #method.ethernet_addr + /// [neighbor_cache]: #method.neighbor_cache + pub fn new(config: Config, device: &mut D) -> Self + where + D: Device + ?Sized, + { + let caps = device.capabilities(); + + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + let hardware_addr = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => Some( + config + .hardware_addr + .expect("hardware_addr required option was not set"), + ), + #[cfg(feature = "medium-ip")] + Medium::Ip => { + assert!( + config.hardware_addr.is_none(), + "hardware_addr is set, but device medium is IP" + ); + None + } + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => Some( + config + .hardware_addr + .expect("hardware_addr required option was not set"), + ), + }; + + let mut rand = Rand::new(config.random_seed); + + #[cfg(feature = "medium-ieee802154")] + let mut sequence_no; + #[cfg(feature = "medium-ieee802154")] + loop { + sequence_no = (rand.rand_u32() & 0xff) as u8; + if sequence_no != 0 { + break; + } + } + + #[cfg(feature = "proto-sixlowpan")] + let mut tag; + + #[cfg(feature = "proto-sixlowpan")] + loop { + tag = rand.rand_u16(); + if tag != 0 { + break; + } + } + + #[cfg(feature = "proto-ipv4")] + let mut ipv4_id; + + #[cfg(feature = "proto-ipv4")] + loop { + ipv4_id = rand.rand_u16(); + if ipv4_id != 0 { + break; + } + } + + Interface { + fragments: FragmentsBuffer { + #[cfg(feature = "proto-sixlowpan")] + decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN], + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_fragments: PacketAssemblerSet::new(), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments: PacketAssemblerSet::new(), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_fragments_cache_timeout: Duration::from_secs(60), + }, + out_packets: OutPackets { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket::new(), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan_out_packet: SixlowpanOutPacket::new(), + }, + inner: InterfaceInner { + now: Instant::from_secs(0), + caps, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + hardware_addr, + ip_addrs: Vec::new(), + #[cfg(feature = "proto-ipv4")] + any_ip: false, + routes: Routes::new(), + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + neighbor_cache: Some(NeighborCache::new()), + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: LinearMap::new(), + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + #[cfg(feature = "medium-ieee802154")] + sequence_no, + #[cfg(feature = "medium-ieee802154")] + pan_id: config.pan_id, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + tag, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id, + #[cfg(feature = "proto-sixlowpan")] + sixlowpan_address_context: Vec::new(), + rand, + }, + } + } + /// Get the socket context. /// /// The context is needed for some socket methods. @@ -842,13 +658,13 @@ impl Interface { /// Get the first IPv4 address if present. #[cfg(feature = "proto-ipv4")] pub fn ipv4_addr(&self) -> Option { - self.ip_addrs() - .iter() - .find_map(|cidr| match cidr.address() { - IpAddress::Ipv4(addr) => Some(addr), - #[allow(unreachable_patterns)] - _ => None, - }) + self.inner.ipv4_addr() + } + + /// Get the first IPv6 address if present. + #[cfg(feature = "proto-ipv6")] + pub fn ipv6_addr(&self) -> Option { + self.inner.ipv6_addr() } /// Update the IP addresses of the interface. @@ -866,12 +682,6 @@ impl Interface { self.inner.has_ip_addr(addr) } - /// Get the first IPv4 address of the interface. - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_address(&self) -> Option { - self.inner.ipv4_address() - } - pub fn routes(&self) -> &Routes { &self.inner.routes } @@ -880,6 +690,66 @@ impl Interface { &mut self.inner.routes } + /// Enable or disable the AnyIP capability. + /// + /// AnyIP allowins packets to be received + /// locally on IPv4 addresses other than the interface's configured [ip_addrs]. + /// When AnyIP is enabled and a route prefix in [`routes`](Self::routes) specifies one of + /// the interface's [`ip_addrs`](Self::ip_addrs) as its gateway, the interface will accept + /// packets addressed to that prefix. + /// + /// # IPv6 + /// + /// This option is not available or required for IPv6 as packets sent to + /// the interface are not filtered by IPv6 address. + #[cfg(feature = "proto-ipv4")] + pub fn set_any_ip(&mut self, any_ip: bool) { + self.inner.any_ip = any_ip; + } + + /// Get whether AnyIP is enabled. + /// + /// See [`set_any_ip`](Self::set_any_ip) for details on AnyIP + #[cfg(feature = "proto-ipv4")] + pub fn any_ip(&self) -> bool { + self.inner.any_ip + } + + /// Get the 6LoWPAN address contexts. + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_address_context( + &self, + ) -> &Vec { + &self.inner.sixlowpan_address_context + } + + /// Get a mutable reference to the 6LoWPAN address contexts. + #[cfg(feature = "proto-sixlowpan")] + pub fn sixlowpan_address_context_mut( + &mut self, + ) -> &mut Vec { + &mut self.inner.sixlowpan_address_context + } + + /// Get the packet reassembly timeout. + /// + /// Currently used only for 6LoWPAN, will be used for IPv4 in the future as well. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn reassembly_timeout(&self) -> Duration { + self.fragments.sixlowpan_fragments_cache_timeout + } + + /// Set the packet reassembly timeout. + /// + /// Currently used only for 6LoWPAN, will be used for IPv4 in the future as well. + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub fn set_reassembly_timeout(&mut self, timeout: Duration) { + if timeout > Duration::from_secs(60) { + net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); + } + self.fragments.sixlowpan_fragments_cache_timeout = timeout; + } + /// Transmit packets queued in the given sockets, and receive packets queued /// in the device. /// @@ -1441,11 +1311,21 @@ impl InterfaceInner { /// Get the first IPv4 address of the interface. #[cfg(feature = "proto-ipv4")] - pub fn ipv4_address(&self) -> Option { + pub fn ipv4_addr(&self) -> Option { self.ip_addrs.iter().find_map(|addr| match *addr { IpCidr::Ipv4(cidr) => Some(cidr.address()), - #[cfg(feature = "proto-ipv6")] - IpCidr::Ipv6(_) => None, + #[allow(unreachable_patterns)] + _ => None, + }) + } + + /// Get the first IPv6 address if present. + #[cfg(feature = "proto-ipv6")] + pub fn ipv6_addr(&self) -> Option { + self.ip_addrs.iter().find_map(|addr| match *addr { + IpCidr::Ipv6(cidr) => Some(cidr.address()), + #[allow(unreachable_patterns)] + _ => None, }) } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index c3dcf13be..01c7dba27 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -4,8 +4,6 @@ use std::vec::Vec; use super::*; use crate::iface::Interface; -#[cfg(feature = "medium-ethernet")] -use crate::iface::NeighborCache; use crate::phy::{ChecksumCapabilities, Loopback}; #[cfg(feature = "proto-igmp")] use crate::time::Instant; @@ -40,23 +38,23 @@ fn create<'a>(medium: Medium) -> (Interface, SocketSet<'a>, Loopback) { fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); - let mut ip_addrs = heapless::Vec::::new(); - #[cfg(feature = "proto-ipv4")] - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); - - let iface = iface_builder.finalize(&mut device); + let mut config = Config::new(); + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv4")] + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); (iface, SocketSet::new(vec![]), device) } @@ -65,26 +63,24 @@ fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); - let mut ip_addrs = heapless::Vec::::new(); - #[cfg(feature = "proto-ipv4")] - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - - let iface_builder = InterfaceBuilder::new() - .hardware_addr(EthernetAddress::default().into()) - .neighbor_cache(NeighborCache::new()) - .ip_addrs(ip_addrs); - let iface = iface_builder.finalize(&mut device); + let mut config = Config::new(); + config.hardware_addr = Some(EthernetAddress::default().into()); + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv4")] + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); (iface, SocketSet::new(vec![]), device) } @@ -93,22 +89,20 @@ fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { fn create_ieee802154<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); - let mut ip_addrs = heapless::Vec::::new(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - - let iface_builder = InterfaceBuilder::new() - .hardware_addr(Ieee802154Address::default().into()) - .neighbor_cache(NeighborCache::new()) - .ip_addrs(ip_addrs); - let iface = iface_builder.finalize(&mut device); + let mut config = Config::new(); + config.hardware_addr = Some(Ieee802154Address::default().into()); + let mut iface = Interface::new(config, &mut device); + iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); + #[cfg(feature = "proto-ipv6")] + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); (iface, SocketSet::new(vec![]), device) } @@ -141,9 +135,10 @@ impl TxToken for MockTxToken { #[test] #[should_panic(expected = "hardware_addr required option was not set")] #[cfg(all(feature = "medium-ethernet"))] -fn test_builder_initialization_panic() { +fn test_new_panic() { let mut device = Loopback::new(Medium::Ethernet); - InterfaceBuilder::new().finalize(&mut device); + let config = Config::new(); + Interface::new(config, &mut device); } #[test] @@ -527,7 +522,7 @@ fn test_handle_ipv4_broadcast() { let (mut iface, mut sockets, _device) = create(MEDIUM); - let our_ipv4_addr = iface.ipv4_address().unwrap(); + let our_ipv4_addr = iface.ipv4_addr().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); // ICMPv4 echo request diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 96d01d46d..e5cb832b5 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -13,16 +13,6 @@ mod route; mod socket_meta; mod socket_set; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] -pub(crate) use self::neighbor::Answer as NeighborAnswer; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] -pub use self::neighbor::Cache as NeighborCache; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] -pub use self::neighbor::Neighbor; +pub use self::interface::{Config, Interface, InterfaceInner as Context}; pub use self::route::{Route, RouteTableFull, Routes}; pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; - -#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] -pub use self::fragmentation::{PacketAssembler, PacketAssemblerSet as ReassemblyBuffer}; - -pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8c9700412..4e8c53583 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -731,15 +731,15 @@ impl<'a> Socket<'a> { /// The local port must be provided explicitly. Assuming `fn get_ephemeral_port() -> u16` /// allocates a port between 49152 and 65535, a connection may be established as follows: /// - /// ```rust + /// ```no_run /// # #[cfg(all( /// # feature = "medium-ethernet", /// # feature = "proto-ipv4", /// # ))] /// # { /// # use smoltcp::socket::tcp::{Socket, SocketBuffer}; - /// # use smoltcp::iface::{InterfaceBuilder, NeighborCache}; - /// # use smoltcp::wire::{HardwareAddress, EthernetAddress, IpAddress, IpCidr}; + /// # use smoltcp::iface::Interface; + /// # use smoltcp::wire::IpAddress; /// # /// # fn get_ephemeral_port() -> u16 { /// # 49152 @@ -750,17 +750,7 @@ impl<'a> Socket<'a> { /// # SocketBuffer::new(vec![0; 1200]) /// # ); /// # - /// # let mut ip_addrs = heapless::Vec::::new(); - /// # ip_addrs - /// # .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) - /// # .unwrap(); - /// # - /// # let mut device =smoltcp::phy::Loopback::new(smoltcp::phy::Medium::Ethernet); - /// # let mut iface = InterfaceBuilder::new() - /// # .hardware_addr(HardwareAddress::Ethernet(EthernetAddress::default())) - /// # .neighbor_cache(NeighborCache::new()) - /// # .ip_addrs(ip_addrs) - /// # .finalize(&mut device); + /// # let mut iface: Interface = todo!(); /// # /// socket.connect( /// iface.context(), From 9a28858b68bb14014395fe9f4f41a4dd304399dc Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 28 Jan 2023 19:36:23 +0100 Subject: [PATCH 497/566] Instant is microseconds, not milliseconds --- src/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time.rs b/src/time.rs index 961e4d7e7..99f8308b6 100644 --- a/src/time.rs +++ b/src/time.rs @@ -15,7 +15,7 @@ use core::{fmt, ops}; /// A representation of an absolute time value. /// /// The `Instant` type is a wrapper around a `i64` value that -/// represents a number of milliseconds, monotonically increasing +/// represents a number of microseconds, monotonically increasing /// since an arbitrary moment in time, such as system startup. /// /// * A value of `0` is inherently arbitrary. From 3a9d017354acde5c139496243dffec4e4fb05fdf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Feb 2023 23:57:42 +0100 Subject: [PATCH 498/566] Remove unneeded ref patterns and destructures. This brings the code to a more modern Rust style. - Dstructuring is not so necessary nowadays, with the borrow checker being smarter, especially around partial captures in closures. - "ref" is barely needed anymore, with [match ergonomics](https://rust-lang.github.io/rfcs/2005-match-ergonomics.html). --- fuzz/fuzz_targets/dhcp_header.rs | 2 +- fuzz/fuzz_targets/ieee802154_header.rs | 2 +- src/iface/interface/ipv4.rs | 41 +++++--------- src/iface/interface/mod.rs | 60 +++++++------------- src/iface/interface/sixlowpan.rs | 76 +++++++++----------------- src/iface/socket_set.rs | 4 +- src/phy/fuzz_injector.rs | 20 ++----- src/phy/raw_socket.rs | 2 +- src/phy/tracer.rs | 23 +++----- src/phy/tuntap_interface.rs | 2 +- src/socket/dns.rs | 4 +- src/socket/icmp.rs | 4 +- src/socket/tcp.rs | 14 ++--- src/storage/packet_buffer.rs | 25 ++++----- src/wire/ieee802154.rs | 20 +++---- src/wire/ip.rs | 14 ++--- 16 files changed, 114 insertions(+), 199 deletions(-) diff --git a/fuzz/fuzz_targets/dhcp_header.rs b/fuzz/fuzz_targets/dhcp_header.rs index ea2c64dce..f56efd091 100644 --- a/fuzz/fuzz_targets/dhcp_header.rs +++ b/fuzz/fuzz_targets/dhcp_header.rs @@ -4,7 +4,7 @@ use smoltcp::wire::{DhcpPacket, DhcpRepr}; fuzz_target!(|data: &[u8]| { let _ = match DhcpPacket::new_checked(data) { - Ok(ref packet) => match DhcpRepr::parse(packet) { + Ok(packet) => match DhcpRepr::parse(packet) { Ok(dhcp_repr) => { let mut dhcp_payload = vec![0; dhcp_repr.buffer_len()]; match DhcpPacket::new_checked(&mut dhcp_payload[..]) { diff --git a/fuzz/fuzz_targets/ieee802154_header.rs b/fuzz/fuzz_targets/ieee802154_header.rs index c50839122..88f52f63e 100644 --- a/fuzz/fuzz_targets/ieee802154_header.rs +++ b/fuzz/fuzz_targets/ieee802154_header.rs @@ -3,7 +3,7 @@ use libfuzzer_sys::fuzz_target; use smoltcp::wire::{Ieee802154Frame, Ieee802154Repr}; fuzz_target!(|data: &[u8]| { - if let Ok(ref frame) = Ieee802154Frame::new_checked(data) { + if let Ok(frame) = Ieee802154Frame::new_checked(data) { if let Ok(repr) = Ieee802154Repr::parse(frame) { // The buffer len returns only the length required for emitting the header // and does not take into account the length of the payload. diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index e9e31c8b8..79247f0e1 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -348,28 +348,17 @@ impl InterfaceInner { pub(super) fn dispatch_ipv4_out_packet( &mut self, tx_token: Tx, - out_packet: &mut Ipv4OutPacket, + pkt: &mut Ipv4OutPacket, ) { - let Ipv4OutPacket { - buffer, - packet_len, - sent_bytes, - repr, - dst_hardware_addr, - frag_offset, - ident, - .. - } = out_packet; - let caps = self.caps.clone(); let mtu_max = self.ip_mtu(); - let ip_len = (*packet_len - *sent_bytes + repr.buffer_len()).min(mtu_max); - let payload_len = ip_len - repr.buffer_len(); + let ip_len = (pkt.packet_len - pkt.sent_bytes + pkt.repr.buffer_len()).min(mtu_max); + let payload_len = ip_len - pkt.repr.buffer_len(); - let more_frags = (*packet_len - *sent_bytes) != payload_len; - repr.payload_len = payload_len; - *sent_bytes += payload_len; + let more_frags = (pkt.packet_len - pkt.sent_bytes) != payload_len; + pkt.repr.payload_len = payload_len; + pkt.sent_bytes += payload_len; let mut tx_len = ip_len; #[cfg(feature = "medium-ethernet")] @@ -384,7 +373,7 @@ impl InterfaceInner { let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); frame.set_src_addr(src_addr); - frame.set_dst_addr(*dst_hardware_addr); + frame.set_dst_addr(pkt.dst_hardware_addr); match repr.version() { #[cfg(feature = "proto-ipv4")] @@ -397,27 +386,27 @@ impl InterfaceInner { tx_token.consume(tx_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer); + emit_ethernet(&IpRepr::Ipv4(pkt.repr), tx_buffer); tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; } - let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..repr.buffer_len()]); - repr.emit(&mut packet, &caps.checksum); - packet.set_ident(*ident); + let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..pkt.repr.buffer_len()]); + pkt.repr.emit(&mut packet, &caps.checksum); + packet.set_ident(pkt.ident); packet.set_more_frags(more_frags); packet.set_dont_frag(false); - packet.set_frag_offset(*frag_offset); + packet.set_frag_offset(pkt.frag_offset); if caps.checksum.ipv4.tx() { packet.fill_checksum(); } - tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( - &buffer[*frag_offset as usize + repr.buffer_len()..][..payload_len], + tx_buffer[pkt.repr.buffer_len()..][..payload_len].copy_from_slice( + &pkt.buffer[pkt.frag_offset as usize + pkt.repr.buffer_len()..][..payload_len], ); // Update the frag offset for the next fragment. - *frag_offset += payload_len as u16; + pkt.frag_offset += payload_len as u16; }) } diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 05d766d75..d30e6abc1 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -997,10 +997,9 @@ impl Interface { respond(inner, IpPacket::Dhcpv4(response)) }), #[cfg(feature = "socket-dns")] - Socket::Dns(ref mut socket) => socket - .dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) - }), + Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, response| { + respond(inner, IpPacket::Udp(response)) + }), }; match result { @@ -1040,13 +1039,8 @@ impl Interface { return false; } - let Ipv4OutPacket { - packet_len, - sent_bytes, - .. - } = &self.out_packets.ipv4_out_packet; - - if *packet_len > *sent_bytes { + let pkt = &self.out_packets.ipv4_out_packet; + if pkt.packet_len > pkt.sent_bytes { if let Some(tx_token) = device.transmit(self.inner.now) { self.inner .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet); @@ -1075,13 +1069,8 @@ impl Interface { return false; } - let SixlowpanOutPacket { - packet_len, - sent_bytes, - .. - } = &self.out_packets.sixlowpan_out_packet; - - if *packet_len > *sent_bytes { + let pkt = &self.out_packets.sixlowpan_out_packet; + if pkt.packet_len > pkt.sent_bytes { if let Some(tx_token) = device.transmit(self.inner.now) { self.inner.dispatch_ieee802154_out_packet( tx_token, @@ -1735,7 +1724,7 @@ impl InterfaceInner { packet: IpPacket, _out_packet: Option<&mut OutPackets>, ) -> Result<(), DispatchError> { - let mut ip_repr = packet.ip_repr(); + let ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); // Dispatch IEEE802.15.4: @@ -1814,23 +1803,14 @@ impl InterfaceInner { match ip_repr { #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(ref mut repr) => { + IpRepr::Ipv4(mut repr) => { // If we have an IPv4 packet, then we need to check if we need to fragment it. if total_ip_len > self.caps.max_transmission_unit { #[cfg(feature = "proto-ipv4-fragmentation")] { net_debug!("start fragmentation"); - let Ipv4OutPacket { - buffer, - packet_len, - sent_bytes, - repr: out_packet_repr, - frag_offset, - ident, - #[cfg(feature = "medium-ethernet")] - dst_hardware_addr: dst_address, - } = &mut _out_packet.unwrap().ipv4_out_packet; + let pkt = &mut _out_packet.unwrap().ipv4_out_packet; // Calculate how much we will send now (including the Ethernet header). let tx_len = self.caps.max_transmission_unit; @@ -1838,7 +1818,7 @@ impl InterfaceInner { let ip_header_len = repr.buffer_len(); let first_frag_ip_len = self.caps.ip_mtu(); - if buffer.len() < first_frag_ip_len { + if pkt.buffer.len() < first_frag_ip_len { net_debug!( "Fragmentation buffer is too small, at least {} needed. Dropping", first_frag_ip_len @@ -1848,26 +1828,26 @@ impl InterfaceInner { #[cfg(feature = "medium-ethernet")] { - *dst_address = dst_hardware_addr; + pkt.dst_hardware_addr = dst_hardware_addr; } // Save the total packet len (without the Ethernet header, but with the first // IP header). - *packet_len = total_ip_len; + pkt.packet_len = total_ip_len; // Save the IP header for other fragments. - *out_packet_repr = *repr; + pkt.repr = repr; // Save how much bytes we will send now. - *sent_bytes = first_frag_ip_len; + pkt.sent_bytes = first_frag_ip_len; // Modify the IP header repr.payload_len = first_frag_ip_len - repr.buffer_len(); // Emit the IP header to the buffer. - emit_ip(&ip_repr, buffer); - let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); - *ident = ipv4_id; + emit_ip(&ip_repr, &mut pkt.buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut pkt.buffer[..]); + pkt.ident = ipv4_id; ipv4_packet.set_ident(ipv4_id); ipv4_packet.set_more_frags(true); ipv4_packet.set_dont_frag(false); @@ -1886,11 +1866,11 @@ impl InterfaceInner { } // Change the offset for the next packet. - *frag_offset = (first_frag_ip_len - ip_header_len) as u16; + pkt.frag_offset = (first_frag_ip_len - ip_header_len) as u16; // Copy the IP header and the payload. tx_buffer[..first_frag_ip_len] - .copy_from_slice(&buffer[..first_frag_ip_len]); + .copy_from_slice(&pkt.buffer[..first_frag_ip_len]); Ok(()) }) diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 9bbdbe244..ae21a82b2 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -371,20 +371,9 @@ impl InterfaceInner { // `dispatch_ieee802154_out_packet` requires some information about the total packet size, // the link local source and destination address... - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - datagram_offset, - .. - } = &mut _out_packet.unwrap().sixlowpan_out_packet; - - if buffer.len() < total_size { + let pkt = &mut _out_packet.unwrap().sixlowpan_out_packet; + + if pkt.buffer.len() < total_size { net_debug!( "dispatch_ieee802154: dropping, fragmentation buffer is too small, at least {} needed", total_size @@ -392,14 +381,14 @@ impl InterfaceInner { return; } - *ll_dst_addr = ll_dst_a; - *ll_src_addr = ll_src_a; + pkt.ll_dst_addr = ll_dst_a; + pkt.ll_src_addr = ll_src_a; let mut iphc_packet = - SixlowpanIphcPacket::new_unchecked(&mut buffer[..iphc_repr.buffer_len()]); + SixlowpanIphcPacket::new_unchecked(&mut pkt.buffer[..iphc_repr.buffer_len()]); iphc_repr.emit(&mut iphc_packet); - let b = &mut buffer[iphc_repr.buffer_len()..]; + let b = &mut pkt.buffer[iphc_repr.buffer_len()..]; match packet { #[cfg(feature = "socket-udp")] @@ -442,24 +431,24 @@ impl InterfaceInner { _ => unreachable!(), } - *packet_len = total_size; + pkt.packet_len = total_size; // The datagram size that we need to set in the first fragment header is equal to the // IPv6 payload length + 40. - *datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + pkt.datagram_size = (packet.ip_repr().payload_len() + 40) as u16; // We generate a random tag. let tag = self.get_sixlowpan_fragment_tag(); // We save the tag for the other fragments that will be created when calling `poll` // multiple times. - *datagram_tag = tag; + pkt.datagram_tag = tag; let frag1 = SixlowpanFragRepr::FirstFragment { - size: *datagram_size, + size: pkt.datagram_size, tag, }; let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, + size: pkt.datagram_size, tag, offset: 0, }; @@ -475,10 +464,10 @@ impl InterfaceInner { let frag1_size = (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); - *fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; + pkt.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; - *sent_bytes = frag1_size; - *datagram_offset = frag1_size + header_diff; + pkt.sent_bytes = frag1_size; + pkt.datagram_offset = frag1_size + header_diff; tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| { // Add the IEEE header. @@ -492,7 +481,7 @@ impl InterfaceInner { tx_buf = &mut tx_buf[frag1.buffer_len()..]; // Add the buffer part. - tx_buf[..frag1_size].copy_from_slice(&buffer[..frag1_size]); + tx_buf[..frag1_size].copy_from_slice(&pkt.buffer[..frag1_size]); }); } @@ -566,21 +555,8 @@ impl InterfaceInner { pub(super) fn dispatch_ieee802154_out_packet( &mut self, tx_token: Tx, - out_packet: &mut SixlowpanOutPacket, + pkt: &mut SixlowpanOutPacket, ) { - let SixlowpanOutPacket { - buffer, - packet_len, - datagram_size, - datagram_tag, - datagram_offset, - sent_bytes, - fragn_size, - ll_dst_addr, - ll_src_addr, - .. - } = out_packet; - // Create the IEEE802.15.4 header. let ieee_repr = Ieee802154Repr { frame_type: Ieee802154FrameType::Data, @@ -591,20 +567,20 @@ impl InterfaceInner { pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, dst_pan_id: self.pan_id, - dst_addr: Some(*ll_dst_addr), + dst_addr: Some(pkt.ll_dst_addr), src_pan_id: self.pan_id, - src_addr: Some(*ll_src_addr), + src_addr: Some(pkt.ll_src_addr), }; // Create the FRAG_N header. let fragn = SixlowpanFragRepr::Fragment { - size: *datagram_size, - tag: *datagram_tag, - offset: (*datagram_offset / 8) as u8, + size: pkt.datagram_size, + tag: pkt.datagram_tag, + offset: (pkt.datagram_offset / 8) as u8, }; let ieee_len = ieee_repr.buffer_len(); - let frag_size = (*packet_len - *sent_bytes).min(*fragn_size); + let frag_size = (pkt.packet_len - pkt.sent_bytes).min(pkt.fragn_size); tx_token.consume( ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, @@ -619,10 +595,10 @@ impl InterfaceInner { tx_buf = &mut tx_buf[fragn.buffer_len()..]; // Add the buffer part - tx_buf[..frag_size].copy_from_slice(&buffer[*sent_bytes..][..frag_size]); + tx_buf[..frag_size].copy_from_slice(&pkt.buffer[pkt.sent_bytes..][..frag_size]); - *sent_bytes += frag_size; - *datagram_offset += frag_size; + pkt.sent_bytes += frag_size; + pkt.datagram_offset += frag_size; }, ); } diff --git a/src/iface/socket_set.rs b/src/iface/socket_set.rs index 1611d579a..fe9bef755 100644 --- a/src/iface/socket_set.rs +++ b/src/iface/socket_set.rs @@ -77,10 +77,10 @@ impl<'a> SocketSet<'a> { } } - match self.sockets { + match &mut self.sockets { ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"), #[cfg(feature = "alloc")] - ManagedSlice::Owned(ref mut sockets) => { + ManagedSlice::Owned(sockets) => { sockets.push(SocketStorage { inner: None }); let index = sockets.len() - 1; put(index, &mut sockets[index], socket) diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index a4f4e19f9..a2a95405b 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -62,18 +62,13 @@ where } fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let &mut Self { - ref mut inner, - ref fuzz_rx, - ref fuzz_tx, - } = self; - inner.receive(timestamp).map(|(rx_token, tx_token)| { + self.inner.receive(timestamp).map(|(rx_token, tx_token)| { let rx = RxToken { - fuzzer: fuzz_rx, + fuzzer: &mut self.fuzz_rx, token: rx_token, }; let tx = TxToken { - fuzzer: fuzz_tx, + fuzzer: &mut self.fuzz_tx, token: tx_token, }; (rx, tx) @@ -81,13 +76,8 @@ where } fn transmit(&mut self, timestamp: Instant) -> Option> { - let &mut Self { - ref mut inner, - fuzz_rx: _, - ref fuzz_tx, - } = self; - inner.transmit(timestamp).map(|token| TxToken { - fuzzer: fuzz_tx, + self.inner.transmit(timestamp).map(|token| TxToken { + fuzzer: &mut self.fuzz_tx, token: token, }) } diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index beadfdac3..0a4cc2990 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -81,7 +81,7 @@ impl Device for RawSocket { }; Some((rx, tx)) } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => None, Err(err) => panic!("{}", err), } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 5e9b7df08..9e877753c 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -54,22 +54,17 @@ impl Device for Tracer { } fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let &mut Self { - ref mut inner, - writer, - .. - } = self; - let medium = inner.capabilities().medium; - inner.receive(timestamp).map(|(rx_token, tx_token)| { + let medium = self.inner.capabilities().medium; + self.inner.receive(timestamp).map(|(rx_token, tx_token)| { let rx = RxToken { token: rx_token, - writer, + writer: self.writer, medium, timestamp, }; let tx = TxToken { token: tx_token, - writer, + writer: self.writer, medium, timestamp, }; @@ -78,15 +73,11 @@ impl Device for Tracer { } fn transmit(&mut self, timestamp: Instant) -> Option> { - let &mut Self { - ref mut inner, - writer, - } = self; - let medium = inner.capabilities().medium; - inner.transmit(timestamp).map(|tx_token| TxToken { + let medium = self.inner.capabilities().medium; + self.inner.transmit(timestamp).map(|tx_token| TxToken { token: tx_token, medium, - writer, + writer: self.writer, timestamp, }) } diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index 7f7ae3c05..c81d8ac3e 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -63,7 +63,7 @@ impl Device for TunTapInterface { }; Some((rx, tx)) } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => None, Err(err) => panic!("{}", err), } } diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 0a5c36a68..2a7f3b9bd 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -182,10 +182,10 @@ impl<'a> Socket<'a> { } } - match self.queries { + match &mut self.queries { ManagedSlice::Borrowed(_) => None, #[cfg(feature = "alloc")] - ManagedSlice::Owned(ref mut queries) => { + ManagedSlice::Owned(queries) => { queries.push(None); let index = queries.len() - 1; Some(QueryHandle(index)) diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index b3abc03dd..7e12c9229 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -446,7 +446,7 @@ impl<'a> Socket<'a> { pub(crate) fn process(&mut self, _cx: &mut Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) { match *icmp_repr { #[cfg(feature = "proto-ipv4")] - IcmpRepr::Ipv4(ref icmp_repr) => { + IcmpRepr::Ipv4(icmp_repr) => { net_trace!("icmp: receiving {} octets", icmp_repr.buffer_len()); match self @@ -463,7 +463,7 @@ impl<'a> Socket<'a> { } } #[cfg(feature = "proto-ipv6")] - IcmpRepr::Ipv6(ref icmp_repr) => { + IcmpRepr::Ipv6(icmp_repr) => { net_trace!("icmp: receiving {} octets", icmp_repr.buffer_len()); match self diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 4e8c53583..bcec24d84 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -273,10 +273,7 @@ impl Timer { } fn set_keep_alive(&mut self) { - if let Timer::Idle { - ref mut keep_alive_at, - } = *self - { + if let Timer::Idle { keep_alive_at } = self { if keep_alive_at.is_none() { *keep_alive_at = Some(Instant::from_millis(0)) } @@ -284,10 +281,7 @@ impl Timer { } fn rewind_keep_alive(&mut self, timestamp: Instant, interval: Option) { - if let Timer::Idle { - ref mut keep_alive_at, - } = *self - { + if let Timer::Idle { keep_alive_at } = self { *keep_alive_at = interval.map(|interval| timestamp + interval) } } @@ -1734,9 +1728,9 @@ impl<'a> Socket<'a> { // Duplicate ACK if payload empty and ACK doesn't move send window -> // Increment duplicate ACK count and set for retransmit if we just received // the third duplicate ACK - Some(ref last_rx_ack) + Some(last_rx_ack) if repr.payload.is_empty() - && *last_rx_ack == ack_number + && last_rx_ack == ack_number && ack_number < self.remote_last_seq => { // Increment duplicate ACK count diff --git a/src/storage/packet_buffer.rs b/src/storage/packet_buffer.rs index eb8c0627f..1447e8245 100644 --- a/src/storage/packet_buffer.rs +++ b/src/storage/packet_buffer.rs @@ -184,17 +184,15 @@ impl<'a, H> PacketBuffer<'a, H> { self.dequeue_padding(); self.metadata_ring.dequeue_one_with(|metadata| { - let PacketMetadata { - ref mut header, - size, - } = *metadata; - self.payload_ring .dequeue_many_with(|payload_buf| { - debug_assert!(payload_buf.len() >= size); + debug_assert!(payload_buf.len() >= metadata.size); - match f(header.as_mut().unwrap(), &mut payload_buf[..size]) { - Ok(val) => (size, Ok(val)), + match f( + metadata.header.as_mut().unwrap(), + &mut payload_buf[..metadata.size], + ) { + Ok(val) => (metadata.size, Ok(val)), Err(err) => (0, Err(err)), } }) @@ -207,14 +205,11 @@ impl<'a, H> PacketBuffer<'a, H> { pub fn dequeue(&mut self) -> Result<(H, &mut [u8]), Empty> { self.dequeue_padding(); - let PacketMetadata { - ref mut header, - size, - } = *self.metadata_ring.dequeue_one()?; + let meta = self.metadata_ring.dequeue_one()?; - let payload_buf = self.payload_ring.dequeue_many(size); - debug_assert!(payload_buf.len() == size); - Ok((header.take().unwrap(), payload_buf)) + let payload_buf = self.payload_ring.dequeue_many(meta.size); + debug_assert!(payload_buf.len() == meta.size); + Ok((meta.header.take().unwrap(), payload_buf)) } /// Peek at a single packet from the buffer without removing it, and return a reference to diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 67024bed9..947793d72 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -637,21 +637,21 @@ impl + AsMut<[u8]>> Frame { /// Set the destination address. #[inline] - pub fn set_dst_addr(&mut self, mut value: Address) { + pub fn set_dst_addr(&mut self, value: Address) { match value { Address::Absent => self.set_dst_addressing_mode(AddressingMode::Absent), - Address::Short(ref mut value) => { + Address::Short(mut value) => { value.reverse(); self.set_dst_addressing_mode(AddressingMode::Short); let data = self.buffer.as_mut(); - data[field::ADDRESSING][2..2 + 2].copy_from_slice(value); + data[field::ADDRESSING][2..2 + 2].copy_from_slice(&value); value.reverse(); } - Address::Extended(ref mut value) => { + Address::Extended(mut value) => { value.reverse(); self.set_dst_addressing_mode(AddressingMode::Extended); let data = &mut self.buffer.as_mut()[field::ADDRESSING]; - data[2..2 + 8].copy_from_slice(value); + data[2..2 + 8].copy_from_slice(&value); value.reverse(); } } @@ -683,7 +683,7 @@ impl + AsMut<[u8]>> Frame { /// Set the source address. #[inline] - pub fn set_src_addr(&mut self, mut value: Address) { + pub fn set_src_addr(&mut self, value: Address) { let offset = match self.dst_addressing_mode() { AddressingMode::Absent => 0, AddressingMode::Short => 2, @@ -695,18 +695,18 @@ impl + AsMut<[u8]>> Frame { match value { Address::Absent => self.set_src_addressing_mode(AddressingMode::Absent), - Address::Short(ref mut value) => { + Address::Short(mut value) => { value.reverse(); self.set_src_addressing_mode(AddressingMode::Short); let data = &mut self.buffer.as_mut()[field::ADDRESSING]; - data[offset..offset + 2].copy_from_slice(value); + data[offset..offset + 2].copy_from_slice(&value); value.reverse(); } - Address::Extended(ref mut value) => { + Address::Extended(mut value) => { value.reverse(); self.set_src_addressing_mode(AddressingMode::Extended); let data = &mut self.buffer.as_mut()[field::ADDRESSING]; - data[offset..offset + 8].copy_from_slice(value); + data[offset..offset + 8].copy_from_slice(&value); value.reverse(); } } diff --git a/src/wire/ip.rs b/src/wire/ip.rs index e3a3478c5..1c6baa475 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -116,17 +116,17 @@ impl Address { /// Return an address as a sequence of octets, in big-endian. pub const fn as_bytes(&self) -> &[u8] { - match *self { + match self { #[cfg(feature = "proto-ipv4")] - Address::Ipv4(ref addr) => addr.as_bytes(), + Address::Ipv4(addr) => addr.as_bytes(), #[cfg(feature = "proto-ipv6")] - Address::Ipv6(ref addr) => addr.as_bytes(), + Address::Ipv6(addr) => addr.as_bytes(), } } /// Query whether the address is a valid unicast address. pub fn is_unicast(&self) -> bool { - match *self { + match self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_unicast(), #[cfg(feature = "proto-ipv6")] @@ -136,7 +136,7 @@ impl Address { /// Query whether the address is a valid multicast address. pub const fn is_multicast(&self) -> bool { - match *self { + match self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_multicast(), #[cfg(feature = "proto-ipv6")] @@ -146,7 +146,7 @@ impl Address { /// Query whether the address is the broadcast address. pub fn is_broadcast(&self) -> bool { - match *self { + match self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_broadcast(), #[cfg(feature = "proto-ipv6")] @@ -156,7 +156,7 @@ impl Address { /// Query whether the address falls into the "unspecified" range. pub fn is_unspecified(&self) -> bool { - match *self { + match self { #[cfg(feature = "proto-ipv4")] Address::Ipv4(addr) => addr.is_unspecified(), #[cfg(feature = "proto-ipv6")] From 7bc1d4e4ea155db5a75176bba9eba08eb2a35a95 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Feb 2023 19:58:28 +0100 Subject: [PATCH 499/566] Fix clippy. --- src/iface/socket_meta.rs | 9 ++------- src/phy/mod.rs | 9 ++------- src/socket/icmp.rs | 9 ++------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/iface/socket_meta.rs b/src/iface/socket_meta.rs index a48f08bd8..82c99087b 100644 --- a/src/iface/socket_meta.rs +++ b/src/iface/socket_meta.rs @@ -9,10 +9,11 @@ use crate::{ /// /// This enum tracks whether the socket should be polled based on the neighbor /// it is going to send packets to. -#[derive(Debug)] +#[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum NeighborState { /// Socket can be polled immediately. + #[default] Active, /// Socket should not be polled until either `silent_until` passes or /// `neighbor` appears in the neighbor cache. @@ -22,12 +23,6 @@ enum NeighborState { }, } -impl Default for NeighborState { - fn default() -> Self { - NeighborState::Active - } -} - /// Network socket metadata. /// /// This includes things that only external (to the socket, that is) code diff --git a/src/phy/mod.rs b/src/phy/mod.rs index cb76fb779..91cef8b1a 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -131,10 +131,11 @@ pub use self::tracer::Tracer; pub use self::tuntap_interface::TunTapInterface; /// A description of checksum behavior for a particular protocol. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Checksum { /// Verify checksum when receiving and compute checksum when sending. + #[default] Both, /// Verify checksum when receiving. Rx, @@ -144,12 +145,6 @@ pub enum Checksum { None, } -impl Default for Checksum { - fn default() -> Checksum { - Checksum::Both - } -} - impl Checksum { /// Returns whether checksum should be verified when receiving. pub fn rx(&self) -> bool { diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 7e12c9229..c9ec30f21 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -43,9 +43,10 @@ pub enum RecvError { /// more details. /// /// [IcmpSocket::bind]: struct.IcmpSocket.html#method.bind -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Endpoint { + #[default] Unspecified, Ident(u16), Udp(IpListenEndpoint), @@ -61,12 +62,6 @@ impl Endpoint { } } -impl Default for Endpoint { - fn default() -> Endpoint { - Endpoint::Unspecified - } -} - /// An ICMP packet metadata. pub type PacketMetadata = crate::storage::PacketMetadata; From f428a50e7f1bc351cfd1781153f8483d3d1a155e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Feb 2023 22:35:27 +0100 Subject: [PATCH 500/566] iface: rename sixlowpan_fragments_cache_timeout -> sixlowpan_reassembly_timeout --- src/iface/interface/mod.rs | 8 ++++---- src/iface/interface/sixlowpan.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index d30e6abc1..a5bf50872 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -51,7 +51,7 @@ pub(crate) struct FragmentsBuffer { #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: Duration, + sixlowpan_reassembly_timeout: Duration, } pub(crate) struct OutPackets { @@ -563,7 +563,7 @@ impl Interface { #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments_cache_timeout: Duration::from_secs(60), + sixlowpan_reassembly_timeout: Duration::from_secs(60), }, out_packets: OutPackets { #[cfg(feature = "proto-ipv4-fragmentation")] @@ -736,7 +736,7 @@ impl Interface { /// Currently used only for 6LoWPAN, will be used for IPv4 in the future as well. #[cfg(feature = "proto-sixlowpan-fragmentation")] pub fn reassembly_timeout(&self) -> Duration { - self.fragments.sixlowpan_fragments_cache_timeout + self.fragments.sixlowpan_reassembly_timeout } /// Set the packet reassembly timeout. @@ -747,7 +747,7 @@ impl Interface { if timeout > Duration::from_secs(60) { net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); } - self.fragments.sixlowpan_fragments_cache_timeout = timeout; + self.fragments.sixlowpan_reassembly_timeout = timeout; } /// Transmit packets queued in the given sockets, and receive packets queued diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index ae21a82b2..4e50b2009 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -117,7 +117,7 @@ impl InterfaceInner { // (other than the first one) are added. let frag_slot = match f .sixlowpan_fragments - .get(&key, self.now + f.sixlowpan_fragments_cache_timeout) + .get(&key, self.now + f.sixlowpan_reassembly_timeout) { Ok(frag) => frag, Err(AssemblerFullError) => { From 12fd8161c75a52d42d9922f3ec78bf01c09042b3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Feb 2023 22:39:41 +0100 Subject: [PATCH 501/566] iface: unify ipv4/6lowpan packet assemblers. If you enable both ipv4 and 6lowpan on the same binary, an Interface had twice the needed reassembly buffers, one copy for ipv4 and another for 6lowpan. Only one of both was used at a time. Now interfaces have a single assembler used for either medium. --- Cargo.toml | 10 +++++-- src/iface/interface/ethernet.rs | 20 ++++++------- src/iface/interface/ipv4.rs | 27 +++++------------- src/iface/interface/mod.rs | 48 +++++++++++++++++--------------- src/iface/interface/sixlowpan.rs | 5 ++-- src/iface/interface/tests.rs | 38 +++++++------------------ src/wire/ipv4.rs | 1 + 7 files changed, 62 insertions(+), 87 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 289600449..86a66378f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,12 +45,12 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "proto-ipv4" = [] -"proto-ipv4-fragmentation" = ["proto-ipv4"] +"proto-ipv4-fragmentation" = ["proto-ipv4", "_proto-fragmentation"] "proto-igmp" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] "proto-sixlowpan" = ["proto-ipv6"] -"proto-sixlowpan-fragmentation" = ["proto-sixlowpan"] +"proto-sixlowpan-fragmentation" = ["proto-sixlowpan", "_proto-fragmentation"] "proto-dns" = [] "socket" = [] @@ -74,6 +74,12 @@ default = [ "async" ] +# Private features +# Features starting with "_" are considered private. They should not be enabled by +# other crates, and they are not considered semver-stable. + +"_proto-fragmentation" = [] + [[example]] name = "packet2pcap" path = "utils/packet2pcap.rs" diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index d208a34d9..72eaf290c 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -15,7 +15,7 @@ impl InterfaceInner { &mut self, sockets: &mut SocketSet, frame: &'frame T, - _fragments: &'frame mut FragmentsBuffer, + fragments: &'frame mut FragmentsBuffer, ) -> Option> { let eth_frame = check!(EthernetFrame::new_checked(frame)); @@ -34,17 +34,13 @@ impl InterfaceInner { EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - #[cfg(feature = "proto-ipv4-fragmentation")] - { - self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - .map(EthernetPacket::Ip) - } - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - { - self.process_ipv4(sockets, &ipv4_packet, None) - .map(EthernetPacket::Ip) - } + self.process_ipv4( + sockets, + &ipv4_packet, + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut fragments.assembler, + ) + .map(EthernetPacket::Ip) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 79247f0e1..c8ee02e73 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -1,15 +1,4 @@ -use super::check; -use super::icmp_reply_payload_len; -use super::InterfaceInner; -use super::IpPacket; -use super::PacketAssemblerSet; -use super::SocketSet; - -#[cfg(feature = "medium-ethernet")] -use super::EthernetPacket; - -#[cfg(feature = "proto-ipv4-fragmentation")] -use super::Ipv4OutPacket; +use super::*; #[cfg(feature = "socket-dhcpv4")] use crate::socket::dhcpv4; @@ -22,12 +11,12 @@ use crate::time::{Duration, Instant}; use crate::wire::*; impl InterfaceInner { - pub(super) fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - ipv4_packet: &Ipv4Packet<&'payload T>, - _fragments: Option<&'output mut PacketAssemblerSet>, - ) -> Option> { + ipv4_packet: &Ipv4Packet<&'a T>, + #[cfg(feature = "proto-ipv4-fragmentation")] assembler: &'a mut PacketAssemblerSet, + ) -> Option> { let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); if !self.is_unicast_v4(ipv4_repr.src_addr) { // Discard packets with non-unicast source addresses. @@ -39,12 +28,10 @@ impl InterfaceInner { let ip_payload = { const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(90); - let fragments = _fragments.unwrap(); - if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { - let key = ipv4_packet.get_key(); + let key = FragKey::Ipv4(ipv4_packet.get_key()); - let f = match fragments.get(&key, self.now + REASSEMBLY_TIMEOUT) { + let f = match assembler.get(&key, self.now + REASSEMBLY_TIMEOUT) { Ok(f) => f, Err(_) => { net_debug!("No available packet assembler for fragmented packet"); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index a5bf50872..69f8c7cbc 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -43,13 +43,23 @@ const FRAGMENTATION_BUFFER_SIZE: usize = 1500; #[cfg(feature = "proto-sixlowpan")] const SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4; +#[cfg(feature = "_proto-fragmentation")] +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) enum FragKey { + #[cfg(feature = "proto-ipv4-fragmentation")] + Ipv4(Ipv4FragKey), + #[cfg(feature = "proto-sixlowpan-fragmentation")] + Sixlowpan(SixlowpanFragKey), +} + pub(crate) struct FragmentsBuffer { #[cfg(feature = "proto-sixlowpan")] decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN], - #[cfg(feature = "proto-ipv4-fragmentation")] - pub(crate) ipv4_fragments: PacketAssemblerSet, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet, + + #[cfg(feature = "_proto-fragmentation")] + pub(crate) assembler: PacketAssemblerSet, + #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_timeout: Duration, } @@ -558,10 +568,8 @@ impl Interface { #[cfg(feature = "proto-sixlowpan")] decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN], - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: PacketAssemblerSet::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: PacketAssemblerSet::new(), + #[cfg(feature = "_proto-fragmentation")] + assembler: PacketAssemblerSet::new(), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_timeout: Duration::from_secs(60), }, @@ -767,11 +775,8 @@ impl Interface { { self.inner.now = timestamp; - #[cfg(feature = "proto-ipv4-fragmentation")] - self.fragments.ipv4_fragments.remove_expired(timestamp); - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - self.fragments.sixlowpan_fragments.remove_expired(timestamp); + #[cfg(feature = "_proto-fragmentation")] + self.fragments.assembler.remove_expired(timestamp); #[cfg(feature = "proto-ipv4-fragmentation")] if self.ipv4_egress(device) { @@ -1328,22 +1333,19 @@ impl InterfaceInner { &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, - _fragments: &'frame mut FragmentsBuffer, + fragments: &'frame mut FragmentsBuffer, ) -> Option> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - #[cfg(feature = "proto-ipv4-fragmentation")] - { - self.process_ipv4(sockets, &ipv4_packet, Some(&mut _fragments.ipv4_fragments)) - } - - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - { - self.process_ipv4(sockets, &ipv4_packet, None) - } + self.process_ipv4( + sockets, + &ipv4_packet, + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut fragments.assembler, + ) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 4e50b2009..e2db1054c 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -98,6 +98,7 @@ impl InterfaceInner { f: &'output mut FragmentsBuffer, ) -> Option<&'output [u8]> { use crate::iface::fragmentation::{AssemblerError, AssemblerFullError}; + use crate::iface::interface::FragKey; // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. @@ -105,7 +106,7 @@ impl InterfaceInner { // The key specifies to which 6LoWPAN fragment it belongs too. // It is based on the link layer addresses, the tag and the size. - let key = frag.get_key(ieee802154_repr); + let key = FragKey::Sixlowpan(frag.get_key(ieee802154_repr)); // The offset of this fragment in increments of 8 octets. let offset = frag.datagram_offset() as usize * 8; @@ -116,7 +117,7 @@ impl InterfaceInner { // We also pass the header size, since this is needed when other fragments // (other than the first one) are added. let frag_slot = match f - .sixlowpan_fragments + .assembler .get(&key, self.now + f.sixlowpan_reassembly_timeout) { Ok(frag) => frag, diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 01c7dba27..8488ce09e 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -167,14 +167,12 @@ fn test_no_icmp_no_unicast_ipv4() { // ICMP error response when the destination address is a // broadcast address - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( iface.inner.process_ipv4( &mut sockets, &frame, - Some(&mut iface.fragments.ipv4_fragments) + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut iface.fragments.assembler ), None ); @@ -255,18 +253,12 @@ fn test_icmp_error_no_payload() { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame, None), - Some(expected_repr) - ); - - #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( iface.inner.process_ipv4( &mut sockets, &frame, - Some(&mut iface.fragments.ipv4_fragments) + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut iface.fragments.assembler ), Some(expected_repr) ); @@ -571,18 +563,12 @@ fn test_handle_ipv4_broadcast() { }; let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!( - iface.inner.process_ipv4(&mut sockets, &frame, None), - Some(expected_packet) - ); - - #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( iface.inner.process_ipv4( &mut sockets, &frame, - Some(&mut iface.fragments.ipv4_fragments) + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut iface.fragments.assembler ), Some(expected_packet) ); @@ -1277,14 +1263,12 @@ fn test_raw_socket_no_reply() { Ipv4Packet::new_unchecked(&bytes) }; - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( iface.inner.process_ipv4( &mut sockets, &frame, - Some(&mut iface.fragments.ipv4_fragments) + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut iface.fragments.assembler ), None ); @@ -1368,14 +1352,12 @@ fn test_raw_socket_with_udp_socket() { Ipv4Packet::new_unchecked(&bytes) }; - #[cfg(not(feature = "proto-ipv4-fragmentation"))] - assert_eq!(iface.inner.process_ipv4(&mut sockets, &frame, None), None); - #[cfg(feature = "proto-ipv4-fragmentation")] assert_eq!( iface.inner.process_ipv4( &mut sockets, &frame, - Some(&mut iface.fragments.ipv4_fragments) + #[cfg(feature = "proto-ipv4-fragmentation")] + &mut iface.fragments.assembler ), None ); diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index cdebe7b39..b039704b1 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -27,6 +27,7 @@ pub const MIN_MTU: usize = 576; pub const ADDR_SIZE: usize = 4; #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Key { id: u16, src_addr: Address, From d13db8b291dc8291e837e10abf22c946410f21ba Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Feb 2023 00:54:40 +0100 Subject: [PATCH 502/566] iface: unify ipv4/6lowpan fragmenters. Same rationale as previous commit. Also, rename "OutPacket" to "Fragmenter". --- src/iface/interface/igmp.rs | 16 +- src/iface/interface/ipv4.rs | 36 ++-- src/iface/interface/mod.rs | 309 ++++++++++++++----------------- src/iface/interface/sixlowpan.rs | 61 +++--- src/iface/interface/tests.rs | 16 +- 5 files changed, 199 insertions(+), 239 deletions(-) diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index 8ac6497a4..3e9fa726f 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -51,7 +51,9 @@ impl Interface { .ok_or(MulticastError::Exhausted)?; // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. - self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + self.inner + .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .unwrap(); Ok(true) } else { @@ -91,7 +93,9 @@ impl Interface { .ok_or(MulticastError::Exhausted)?; // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. - self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + self.inner + .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .unwrap(); Ok(true) } else { @@ -125,7 +129,9 @@ impl Interface { // Send initial membership report if let Some(tx_token) = device.transmit(self.inner.now) { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. - self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + self.inner + .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .unwrap(); } else { return false; } @@ -153,7 +159,9 @@ impl Interface { // Send initial membership report if let Some(tx_token) = device.transmit(self.inner.now) { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. - self.inner.dispatch_ip(tx_token, pkt, None).unwrap(); + self.inner + .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .unwrap(); } else { return false; } diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index c8ee02e73..8b76407a0 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -332,20 +332,16 @@ impl InterfaceInner { } #[cfg(feature = "proto-ipv4-fragmentation")] - pub(super) fn dispatch_ipv4_out_packet( - &mut self, - tx_token: Tx, - pkt: &mut Ipv4OutPacket, - ) { + pub(super) fn dispatch_ipv4_frag(&mut self, tx_token: Tx, frag: &mut Fragmenter) { let caps = self.caps.clone(); let mtu_max = self.ip_mtu(); - let ip_len = (pkt.packet_len - pkt.sent_bytes + pkt.repr.buffer_len()).min(mtu_max); - let payload_len = ip_len - pkt.repr.buffer_len(); + let ip_len = (frag.packet_len - frag.sent_bytes + frag.ipv4.repr.buffer_len()).min(mtu_max); + let payload_len = ip_len - frag.ipv4.repr.buffer_len(); - let more_frags = (pkt.packet_len - pkt.sent_bytes) != payload_len; - pkt.repr.payload_len = payload_len; - pkt.sent_bytes += payload_len; + let more_frags = (frag.packet_len - frag.sent_bytes) != payload_len; + frag.ipv4.repr.payload_len = payload_len; + frag.sent_bytes += payload_len; let mut tx_len = ip_len; #[cfg(feature = "medium-ethernet")] @@ -360,7 +356,7 @@ impl InterfaceInner { let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); frame.set_src_addr(src_addr); - frame.set_dst_addr(pkt.dst_hardware_addr); + frame.set_dst_addr(frag.ipv4.dst_hardware_addr); match repr.version() { #[cfg(feature = "proto-ipv4")] @@ -373,27 +369,29 @@ impl InterfaceInner { tx_token.consume(tx_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] if matches!(self.caps.medium, Medium::Ethernet) { - emit_ethernet(&IpRepr::Ipv4(pkt.repr), tx_buffer); + emit_ethernet(&IpRepr::Ipv4(frag.ipv4.repr), tx_buffer); tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; } - let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..pkt.repr.buffer_len()]); - pkt.repr.emit(&mut packet, &caps.checksum); - packet.set_ident(pkt.ident); + let mut packet = + Ipv4Packet::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]); + frag.ipv4.repr.emit(&mut packet, &caps.checksum); + packet.set_ident(frag.ipv4.ident); packet.set_more_frags(more_frags); packet.set_dont_frag(false); - packet.set_frag_offset(pkt.frag_offset); + packet.set_frag_offset(frag.ipv4.frag_offset); if caps.checksum.ipv4.tx() { packet.fill_checksum(); } - tx_buffer[pkt.repr.buffer_len()..][..payload_len].copy_from_slice( - &pkt.buffer[pkt.frag_offset as usize + pkt.repr.buffer_len()..][..payload_len], + tx_buffer[frag.ipv4.repr.buffer_len()..][..payload_len].copy_from_slice( + &frag.buffer[frag.ipv4.frag_offset as usize + frag.ipv4.repr.buffer_len()..] + [..payload_len], ); // Update the frag offset for the next fragment. - pkt.frag_offset += payload_len as u16; + frag.ipv4.frag_offset += payload_len as u16; }) } diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 69f8c7cbc..cb680c519 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -64,37 +64,18 @@ pub(crate) struct FragmentsBuffer { sixlowpan_reassembly_timeout: Duration, } -pub(crate) struct OutPackets { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket, -} - -impl OutPackets { - #[cfg(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - ))] - /// Returns `true` when all the data of the outgoing buffers are transmitted. - fn all_transmitted(&self) -> bool { - #[cfg(feature = "proto-ipv4-fragmentation")] - if !self.ipv4_out_packet.is_empty() { - return false; - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - if !self.sixlowpan_out_packet.is_empty() { - return false; - } +#[cfg(not(feature = "_proto-fragmentation"))] +pub(crate) struct Fragmenter {} - true +#[cfg(not(feature = "_proto-fragmentation"))] +impl Fragmenter { + pub(crate) fn new() -> Self { + Self {} } } -#[allow(unused)] -#[cfg(feature = "proto-ipv4-fragmentation")] -pub(crate) struct Ipv4OutPacket { +#[cfg(feature = "_proto-fragmentation")] +pub(crate) struct Fragmenter { /// The buffer that holds the unfragmented 6LoWPAN packet. buffer: [u8; FRAGMENTATION_BUFFER_SIZE], /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. @@ -102,6 +83,14 @@ pub(crate) struct Ipv4OutPacket { /// The amount of bytes that already have been transmitted. sent_bytes: usize, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4: Ipv4Fragmenter, + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan: SixlowpanFragmenter, +} + +#[cfg(feature = "proto-ipv4-fragmentation")] +pub(crate) struct Ipv4Fragmenter { /// The IPv4 representation. repr: Ipv4Repr, /// The destination hardware address. @@ -113,67 +102,8 @@ pub(crate) struct Ipv4OutPacket { ident: u16, } -#[cfg(feature = "proto-ipv4-fragmentation")] -impl Ipv4OutPacket { - pub(crate) fn new() -> Self { - Self { - buffer: [0u8; FRAGMENTATION_BUFFER_SIZE], - packet_len: 0, - sent_bytes: 0, - repr: Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: Ipv4Address::default(), - next_header: IpProtocol::Unknown(0), - payload_len: 0, - hop_limit: 0, - }, - #[cfg(feature = "medium-ethernet")] - dst_hardware_addr: EthernetAddress::default(), - frag_offset: 0, - ident: 0, - } - } - - /// Return `true` when everything is transmitted. - #[inline] - fn finished(&self) -> bool { - self.packet_len == self.sent_bytes - } - - /// Returns `true` when there is nothing to transmit. - #[inline] - fn is_empty(&self) -> bool { - self.packet_len == 0 - } - - // Reset the buffer. - fn reset(&mut self) { - self.packet_len = 0; - self.sent_bytes = 0; - self.repr = Ipv4Repr { - src_addr: Ipv4Address::default(), - dst_addr: Ipv4Address::default(), - next_header: IpProtocol::Unknown(0), - payload_len: 0, - hop_limit: 0, - }; - #[cfg(feature = "medium-ethernet")] - { - self.dst_hardware_addr = EthernetAddress::default(); - } - } -} - -#[allow(unused)] -#[cfg(feature = "proto-sixlowpan")] -pub(crate) struct SixlowpanOutPacket { - /// The buffer that holds the unfragmented 6LoWPAN packet. - buffer: [u8; FRAGMENTATION_BUFFER_SIZE], - /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. - packet_len: usize, - /// The amount of bytes that already have been transmitted. - sent_bytes: usize, - +#[cfg(feature = "proto-sixlowpan-fragmentation")] +pub(crate) struct SixlowpanFragmenter { /// The datagram size that is used for the fragmentation headers. datagram_size: u16, /// The datagram tag that is used for the fragmentation headers. @@ -189,19 +119,38 @@ pub(crate) struct SixlowpanOutPacket { ll_src_addr: Ieee802154Address, } -#[cfg(feature = "proto-sixlowpan-fragmentation")] -impl SixlowpanOutPacket { +#[cfg(feature = "_proto-fragmentation")] +impl Fragmenter { pub(crate) fn new() -> Self { Self { buffer: [0u8; FRAGMENTATION_BUFFER_SIZE], packet_len: 0, - datagram_size: 0, - datagram_tag: 0, - datagram_offset: 0, sent_bytes: 0, - fragn_size: 0, - ll_dst_addr: Ieee802154Address::Absent, - ll_src_addr: Ieee802154Address::Absent, + + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4: Ipv4Fragmenter { + repr: Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }, + #[cfg(feature = "medium-ethernet")] + dst_hardware_addr: EthernetAddress::default(), + frag_offset: 0, + ident: 0, + }, + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + sixlowpan: SixlowpanFragmenter { + datagram_size: 0, + datagram_tag: 0, + datagram_offset: 0, + fragn_size: 0, + ll_dst_addr: Ieee802154Address::Absent, + ll_src_addr: Ieee802154Address::Absent, + }, } } @@ -220,12 +169,31 @@ impl SixlowpanOutPacket { // Reset the buffer. fn reset(&mut self) { self.packet_len = 0; - self.datagram_size = 0; - self.datagram_tag = 0; self.sent_bytes = 0; - self.fragn_size = 0; - self.ll_dst_addr = Ieee802154Address::Absent; - self.ll_src_addr = Ieee802154Address::Absent; + + #[cfg(feature = "proto-ipv4-fragmentation")] + { + self.ipv4.repr = Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }; + #[cfg(feature = "medium-ethernet")] + { + self.ipv4.dst_hardware_addr = EthernetAddress::default(); + } + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + { + self.sixlowpan.datagram_size = 0; + self.sixlowpan.datagram_tag = 0; + self.sixlowpan.fragn_size = 0; + self.sixlowpan.ll_dst_addr = Ieee802154Address::Absent; + self.sixlowpan.ll_src_addr = Ieee802154Address::Absent; + } } } @@ -254,7 +222,7 @@ use check; pub struct Interface { inner: InterfaceInner, fragments: FragmentsBuffer, - out_packets: OutPackets, + fragmenter: Fragmenter, } /// The device independent part of an Ethernet network interface. @@ -573,12 +541,7 @@ impl Interface { #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_reassembly_timeout: Duration::from_secs(60), }, - out_packets: OutPackets { - #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_out_packet: Ipv4OutPacket::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new(), - }, + fragmenter: Fragmenter::new(), inner: InterfaceInner { now: Instant::from_secs(0), caps, @@ -778,14 +741,23 @@ impl Interface { #[cfg(feature = "_proto-fragmentation")] self.fragments.assembler.remove_expired(timestamp); - #[cfg(feature = "proto-ipv4-fragmentation")] - if self.ipv4_egress(device) { - return true; - } - - #[cfg(feature = "proto-sixlowpan-fragmentation")] - if self.sixlowpan_egress(device) { - return true; + match self.inner.caps.medium { + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => + { + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if self.sixlowpan_egress(device) { + return true; + } + } + #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] + _ => + { + #[cfg(feature = "proto-ipv4-fragmentation")] + if self.ipv4_egress(device) { + return true; + } + } } let mut readiness_may_have_changed = false; @@ -821,8 +793,8 @@ impl Interface { pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option { self.inner.now = timestamp; - #[cfg(feature = "proto-sixlowpan-fragmentation")] - if !self.out_packets.all_transmitted() { + #[cfg(feature = "_proto-fragmentation")] + if !self.fragmenter.is_empty() { return Some(Instant::from_millis(0)); } @@ -876,8 +848,7 @@ impl Interface { .process_ethernet(sockets, &frame, &mut self.fragments) { if let Err(err) = - self.inner - .dispatch(tx_token, packet, Some(&mut self.out_packets)) + self.inner.dispatch(tx_token, packet, &mut self.fragmenter) { net_debug!("Failed to send response: {:?}", err); } @@ -888,11 +859,10 @@ impl Interface { if let Some(packet) = self.inner.process_ip(sockets, &frame, &mut self.fragments) { - if let Err(err) = self.inner.dispatch_ip( - tx_token, - packet, - Some(&mut self.out_packets), - ) { + if let Err(err) = + self.inner + .dispatch_ip(tx_token, packet, &mut self.fragmenter) + { net_debug!("Failed to send response: {:?}", err); } } @@ -903,11 +873,10 @@ impl Interface { self.inner .process_ieee802154(sockets, &frame, &mut self.fragments) { - if let Err(err) = self.inner.dispatch_ip( - tx_token, - packet, - Some(&mut self.out_packets), - ) { + if let Err(err) = + self.inner + .dispatch_ip(tx_token, packet, &mut self.fragmenter) + { net_debug!("Failed to send response: {:?}", err); } } @@ -948,20 +917,8 @@ impl Interface { EgressError::Exhausted })?; - #[cfg(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - ))] inner - .dispatch_ip(t, response, Some(&mut self.out_packets)) - .map_err(EgressError::Dispatch)?; - - #[cfg(not(any( - feature = "proto-ipv4-fragmentation", - feature = "proto-sixlowpan-fragmentation" - )))] - inner - .dispatch_ip(t, response, None) + .dispatch_ip(t, response, &mut self.fragmenter) .map_err(EgressError::Dispatch)?; emitted_any = true; @@ -1036,19 +993,19 @@ impl Interface { D: Device + ?Sized, { // Reset the buffer when we transmitted everything. - if self.out_packets.ipv4_out_packet.finished() { - self.out_packets.ipv4_out_packet.reset(); + if self.fragmenter.finished() { + self.fragmenter.reset(); } - if self.out_packets.ipv4_out_packet.is_empty() { + if self.fragmenter.is_empty() { return false; } - let pkt = &self.out_packets.ipv4_out_packet; + let pkt = &self.fragmenter; if pkt.packet_len > pkt.sent_bytes { if let Some(tx_token) = device.transmit(self.inner.now) { self.inner - .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet); + .dispatch_ipv4_frag(tx_token, &mut self.fragmenter); return true; } } @@ -1066,21 +1023,19 @@ impl Interface { D: Device + ?Sized, { // Reset the buffer when we transmitted everything. - if self.out_packets.sixlowpan_out_packet.finished() { - self.out_packets.sixlowpan_out_packet.reset(); + if self.fragmenter.finished() { + self.fragmenter.reset(); } - if self.out_packets.sixlowpan_out_packet.is_empty() { + if self.fragmenter.is_empty() { return false; } - let pkt = &self.out_packets.sixlowpan_out_packet; + let pkt = &self.fragmenter; if pkt.packet_len > pkt.sent_bytes { if let Some(tx_token) = device.transmit(self.inner.now) { - self.inner.dispatch_ieee802154_out_packet( - tx_token, - &mut self.out_packets.sixlowpan_out_packet, - ); + self.inner + .dispatch_ieee802154_frag(tx_token, &mut self.fragmenter); return true; } } @@ -1509,7 +1464,7 @@ impl InterfaceInner { &mut self, tx_token: Tx, packet: EthernetPacket, - _out_packet: Option<&mut OutPackets>, + frag: &mut Fragmenter, ) -> Result<(), DispatchError> where Tx: TxToken, @@ -1532,7 +1487,7 @@ impl InterfaceInner { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, _out_packet), + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, frag), } } @@ -1580,6 +1535,7 @@ impl InterfaceInner { tx_token: Tx, src_addr: &IpAddress, dst_addr: &IpAddress, + fragmenter: &mut Fragmenter, ) -> Result<(HardwareAddress, Tx), DispatchError> where Tx: TxToken, @@ -1698,7 +1654,7 @@ impl InterfaceInner { solicit, )); - if let Err(e) = self.dispatch_ip(tx_token, packet, None) { + if let Err(e) = self.dispatch_ip(tx_token, packet, fragmenter) { net_debug!("Failed to dispatch NDISC solicit: {:?}", e); return Err(DispatchError::NeighborPending); } @@ -1724,7 +1680,7 @@ impl InterfaceInner { &mut self, tx_token: Tx, packet: IpPacket, - _out_packet: Option<&mut OutPackets>, + frag: &mut Fragmenter, ) -> Result<(), DispatchError> { let ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); @@ -1733,11 +1689,15 @@ impl InterfaceInner { #[cfg(feature = "medium-ieee802154")] if matches!(self.caps.medium, Medium::Ieee802154) { - let (addr, tx_token) = - self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())?; + let (addr, tx_token) = self.lookup_hardware_addr( + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + frag, + )?; let addr = addr.ieee802154_or_panic(); - self.dispatch_ieee802154(addr, tx_token, packet, _out_packet); + self.dispatch_ieee802154(addr, tx_token, packet, frag); return Ok(()); } @@ -1765,6 +1725,7 @@ impl InterfaceInner { tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr(), + frag, )? { (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), #[cfg(feature = "medium-ieee802154")] @@ -1812,15 +1773,13 @@ impl InterfaceInner { { net_debug!("start fragmentation"); - let pkt = &mut _out_packet.unwrap().ipv4_out_packet; - // Calculate how much we will send now (including the Ethernet header). let tx_len = self.caps.max_transmission_unit; let ip_header_len = repr.buffer_len(); let first_frag_ip_len = self.caps.ip_mtu(); - if pkt.buffer.len() < first_frag_ip_len { + if frag.buffer.len() < first_frag_ip_len { net_debug!( "Fragmentation buffer is too small, at least {} needed. Dropping", first_frag_ip_len @@ -1830,26 +1789,26 @@ impl InterfaceInner { #[cfg(feature = "medium-ethernet")] { - pkt.dst_hardware_addr = dst_hardware_addr; + frag.ipv4.dst_hardware_addr = dst_hardware_addr; } // Save the total packet len (without the Ethernet header, but with the first // IP header). - pkt.packet_len = total_ip_len; + frag.packet_len = total_ip_len; // Save the IP header for other fragments. - pkt.repr = repr; + frag.ipv4.repr = repr; // Save how much bytes we will send now. - pkt.sent_bytes = first_frag_ip_len; + frag.sent_bytes = first_frag_ip_len; // Modify the IP header repr.payload_len = first_frag_ip_len - repr.buffer_len(); // Emit the IP header to the buffer. - emit_ip(&ip_repr, &mut pkt.buffer); - let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut pkt.buffer[..]); - pkt.ident = ipv4_id; + emit_ip(&ip_repr, &mut frag.buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]); + frag.ipv4.ident = ipv4_id; ipv4_packet.set_ident(ipv4_id); ipv4_packet.set_more_frags(true); ipv4_packet.set_dont_frag(false); @@ -1868,11 +1827,11 @@ impl InterfaceInner { } // Change the offset for the next packet. - pkt.frag_offset = (first_frag_ip_len - ip_header_len) as u16; + frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16; // Copy the IP header and the payload. tx_buffer[..first_frag_ip_len] - .copy_from_slice(&pkt.buffer[..first_frag_ip_len]); + .copy_from_slice(&frag.buffer[..first_frag_ip_len]); Ok(()) }) diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index e2db1054c..2eb5128da 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -1,12 +1,4 @@ -use super::check; -use super::FragmentsBuffer; -use super::InterfaceInner; -use super::IpPacket; -use super::OutPackets; -use super::SocketSet; - -#[cfg(feature = "proto-sixlowpan-fragmentation")] -use super::SixlowpanOutPacket; +use super::*; use crate::phy::ChecksumCapabilities; use crate::phy::TxToken; @@ -98,7 +90,6 @@ impl InterfaceInner { f: &'output mut FragmentsBuffer, ) -> Option<&'output [u8]> { use crate::iface::fragmentation::{AssemblerError, AssemblerFullError}; - use crate::iface::interface::FragKey; // We have a fragment header, which means we cannot process the 6LoWPAN packet, // unless we have a complete one after processing this fragment. @@ -275,7 +266,7 @@ impl InterfaceInner { ll_dst_a: Ieee802154Address, tx_token: Tx, packet: IpPacket, - _out_packet: Option<&mut OutPackets>, + frag: &mut Fragmenter, ) { // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to @@ -365,14 +356,14 @@ impl InterfaceInner { #[cfg(feature = "proto-sixlowpan-fragmentation")] { // The packet does not fit in one Ieee802154 frame, so we need fragmentation. - // We do this by emitting everything in the `out_packet.buffer` from the interface. + // We do this by emitting everything in the `frag.buffer` from the interface. // After emitting everything into that buffer, we send the first fragment heere. - // When `poll` is called again, we check if out_packet was fully sent, otherwise we - // call `dispatch_ieee802154_out_packet`, which will transmit the other fragments. + // When `poll` is called again, we check if frag was fully sent, otherwise we + // call `dispatch_ieee802154_frag`, which will transmit the other fragments. - // `dispatch_ieee802154_out_packet` requires some information about the total packet size, + // `dispatch_ieee802154_frag` requires some information about the total packet size, // the link local source and destination address... - let pkt = &mut _out_packet.unwrap().sixlowpan_out_packet; + let pkt = frag; if pkt.buffer.len() < total_size { net_debug!( @@ -382,8 +373,8 @@ impl InterfaceInner { return; } - pkt.ll_dst_addr = ll_dst_a; - pkt.ll_src_addr = ll_src_a; + pkt.sixlowpan.ll_dst_addr = ll_dst_a; + pkt.sixlowpan.ll_src_addr = ll_src_a; let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut pkt.buffer[..iphc_repr.buffer_len()]); @@ -436,20 +427,20 @@ impl InterfaceInner { // The datagram size that we need to set in the first fragment header is equal to the // IPv6 payload length + 40. - pkt.datagram_size = (packet.ip_repr().payload_len() + 40) as u16; + pkt.sixlowpan.datagram_size = (packet.ip_repr().payload_len() + 40) as u16; // We generate a random tag. let tag = self.get_sixlowpan_fragment_tag(); // We save the tag for the other fragments that will be created when calling `poll` // multiple times. - pkt.datagram_tag = tag; + pkt.sixlowpan.datagram_tag = tag; let frag1 = SixlowpanFragRepr::FirstFragment { - size: pkt.datagram_size, + size: pkt.sixlowpan.datagram_size, tag, }; let fragn = SixlowpanFragRepr::Fragment { - size: pkt.datagram_size, + size: pkt.sixlowpan.datagram_size, tag, offset: 0, }; @@ -465,10 +456,10 @@ impl InterfaceInner { let frag1_size = (125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - (header_diff); - pkt.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; + pkt.sixlowpan.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8; pkt.sent_bytes = frag1_size; - pkt.datagram_offset = frag1_size + header_diff; + pkt.sixlowpan.datagram_offset = frag1_size + header_diff; tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| { // Add the IEEE header. @@ -553,10 +544,10 @@ impl InterfaceInner { feature = "medium-ieee802154", feature = "proto-sixlowpan-fragmentation" ))] - pub(super) fn dispatch_ieee802154_out_packet( + pub(super) fn dispatch_ieee802154_frag( &mut self, tx_token: Tx, - pkt: &mut SixlowpanOutPacket, + frag: &mut Fragmenter, ) { // Create the IEEE802.15.4 header. let ieee_repr = Ieee802154Repr { @@ -568,20 +559,20 @@ impl InterfaceInner { pan_id_compression: true, frame_version: Ieee802154FrameVersion::Ieee802154_2003, dst_pan_id: self.pan_id, - dst_addr: Some(pkt.ll_dst_addr), + dst_addr: Some(frag.sixlowpan.ll_dst_addr), src_pan_id: self.pan_id, - src_addr: Some(pkt.ll_src_addr), + src_addr: Some(frag.sixlowpan.ll_src_addr), }; // Create the FRAG_N header. let fragn = SixlowpanFragRepr::Fragment { - size: pkt.datagram_size, - tag: pkt.datagram_tag, - offset: (pkt.datagram_offset / 8) as u8, + size: frag.sixlowpan.datagram_size, + tag: frag.sixlowpan.datagram_tag, + offset: (frag.sixlowpan.datagram_offset / 8) as u8, }; let ieee_len = ieee_repr.buffer_len(); - let frag_size = (pkt.packet_len - pkt.sent_bytes).min(pkt.fragn_size); + let frag_size = (frag.packet_len - frag.sent_bytes).min(frag.sixlowpan.fragn_size); tx_token.consume( ieee_repr.buffer_len() + fragn.buffer_len() + frag_size, @@ -596,10 +587,10 @@ impl InterfaceInner { tx_buf = &mut tx_buf[fragn.buffer_len()..]; // Add the buffer part - tx_buf[..frag_size].copy_from_slice(&pkt.buffer[pkt.sent_bytes..][..frag_size]); + tx_buf[..frag_size].copy_from_slice(&frag.buffer[frag.sent_bytes..][..frag_size]); - pkt.sent_bytes += frag_size; - pkt.datagram_offset += frag_size; + frag.sent_bytes += frag_size; + frag.sixlowpan.datagram_offset += frag_size; }, ); } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 8488ce09e..e97a5c7c2 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -749,7 +749,8 @@ fn test_handle_valid_arp_request() { iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr) + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, ), Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); @@ -821,7 +822,8 @@ fn test_handle_valid_ndisc_request() { iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv6(local_ip_addr), - &IpAddress::Ipv6(remote_ip_addr) + &IpAddress::Ipv6(remote_ip_addr), + &mut iface.fragmenter, ), Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); @@ -865,7 +867,8 @@ fn test_handle_other_arp_request() { iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr) + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, ), Err(DispatchError::NeighborPending) ); @@ -923,7 +926,8 @@ fn test_arp_flush_after_update_ip() { iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr) + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, ), Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); @@ -1527,7 +1531,7 @@ fn test_echo_request_sixlowpan_128_bytes() { Ieee802154Address::default(), tx_token, result.unwrap(), - Some(&mut iface.out_packets), + &mut iface.fragmenter, ); assert_eq!( @@ -1677,7 +1681,7 @@ fn test_sixlowpan_udp_with_fragmentation() { }, udp_data, )), - Some(&mut iface.out_packets), + &mut iface.fragmenter, ); iface.poll(Instant::now(), &mut device, &mut sockets); From a656ab0c08c3cb6d4bdf214873469d629d156b5d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Feb 2023 16:56:20 +0100 Subject: [PATCH 503/566] iface: use reassembly timeout setting for both ipv4 and 6lowpan. --- src/iface/interface/ethernet.rs | 9 ++----- src/iface/interface/ipv4.rs | 8 +++--- src/iface/interface/mod.rs | 29 +++++++------------- src/iface/interface/sixlowpan.rs | 5 +--- src/iface/interface/tests.rs | 45 +++++++++++--------------------- 5 files changed, 31 insertions(+), 65 deletions(-) diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index 72eaf290c..b769909ce 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -34,13 +34,8 @@ impl InterfaceInner { EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - self.process_ipv4( - sockets, - &ipv4_packet, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut fragments.assembler, - ) - .map(EthernetPacket::Ip) + self.process_ipv4(sockets, &ipv4_packet, fragments) + .map(EthernetPacket::Ip) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 8b76407a0..bd1d6e1de 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -7,7 +7,7 @@ use crate::socket::icmp; use crate::socket::AnySocket; use crate::phy::{Medium, TxToken}; -use crate::time::{Duration, Instant}; +use crate::time::Instant; use crate::wire::*; impl InterfaceInner { @@ -15,7 +15,7 @@ impl InterfaceInner { &mut self, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'a T>, - #[cfg(feature = "proto-ipv4-fragmentation")] assembler: &'a mut PacketAssemblerSet, + frag: &'a mut FragmentsBuffer, ) -> Option> { let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); if !self.is_unicast_v4(ipv4_repr.src_addr) { @@ -26,12 +26,10 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv4-fragmentation")] let ip_payload = { - const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(90); - if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 { let key = FragKey::Ipv4(ipv4_packet.get_key()); - let f = match assembler.get(&key, self.now + REASSEMBLY_TIMEOUT) { + let f = match frag.assembler.get(&key, self.now + frag.reassembly_timeout) { Ok(f) => f, Err(_) => { net_debug!("No available packet assembler for fragmented packet"); diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index cb680c519..8b1d9f315 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -60,8 +60,8 @@ pub(crate) struct FragmentsBuffer { #[cfg(feature = "_proto-fragmentation")] pub(crate) assembler: PacketAssemblerSet, - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_timeout: Duration, + #[cfg(feature = "_proto-fragmentation")] + reassembly_timeout: Duration, } #[cfg(not(feature = "_proto-fragmentation"))] @@ -538,8 +538,8 @@ impl Interface { #[cfg(feature = "_proto-fragmentation")] assembler: PacketAssemblerSet::new(), - #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_reassembly_timeout: Duration::from_secs(60), + #[cfg(feature = "_proto-fragmentation")] + reassembly_timeout: Duration::from_secs(60), }, fragmenter: Fragmenter::new(), inner: InterfaceInner { @@ -703,22 +703,18 @@ impl Interface { } /// Get the packet reassembly timeout. - /// - /// Currently used only for 6LoWPAN, will be used for IPv4 in the future as well. - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(feature = "_proto-fragmentation")] pub fn reassembly_timeout(&self) -> Duration { - self.fragments.sixlowpan_reassembly_timeout + self.fragments.reassembly_timeout } /// Set the packet reassembly timeout. - /// - /// Currently used only for 6LoWPAN, will be used for IPv4 in the future as well. - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(feature = "_proto-fragmentation")] pub fn set_reassembly_timeout(&mut self, timeout: Duration) { if timeout > Duration::from_secs(60) { net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"); } - self.fragments.sixlowpan_reassembly_timeout = timeout; + self.fragments.reassembly_timeout = timeout; } /// Transmit packets queued in the given sockets, and receive packets queued @@ -1288,19 +1284,14 @@ impl InterfaceInner { &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, - fragments: &'frame mut FragmentsBuffer, + frag: &'frame mut FragmentsBuffer, ) -> Option> { match IpVersion::of_packet(ip_payload.as_ref()) { #[cfg(feature = "proto-ipv4")] Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - self.process_ipv4( - sockets, - &ipv4_packet, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut fragments.assembler, - ) + self.process_ipv4(sockets, &ipv4_packet, frag) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 2eb5128da..767d0a446 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -107,10 +107,7 @@ impl InterfaceInner { // This information is the total size of the packet when it is fully assmbled. // We also pass the header size, since this is needed when other fragments // (other than the first one) are added. - let frag_slot = match f - .assembler - .get(&key, self.now + f.sixlowpan_reassembly_timeout) - { + let frag_slot = match f.assembler.get(&key, self.now + f.reassembly_timeout) { Ok(frag) => frag, Err(AssemblerFullError) => { net_debug!("No available packet assembler for fragmented packet"); diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index e97a5c7c2..ef4f9d8cb 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -168,12 +168,9 @@ fn test_no_icmp_no_unicast_ipv4() { // broadcast address assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut iface.fragments.assembler - ), + iface + .inner + .process_ipv4(&mut sockets, &frame, &mut iface.fragments), None ); } @@ -254,12 +251,9 @@ fn test_icmp_error_no_payload() { // And we correctly handle no payload. assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut iface.fragments.assembler - ), + iface + .inner + .process_ipv4(&mut sockets, &frame, &mut iface.fragments), Some(expected_repr) ); } @@ -564,12 +558,9 @@ fn test_handle_ipv4_broadcast() { let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut iface.fragments.assembler - ), + iface + .inner + .process_ipv4(&mut sockets, &frame, &mut iface.fragments), Some(expected_packet) ); } @@ -1268,12 +1259,9 @@ fn test_raw_socket_no_reply() { }; assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut iface.fragments.assembler - ), + iface + .inner + .process_ipv4(&mut sockets, &frame, &mut iface.fragments), None ); } @@ -1357,12 +1345,9 @@ fn test_raw_socket_with_udp_socket() { }; assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - &frame, - #[cfg(feature = "proto-ipv4-fragmentation")] - &mut iface.fragments.assembler - ), + iface + .inner + .process_ipv4(&mut sockets, &frame, &mut iface.fragments), None ); From b047cbeade409203b0d0eaffb4dfe55bdd5dc8fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Feb 2023 19:48:48 +0100 Subject: [PATCH 504/566] Add compile-time configuration options for counts and buffer sizes. --- Cargo.toml | 135 +++++++++++++++++++++++++++++++++++ README.md | 71 +++++++++++++++++- build.rs | 101 ++++++++++++++++++++++++++ gen_config.py | 83 +++++++++++++++++++++ src/iface/fragmentation.rs | 31 +++----- src/iface/interface/mod.rs | 24 +++---- src/iface/interface/tests.rs | 2 +- src/iface/neighbor.rs | 8 +-- src/iface/route.rs | 9 ++- src/lib.rs | 23 ++++++ src/socket/dns.rs | 15 ++-- src/storage/assembler.rs | 49 +++++-------- 12 files changed, 466 insertions(+), 85 deletions(-) create mode 100644 build.rs create mode 100644 gen_config.py diff --git a/Cargo.toml b/Cargo.toml index 86a66378f..c8d670ed1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,141 @@ default = [ "_proto-fragmentation" = [] +# BEGIN AUTOGENERATED CONFIG FEATURES +# Generated by gen_config.py. DO NOT EDIT. +iface-max-addr-count-1 = [] +iface-max-addr-count-2 = [] # Default +iface-max-addr-count-3 = [] +iface-max-addr-count-4 = [] +iface-max-addr-count-5 = [] +iface-max-addr-count-6 = [] +iface-max-addr-count-7 = [] +iface-max-addr-count-8 = [] + +iface-max-multicast-group-count-1 = [] +iface-max-multicast-group-count-2 = [] +iface-max-multicast-group-count-3 = [] +iface-max-multicast-group-count-4 = [] # Default +iface-max-multicast-group-count-5 = [] +iface-max-multicast-group-count-6 = [] +iface-max-multicast-group-count-7 = [] +iface-max-multicast-group-count-8 = [] +iface-max-multicast-group-count-16 = [] +iface-max-multicast-group-count-32 = [] +iface-max-multicast-group-count-64 = [] +iface-max-multicast-group-count-128 = [] +iface-max-multicast-group-count-256 = [] +iface-max-multicast-group-count-512 = [] +iface-max-multicast-group-count-1024 = [] + +iface-max-sixlowpan-address-context-count-1 = [] +iface-max-sixlowpan-address-context-count-2 = [] +iface-max-sixlowpan-address-context-count-3 = [] +iface-max-sixlowpan-address-context-count-4 = [] # Default +iface-max-sixlowpan-address-context-count-5 = [] +iface-max-sixlowpan-address-context-count-6 = [] +iface-max-sixlowpan-address-context-count-7 = [] +iface-max-sixlowpan-address-context-count-8 = [] +iface-max-sixlowpan-address-context-count-16 = [] +iface-max-sixlowpan-address-context-count-32 = [] +iface-max-sixlowpan-address-context-count-64 = [] +iface-max-sixlowpan-address-context-count-128 = [] +iface-max-sixlowpan-address-context-count-256 = [] +iface-max-sixlowpan-address-context-count-512 = [] +iface-max-sixlowpan-address-context-count-1024 = [] + +iface-neighbor-cache-count-1 = [] +iface-neighbor-cache-count-2 = [] +iface-neighbor-cache-count-3 = [] +iface-neighbor-cache-count-4 = [] # Default +iface-neighbor-cache-count-5 = [] +iface-neighbor-cache-count-6 = [] +iface-neighbor-cache-count-7 = [] +iface-neighbor-cache-count-8 = [] +iface-neighbor-cache-count-16 = [] +iface-neighbor-cache-count-32 = [] +iface-neighbor-cache-count-64 = [] +iface-neighbor-cache-count-128 = [] +iface-neighbor-cache-count-256 = [] +iface-neighbor-cache-count-512 = [] +iface-neighbor-cache-count-1024 = [] + +iface-max-route-count-1 = [] +iface-max-route-count-2 = [] # Default +iface-max-route-count-3 = [] +iface-max-route-count-4 = [] +iface-max-route-count-5 = [] +iface-max-route-count-6 = [] +iface-max-route-count-7 = [] +iface-max-route-count-8 = [] +iface-max-route-count-16 = [] +iface-max-route-count-32 = [] +iface-max-route-count-64 = [] +iface-max-route-count-128 = [] +iface-max-route-count-256 = [] +iface-max-route-count-512 = [] +iface-max-route-count-1024 = [] + +fragmentation-buffer-size-256 = [] +fragmentation-buffer-size-512 = [] +fragmentation-buffer-size-1024 = [] +fragmentation-buffer-size-1500 = [] # Default +fragmentation-buffer-size-2048 = [] +fragmentation-buffer-size-4096 = [] +fragmentation-buffer-size-8192 = [] +fragmentation-buffer-size-16384 = [] +fragmentation-buffer-size-32768 = [] +fragmentation-buffer-size-65536 = [] + +assembler-max-segment-count-1 = [] +assembler-max-segment-count-2 = [] +assembler-max-segment-count-3 = [] +assembler-max-segment-count-4 = [] # Default +assembler-max-segment-count-8 = [] +assembler-max-segment-count-16 = [] +assembler-max-segment-count-32 = [] + +reassembly-buffer-size-256 = [] +reassembly-buffer-size-512 = [] +reassembly-buffer-size-1024 = [] +reassembly-buffer-size-1500 = [] # Default +reassembly-buffer-size-2048 = [] +reassembly-buffer-size-4096 = [] +reassembly-buffer-size-8192 = [] +reassembly-buffer-size-16384 = [] +reassembly-buffer-size-32768 = [] +reassembly-buffer-size-65536 = [] + +reassembly-buffer-count-1 = [] # Default +reassembly-buffer-count-2 = [] +reassembly-buffer-count-3 = [] +reassembly-buffer-count-4 = [] +reassembly-buffer-count-8 = [] +reassembly-buffer-count-16 = [] +reassembly-buffer-count-32 = [] + +dns-max-result-count-1 = [] # Default +dns-max-result-count-2 = [] +dns-max-result-count-3 = [] +dns-max-result-count-4 = [] +dns-max-result-count-8 = [] +dns-max-result-count-16 = [] +dns-max-result-count-32 = [] + +dns-max-server-count-1 = [] # Default +dns-max-server-count-2 = [] +dns-max-server-count-3 = [] +dns-max-server-count-4 = [] +dns-max-server-count-8 = [] +dns-max-server-count-16 = [] +dns-max-server-count-32 = [] + +dns-max-name-size-64 = [] +dns-max-name-size-128 = [] +dns-max-name-size-255 = [] # Default + +# END AUTOGENERATED CONFIG FEATURES + [[example]] name = "packet2pcap" path = "utils/packet2pcap.rs" diff --git a/README.md b/README.md index faede60cf..437afca2c 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ You probably want to disable default features and configure them one by one: smoltcp = { version = "0.8.0", default-features = false, features = ["log"] } ``` +## Feature flags + ### Feature `std` The `std` feature enables use of objects and slices owned by the networking stack through a @@ -195,7 +197,7 @@ Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respective These features are enabled by default. -### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4` +### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4`, `socket-dns` Enable the corresponding socket type. @@ -208,6 +210,73 @@ Enable [IPv4] and [IPv6] respectively. [IPv4]: https://tools.ietf.org/rfc/rfc791.txt [IPv6]: https://tools.ietf.org/rfc/rfc8200.txt +## Configuration + +_smoltcp_ has some configuration settings that are set at compile time, affecting sizes +and counts of buffers. + +They can be set in two ways: + +- Via Cargo features: enable a feature like `-`. `name` must be in lowercase and +use dashes instead of underscores. For example. `iface-max-addr-count-3`. Only a selection of values +is available, check `Cargo.toml` for the list. +- Via environment variables at build time: set the variable named `SMOLTCP_`. For example +`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. +Any value can be set, unlike with Cargo features. + +Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting +with different values, compilation fails. + +### IFACE_MAX_ADDR_COUNT + +Max amount of IP addresses that can be assigned to one interface (counting both IPv4 and IPv6 addresses). Default: 2. + +### IFACE_MAX_MULTICAST_GROUP_COUNT + +Max amount of multicast groups that can be joined by one interface. Default: 4. + +### IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT + +Max amount of 6LoWPAN address contexts that can be assigned to one interface. Default: 4. + +### IFACE_NEIGHBOR_CACHE_COUNT + +Amount of "IP address -> hardware address" entries the neighbor cache (also known as the "ARP cache" or the "ARP table") holds. Default: 4. + +### IFACE_MAX_ROUTE_COUNT + +Max amount of routes that can be added to one interface. Includes the default route. Includes both IPv4 and IPv6. Default: 2. + +### FRAGMENTATION_BUFFER_SIZE + +Size of the buffer used for fragmenting outgoing packets larger than the MTU. Packets larger than this setting will be dropped instead of fragmented. Default: 1500. + +### ASSEMBLER_MAX_SEGMENT_COUNT + +Maximum number of non-contiguous segments the assembler can hold. Used for both packet reassembly and TCP stream reassembly. Default: 4. + +### REASSEMBLY_BUFFER_SIZE + +Size of the buffer used for reassembling (de-fragmenting) incoming packets. If the reassembled packet is larger than this setting, it will be dropped instead of reassembled. Default: 1500. + +### REASSEMBLY_BUFFER_COUNT + +Number of reassembly buffers, i.e how many different incoming packets can be reassembled at the same time. Default: 1. + +### DNS_MAX_RESULT_COUNT + +Maximum amount of address results for a given DNS query that will be kept. For example, if this is set to 2 and the queried name has 4 `A` records, only the first 2 will be returned. Default: 1. + +### DNS_MAX_SERVER_COUNT + +Maximum amount of DNS servers that can be configured in one DNS socket. Default: 1. + +### DNS_MAX_NAME_SIZE + +Maximum length of DNS names that can be queried. Default: 255. + + + ## Hosted usage examples _smoltcp_, being a freestanding networking stack, needs to be able to transmit and receive diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..760ee189e --- /dev/null +++ b/build.rs @@ -0,0 +1,101 @@ +use std::collections::HashMap; +use std::fmt::Write; +use std::path::PathBuf; +use std::{env, fs}; + +static CONFIGS: &[(&str, usize)] = &[ + // BEGIN AUTOGENERATED CONFIG FEATURES + // Generated by gen_config.py. DO NOT EDIT. + ("IFACE_MAX_ADDR_COUNT", 2), + ("IFACE_MAX_MULTICAST_GROUP_COUNT", 4), + ("IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT", 4), + ("IFACE_NEIGHBOR_CACHE_COUNT", 4), + ("IFACE_MAX_ROUTE_COUNT", 2), + ("FRAGMENTATION_BUFFER_SIZE", 1500), + ("ASSEMBLER_MAX_SEGMENT_COUNT", 4), + ("REASSEMBLY_BUFFER_SIZE", 1500), + ("REASSEMBLY_BUFFER_COUNT", 1), + ("DNS_MAX_RESULT_COUNT", 1), + ("DNS_MAX_SERVER_COUNT", 1), + ("DNS_MAX_NAME_SIZE", 255), + // END AUTOGENERATED CONFIG FEATURES +]; + +struct ConfigState { + value: usize, + seen_feature: bool, + seen_env: bool, +} + +fn main() { + // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any + // other file changed. + println!("cargo:rerun-if-changed=build.rs"); + + // Rebuild if config envvar changed. + for (name, _) in CONFIGS { + println!("cargo:rerun-if-env-changed=SMOLTCP_{name}"); + } + + let mut configs = HashMap::new(); + for (name, default) in CONFIGS { + configs.insert( + *name, + ConfigState { + value: *default, + seen_env: false, + seen_feature: false, + }, + ); + } + + for (var, value) in env::vars() { + if let Some(name) = var.strip_prefix("SMOLTCP_") { + let Some(cfg) = configs.get_mut(name) else { + panic!("Unknown env var {name}") + }; + + let Ok(value) = value.parse::() else { + panic!("Invalid value for env var {name}: {value}") + }; + + cfg.value = value; + cfg.seen_env = true; + } + + if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { + if let Some(i) = feature.rfind('_') { + let name = &feature[..i]; + let value = &feature[i + 1..]; + if let Some(cfg) = configs.get_mut(name) { + let Ok(value) = value.parse::() else { + panic!("Invalid value for feature {name}: {value}") + }; + + // envvars take priority. + if !cfg.seen_env { + if cfg.seen_feature { + panic!( + "multiple values set for feature {}: {} and {}", + name, cfg.value, value + ); + } + + cfg.value = value; + cfg.seen_feature = true; + } + } + } + } + } + + let mut data = String::new(); + + for (name, cfg) in &configs { + writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); + } + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); + fs::write(out_file, data).unwrap(); +} diff --git a/gen_config.py b/gen_config.py new file mode 100644 index 000000000..83d587380 --- /dev/null +++ b/gen_config.py @@ -0,0 +1,83 @@ +import os + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +features = [] + + +def feature(name, default, min, max, pow2=None): + vals = set() + val = min + while val <= max: + vals.add(val) + if pow2 == True or (isinstance(pow2, int) and val >= pow2): + val *= 2 + else: + val += 1 + vals.add(default) + + features.append( + { + "name": name, + "default": default, + "vals": sorted(list(vals)), + } + ) + + +feature("iface_max_addr_count", default=2, min=1, max=8) +feature("iface_max_multicast_group_count", default=4, min=1, max=1024, pow2=8) +feature("iface_max_sixlowpan_address_context_count", default=4, min=1, max=1024, pow2=8) +feature("iface_neighbor_cache_count", default=4, min=1, max=1024, pow2=8) +feature("iface_max_route_count", default=2, min=1, max=1024, pow2=8) +feature("fragmentation_buffer_size", default=1500, min=256, max=65536, pow2=True) +feature("assembler_max_segment_count", default=4, min=1, max=32, pow2=4) +feature("reassembly_buffer_size", default=1500, min=256, max=65536, pow2=True) +feature("reassembly_buffer_count", default=1, min=1, max=32, pow2=4) +feature("dns_max_result_count", default=1, min=1, max=32, pow2=4) +feature("dns_max_server_count", default=1, min=1, max=32, pow2=4) +feature("dns_max_name_size", default=255, min=64, max=255, pow2=True) + +# ========= Update Cargo.toml + +things = "" +for f in features: + name = f["name"].replace("_", "-") + for val in f["vals"]: + things += f"{name}-{val} = []" + if val == f["default"]: + things += " # Default" + things += "\n" + things += "\n" + +SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" +HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" +with open("Cargo.toml", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after +with open("Cargo.toml", "w") as f: + f.write(data) + + +# ========= Update build.rs + +things = "" +for f in features: + name = f["name"].upper() + things += f' ("{name}", {f["default"]}),\n' + +SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" +HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" +with open("build.rs", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + things + " " + SEPARATOR_END + after +with open("build.rs", "w") as f: + f.write(data) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index a02d815c7..ace0a22d2 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -4,18 +4,14 @@ use core::fmt; use managed::{ManagedMap, ManagedSlice}; +use crate::config::{REASSEMBLY_BUFFER_COUNT, REASSEMBLY_BUFFER_SIZE}; use crate::storage::Assembler; use crate::time::{Duration, Instant}; -// TODO: make configurable. -const BUFFER_SIZE: usize = 1500; - #[cfg(feature = "alloc")] type Buffer = alloc::vec::Vec; #[cfg(not(feature = "alloc"))] -type Buffer = [u8; BUFFER_SIZE]; - -const PACKET_ASSEMBLER_COUNT: usize = 4; +type Buffer = [u8; REASSEMBLY_BUFFER_SIZE]; /// Problem when assembling: something was out of bounds. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -44,14 +40,14 @@ pub struct PacketAssembler { impl PacketAssembler { /// Create a new empty buffer for fragments. - pub fn new() -> Self { + pub const fn new() -> Self { Self { key: None, #[cfg(feature = "alloc")] buffer: Buffer::new(), #[cfg(not(feature = "alloc"))] - buffer: [0u8; BUFFER_SIZE], + buffer: [0u8; REASSEMBLY_BUFFER_SIZE], assembler: Assembler::new(), total_size: None, @@ -172,20 +168,16 @@ impl PacketAssembler { /// Set holding multiple [`PacketAssembler`]. #[derive(Debug)] pub struct PacketAssemblerSet { - assemblers: [PacketAssembler; PACKET_ASSEMBLER_COUNT], + assemblers: [PacketAssembler; REASSEMBLY_BUFFER_COUNT], } impl PacketAssemblerSet { + const NEW_PA: PacketAssembler = PacketAssembler::new(); + /// Create a new set of packet assemblers. pub fn new() -> Self { Self { - // TODO: support any PACKET_ASSEMBLER_COUNT - assemblers: [ - PacketAssembler::new(), - PacketAssembler::new(), - PacketAssembler::new(), - PacketAssembler::new(), - ], + assemblers: [Self::NEW_PA; REASSEMBLY_BUFFER_COUNT], } } @@ -291,10 +283,9 @@ mod tests { #[test] fn packet_assembler_set_full() { let mut set = PacketAssemblerSet::new(); - set.get(&Key { id: 0 }, Instant::ZERO).unwrap(); - set.get(&Key { id: 1 }, Instant::ZERO).unwrap(); - set.get(&Key { id: 2 }, Instant::ZERO).unwrap(); - set.get(&Key { id: 3 }, Instant::ZERO).unwrap(); + for i in 0..REASSEMBLY_BUFFER_COUNT { + set.get(&Key { id: i }, Instant::ZERO).unwrap(); + } assert!(set.get(&Key { id: 4 }, Instant::ZERO).is_err()); } diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 8b1d9f315..ea937c7e2 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -27,6 +27,10 @@ use super::fragmentation::PacketAssemblerSet; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache}; use super::socket_set::SocketSet; +use crate::config::{ + FRAGMENTATION_BUFFER_SIZE, IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT, + IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT, +}; use crate::iface::Routes; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::rand::Rand; @@ -36,13 +40,6 @@ use crate::socket::*; use crate::time::{Duration, Instant}; use crate::wire::*; -const MAX_IP_ADDR_COUNT: usize = 5; -#[cfg(feature = "proto-igmp")] -const MAX_IPV4_MULTICAST_GROUPS: usize = 4; -const FRAGMENTATION_BUFFER_SIZE: usize = 1500; -#[cfg(feature = "proto-sixlowpan")] -const SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4; - #[cfg(feature = "_proto-fragmentation")] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -248,15 +245,16 @@ pub struct InterfaceInner { #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: u16, #[cfg(feature = "proto-sixlowpan")] - sixlowpan_address_context: Vec, + sixlowpan_address_context: + Vec, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, - ip_addrs: Vec, + ip_addrs: Vec, #[cfg(feature = "proto-ipv4")] any_ip: bool, routes: Routes, #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: LinearMap, + ipv4_multicast_groups: LinearMap, /// When to report for (all or) the next multicast group membership via IGMP #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState, @@ -642,7 +640,7 @@ impl Interface { /// /// # Panics /// This function panics if any of the addresses are not unicast. - pub fn update_ip_addrs)>(&mut self, f: F) { + pub fn update_ip_addrs)>(&mut self, f: F) { f(&mut self.inner.ip_addrs); InterfaceInner::flush_cache(&mut self.inner); InterfaceInner::check_ip_addrs(&self.inner.ip_addrs) @@ -690,7 +688,7 @@ impl Interface { #[cfg(feature = "proto-sixlowpan")] pub fn sixlowpan_address_context( &self, - ) -> &Vec { + ) -> &Vec { &self.inner.sixlowpan_address_context } @@ -698,7 +696,7 @@ impl Interface { #[cfg(feature = "proto-sixlowpan")] pub fn sixlowpan_address_context_mut( &mut self, - ) -> &mut Vec { + ) -> &mut Vec { &mut self.inner.sixlowpan_address_context } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index ef4f9d8cb..cf4c655fd 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1012,7 +1012,7 @@ fn test_icmpv4_socket() { #[cfg(feature = "proto-ipv6")] fn test_solicited_node_addrs() { let (mut iface, _, _device) = create(MEDIUM); - let mut new_addrs = heapless::Vec::::new(); + let mut new_addrs = heapless::Vec::::new(); new_addrs .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) .unwrap(); diff --git a/src/iface/neighbor.rs b/src/iface/neighbor.rs index d189b7503..710454edf 100644 --- a/src/iface/neighbor.rs +++ b/src/iface/neighbor.rs @@ -3,14 +3,10 @@ use heapless::LinearMap; +use crate::config::IFACE_NEIGHBOR_CACHE_COUNT; use crate::time::{Duration, Instant}; use crate::wire::{HardwareAddress, IpAddress}; -#[cfg(not(test))] -pub const NEIGHBOR_CACHE_SIZE: usize = 16; -#[cfg(test)] -pub const NEIGHBOR_CACHE_SIZE: usize = 3; - /// A cached neighbor. /// /// A neighbor mapping translates from a protocol address to a hardware address, @@ -48,7 +44,7 @@ impl Answer { /// A neighbor cache backed by a map. #[derive(Debug)] pub struct Cache { - storage: LinearMap, + storage: LinearMap, silent_until: Instant, } diff --git a/src/iface/route.rs b/src/iface/route.rs index 2df32c951..138f987b3 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -1,14 +1,13 @@ -use crate::time::Instant; use heapless::Vec; +use crate::config::IFACE_MAX_ROUTE_COUNT; +use crate::time::Instant; use crate::wire::{IpAddress, IpCidr}; #[cfg(feature = "proto-ipv4")] use crate::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] use crate::wire::{Ipv6Address, Ipv6Cidr}; -pub const MAX_ROUTE_COUNT: usize = 4; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RouteTableFull; @@ -58,7 +57,7 @@ impl Route { /// A routing table. #[derive(Debug)] pub struct Routes { - storage: Vec, + storage: Vec, } impl Routes { @@ -70,7 +69,7 @@ impl Routes { } /// Update the routes of this node. - pub fn update)>(&mut self, f: F) { + pub fn update)>(&mut self, f: F) { f(&mut self.storage); } diff --git a/src/lib.rs b/src/lib.rs index 03d148879..ee8abdd5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,29 @@ mod macros; mod parsers; mod rand; +#[cfg(test)] +mod config { + #![allow(unused)] + pub const ASSEMBLER_MAX_SEGMENT_COUNT: usize = 4; + pub const DNS_MAX_NAME_SIZE: usize = 255; + pub const DNS_MAX_RESULT_COUNT: usize = 1; + pub const DNS_MAX_SERVER_COUNT: usize = 1; + pub const FRAGMENTATION_BUFFER_SIZE: usize = 1500; + pub const IFACE_MAX_ADDR_COUNT: usize = 8; + pub const IFACE_MAX_MULTICAST_GROUP_COUNT: usize = 4; + pub const IFACE_MAX_ROUTE_COUNT: usize = 4; + pub const IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT: usize = 4; + pub const IFACE_NEIGHBOR_CACHE_COUNT: usize = 3; + pub const REASSEMBLY_BUFFER_COUNT: usize = 4; + pub const REASSEMBLY_BUFFER_SIZE: usize = 1500; +} + +#[cfg(not(test))] +mod config { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} + #[cfg(any( feature = "medium-ethernet", feature = "medium-ip", diff --git a/src/socket/dns.rs b/src/socket/dns.rs index 2a7f3b9bd..ac21db30f 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -4,6 +4,7 @@ use core::task::Waker; use heapless::Vec; use managed::ManagedSlice; +use crate::config::{DNS_MAX_NAME_SIZE, DNS_MAX_RESULT_COUNT, DNS_MAX_SERVER_COUNT}; use crate::socket::{Context, PollAt}; use crate::time::{Duration, Instant}; use crate::wire::dns::{Flags, Opcode, Packet, Question, Rcode, Record, RecordData, Repr, Type}; @@ -12,12 +13,8 @@ use crate::wire::{self, IpAddress, IpProtocol, IpRepr, UdpRepr}; #[cfg(feature = "async")] use super::WakerRegistration; -pub const MAX_ADDRESS_COUNT: usize = 4; -pub const MAX_SERVER_COUNT: usize = 4; - const DNS_PORT: u16 = 53; const MDNS_DNS_PORT: u16 = 5353; -const MAX_NAME_LEN: usize = 255; const RETRANSMIT_DELAY: Duration = Duration::from_millis(1_000); const MAX_RETRANSMIT_DELAY: Duration = Duration::from_millis(10_000); const RETRANSMIT_TIMEOUT: Duration = Duration::from_millis(10_000); // Should generally be 2-10 secs @@ -79,7 +76,7 @@ enum State { #[derive(Debug)] struct PendingQuery { - name: Vec, + name: Vec, type_: Type, port: u16, // UDP port (src for request, dst for response) @@ -102,7 +99,7 @@ pub enum MulticastDns { #[derive(Debug)] struct CompletedQuery { - addresses: Vec, + addresses: Vec, } /// A handle to an in-progress DNS query. @@ -115,7 +112,7 @@ pub struct QueryHandle(usize); /// packet buffers. #[derive(Debug)] pub struct Socket<'a> { - servers: Vec, + servers: Vec, queries: ManagedSlice<'a, Option>, /// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets. @@ -216,7 +213,7 @@ impl<'a> Socket<'a> { name = &name[..name.len() - 1]; } - let mut raw_name: Vec = Vec::new(); + let mut raw_name: Vec = Vec::new(); let mut mdns = MulticastDns::Disabled; #[cfg(feature = "socket-mdns")] @@ -292,7 +289,7 @@ impl<'a> Socket<'a> { pub fn get_query_result( &mut self, handle: QueryHandle, - ) -> Result, GetQueryResultError> { + ) -> Result, GetQueryResultError> { let slot = &mut self.queries[handle.0]; let q = slot.as_mut().unwrap(); match &mut q.state { diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 9a997f2a8..c1348f1f0 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -1,5 +1,7 @@ use core::fmt; +use crate::config::ASSEMBLER_MAX_SEGMENT_COUNT; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TooManyHolesError; @@ -27,7 +29,7 @@ impl fmt::Display for Contig { } impl Contig { - fn empty() -> Contig { + const fn empty() -> Contig { Contig { hole_size: 0, data_size: 0, @@ -66,24 +68,13 @@ impl Contig { } } -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -const CONTIG_COUNT: usize = 32; - -#[cfg(not(feature = "alloc"))] -const CONTIG_COUNT: usize = 4; - /// A buffer (re)assembler. /// /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer. #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Assembler { - #[cfg(not(feature = "alloc"))] - contigs: [Contig; CONTIG_COUNT], - #[cfg(feature = "alloc")] - contigs: Box<[Contig; CONTIG_COUNT]>, + contigs: [Contig; ASSEMBLER_MAX_SEGMENT_COUNT], } impl fmt::Display for Assembler { @@ -106,12 +97,11 @@ impl fmt::Display for Assembler { impl Assembler { /// Create a new buffer assembler. - pub fn new() -> Assembler { - #[cfg(not(feature = "alloc"))] - let contigs = [Contig::empty(); CONTIG_COUNT]; - #[cfg(feature = "alloc")] - let contigs = Box::new([Contig::empty(); CONTIG_COUNT]); - Assembler { contigs } + pub const fn new() -> Assembler { + const EMPTY: Contig = Contig::empty(); + Assembler { + contigs: [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT], + } } pub fn clear(&mut self) { @@ -355,10 +345,9 @@ mod test { impl From> for Assembler { fn from(vec: Vec<(usize, usize)>) -> Assembler { - #[cfg(not(feature = "alloc"))] - let mut contigs = [Contig::empty(); CONTIG_COUNT]; - #[cfg(feature = "alloc")] - let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]); + const EMPTY: Contig = Contig::empty(); + + let mut contigs = [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT]; for (i, &(hole_size, data_size)) in vec.iter().enumerate() { contigs[i] = Contig { hole_size, @@ -468,7 +457,7 @@ mod test { #[test] fn test_rejected_add_keeps_state() { let mut assr = Assembler::new(); - for c in 1..=CONTIG_COUNT { + for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT { assert_eq!(assr.add(c * 10, 3), Ok(())); } // Maximum of allowed holes is reached @@ -499,12 +488,12 @@ mod test { #[test] fn test_boundary_case_remove_front() { - let mut vec = vec![(1, 1); CONTIG_COUNT]; + let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT]; vec[0] = (0, 2); let mut assr = Assembler::from(vec); assert_eq!(assr.remove_front(), 2); - let mut vec = vec![(1, 1); CONTIG_COUNT]; - vec[CONTIG_COUNT - 1] = (0, 0); + let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT]; + vec[ASSEMBLER_MAX_SEGMENT_COUNT - 1] = (0, 0); let exp_assr = Assembler::from(vec); assert_eq!(assr, exp_assr); } @@ -648,7 +637,7 @@ mod test { #[test] fn test_add_then_remove_front_at_front_full() { let mut assr = Assembler::new(); - for c in 1..=CONTIG_COUNT { + for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT { assert_eq!(assr.add(c * 10, 3), Ok(())); } // Maximum of allowed holes is reached @@ -660,7 +649,7 @@ mod test { #[test] fn test_add_then_remove_front_at_front_full_offset_0() { let mut assr = Assembler::new(); - for c in 1..=CONTIG_COUNT { + for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT { assert_eq!(assr.add(c * 10, 3), Ok(())); } assert_eq!(assr.add_then_remove_front(0, 3), Ok(3)); @@ -708,7 +697,7 @@ mod test { } // Compare. - let wanted_res = if contigs.len() > CONTIG_COUNT { + let wanted_res = if contigs.len() > ASSEMBLER_MAX_SEGMENT_COUNT { Err(TooManyHolesError) } else { Ok(()) From 20e545507693ff2f012ac8de3fdb7dd6299ae502 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 6 Feb 2023 22:52:34 +0100 Subject: [PATCH 505/566] Release v0.9.0 --- CHANGELOG.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 2 +- README.md | 28 +++++++++++++------------- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfae7e77b..53df2cb16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Remove IpRepr::Unspecified (#579) -- Remove IpVersion::Unspecified -- Remove IpAddress::Unspecified -- When sending packets with a raw socket, the source IP address is sent unmodified (it was previously replaced with the interface's address if it was unspecified). -- Fix enable `defmt/alloc` if `alloc` or `std` is enabled. +## [0.9.0] - 2023-02-06 + - Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.65 +- Added DNS client support. + - Add DnsSocket (#465) + - Add support for one-shot mDNS resolution (#669) +- Added support for packet fragmentation and reassembly, both for IPv4 and 6LoWPAN. (#591, #580, #624, #634, #645, #653, #684) +- Major error handling overhaul. + - Previously, _smoltcp_ had a single `Error` enum that all methods returned. Now methods that can fail have their own error enums, with only the actual errors they can return. (#617, #667, #730) + - Consuming `phy::Device` tokens is now infallible. + - In the case of "buffer full", `phy::Device` implementations must return `None` from the `transmit`/`receive` methods. (Previously, they could either do that, or return tokens and then return `Error::Exhausted` when consuming them. The latter wasted computation since it'd make _smoltcp_ pointlessly spend effort preparing the packet, and is now disallowed). + - For all other phy errors, `phy::Device` implementations should drop the packet and handle the error themselves. (Either log it and forget it, or buffer/count it and offer methods to let the user retrieve the error queue/counts.) Returning the error to have it bubble up to `Interface::poll()` is no longer supported. +- phy: the `trait Device` now uses Generic Associated Types (GAT) for the TX and RX tokens. The main impact of this is `Device` impls can now borrow data (because previously, the`for<'a> T: Device<'a>` bounds required to workaround the lack of GATs essentially implied `T: 'static`.) (#572) +- iface: The `Interface` API has been significantly simplified and cleaned up. + - The builder has been removed (#736) + - SocketSet and Device are now borrowed in methods that need them, instead of owning them. (#619) + - `Interface` now owns the list of addresses (#719), routes, neighbor cache (#722), 6LoWPAN address contexts, and fragmentation buffers (#736) instead of borrowing them with `managed`. + - A new compile-time configuration mechanism has been added, to configure the size of the (now owned) buffers (#742) +- iface: Change neighbor discovery timeout from 3s to 1s, to match Linux's behavior. (#620) +- iface: Remove implicit sized bound on device generics (#679) +- iface/6lowpan: Add address context information for resolving 6LoWPAN addresses (#687) +- iface/6lowpan: fix incorrect SAM value in IPHC when address is not compressed (#630) +- iface/6lowpan: packet parsing fuzz fixes (#636) +- socket: Add send_with to udp, raw, and icmp sockets. These methods enable reserving a packet buffer with a greater size than you need, and then shrinking the size once you know it. (#625) +- socket: Make `trait AnySocket` object-safe (#718) +- socket/dhcpv4: add waker support (#623) +- socket/dhcpv4: indicate new config if there's a packet buffer provided (#685) +- socket/dhcpv4: Use renewal time from DHCP server ACK, if given (#683) +- socket/dhcpv4: allow for extra configuration + - setting arbitrary options in the request. (#650) + - retrieving arbitrary options from the response. (#650) + - setting custom parameter request list. (#650) + - setting custom timing for retries. (#650) + - Allow specifying different server/client DHCP ports (#738) +- socket/raw: Add `peek` and `peek_slice` methods (#734) +- socket/raw: When sending packets, send the source IP address unmodified (it was previously replaced with the interface's address if it was unspecified). (#616) +- socket/tcp: Do not reset socket-level settings, such as keepalive, on reset (#603) +- socket/tcp: ensure we always accept the segment at offset=0 even if the assembler is full. (#735, #452) +- socket/tcp: Refactored assembler, now more robust and faster (#726, #735) +- socket/udp: accept packets with checksum field set to `0`, since that means the checksum is not computed (#632) +- wire: make many functions const (#693) +- wire/dhcpv4: remove Option enum (#656) +- wire/dhcpv4: use heapless Vec for DNS server list (#678) +- wire/icmpv4: add support for TimeExceeded packets (#609) +- wire/ip: Remove `IpRepr::Unspecified`, `IpVersion::Unspecified`, `IpAddress::Unspecified` (#579, #616) +- wire/ip: support parsing unspecified IPv6 IpEndpoints from string (like `[::]:12345`) (#732) +- wire/ipv6: Make Public Ipv6RoutingType (#691) +- wire/ndisc: do not error on unrecognized options. (#737) +- Switch to Rust 2021 edition. (#729) +- Remove obsolete Cargo feature `rust-1_28` (#725) ## [0.8.2] - 2022-11-27 @@ -152,7 +196,8 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Use #[non_exhaustive] for enums and structs ([409](https://github.com/smoltcp-rs/smoltcp/pull/409), [411](https://github.com/smoltcp-rs/smoltcp/pull/411)) - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) -[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.2...HEAD +[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.0...HEAD +[0.9.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.2...v0.9.0 [0.8.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...v0.8.0 diff --git a/Cargo.toml b/Cargo.toml index c8d670ed1..0673cbfc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smoltcp" -version = "0.8.1" +version = "0.9.0" edition = "2021" rust-version = "1.65" authors = ["whitequark "] diff --git a/README.md b/README.md index 437afca2c..b3d1f4282 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ There are 3 supported mediums. * IPv4 time-to-live value is configurable per socket, set to 64 by default. * IPv4 default gateway is supported. * Routing outgoing IPv4 packets is supported, through a default gateway or a CIDR route table. - * IPv4 fragmentation is **not** supported. + * IPv4 fragmentation and reassembly is supported. * IPv4 options are **not** supported and are silently ignored. #### IPv6 @@ -86,7 +86,7 @@ The ICMPv4 protocol is supported, and ICMP sockets are available. #### ICMPv6 -The ICMPv6 protocol is supported, but is **not** available via ICMP sockets. +The ICMPv6 protocol is supported, and ICMP sockets are available. * ICMPv6 header checksum is supported. * ICMPv6 echo replies are generated in response to echo requests. @@ -227,51 +227,51 @@ Any value can be set, unlike with Cargo features. Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting with different values, compilation fails. -### IFACE_MAX_ADDR_COUNT +### `IFACE_MAX_ADDR_COUNT` Max amount of IP addresses that can be assigned to one interface (counting both IPv4 and IPv6 addresses). Default: 2. -### IFACE_MAX_MULTICAST_GROUP_COUNT +### `IFACE_MAX_MULTICAST_GROUP_COUNT` Max amount of multicast groups that can be joined by one interface. Default: 4. -### IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT +### `IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT` Max amount of 6LoWPAN address contexts that can be assigned to one interface. Default: 4. -### IFACE_NEIGHBOR_CACHE_COUNT +### `IFACE_NEIGHBOR_CACHE_COUNT` Amount of "IP address -> hardware address" entries the neighbor cache (also known as the "ARP cache" or the "ARP table") holds. Default: 4. -### IFACE_MAX_ROUTE_COUNT +### `IFACE_MAX_ROUTE_COUNT` Max amount of routes that can be added to one interface. Includes the default route. Includes both IPv4 and IPv6. Default: 2. -### FRAGMENTATION_BUFFER_SIZE +### `FRAGMENTATION_BUFFER_SIZE` Size of the buffer used for fragmenting outgoing packets larger than the MTU. Packets larger than this setting will be dropped instead of fragmented. Default: 1500. -### ASSEMBLER_MAX_SEGMENT_COUNT +### `ASSEMBLER_MAX_SEGMENT_COUNT` Maximum number of non-contiguous segments the assembler can hold. Used for both packet reassembly and TCP stream reassembly. Default: 4. -### REASSEMBLY_BUFFER_SIZE +### `REASSEMBLY_BUFFER_SIZE` Size of the buffer used for reassembling (de-fragmenting) incoming packets. If the reassembled packet is larger than this setting, it will be dropped instead of reassembled. Default: 1500. -### REASSEMBLY_BUFFER_COUNT +### `REASSEMBLY_BUFFER_COUNT` Number of reassembly buffers, i.e how many different incoming packets can be reassembled at the same time. Default: 1. -### DNS_MAX_RESULT_COUNT +### `DNS_MAX_RESULT_COUNT` Maximum amount of address results for a given DNS query that will be kept. For example, if this is set to 2 and the queried name has 4 `A` records, only the first 2 will be returned. Default: 1. -### DNS_MAX_SERVER_COUNT +### `DNS_MAX_SERVER_COUNT` Maximum amount of DNS servers that can be configured in one DNS socket. Default: 1. -### DNS_MAX_NAME_SIZE +### `DNS_MAX_NAME_SIZE` Maximum length of DNS names that can be queried. Default: 255. From 46cca501d7e1c8dc920ba36bb78351e01f8fb8ec Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 8 Feb 2023 16:26:10 +0100 Subject: [PATCH 506/566] Fix parsing of link layer address for ndiscoptions --- src/wire/ndiscoption.rs | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 51b883fbe..4e3f4ee67 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -433,14 +433,14 @@ impl<'a> Repr<'a> { { match opt.option_type() { Type::SourceLinkLayerAddr => { - if opt.data_len() == 1 { + if opt.data_len() >= 1 { Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr())) } else { Err(Error) } } Type::TargetLinkLayerAddr => { - if opt.data_len() == 1 { + if opt.data_len() >= 1 { Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) } else { Err(Error) @@ -628,13 +628,18 @@ impl> PrettyPrint for NdiscOption { } } -#[cfg(feature = "medium-ethernet")] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(test)] mod test { use super::Error; use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type}; use crate::time::Duration; - use crate::wire::{EthernetAddress, Ipv6Address}; + use crate::wire::Ipv6Address; + + #[cfg(feature = "medium-ethernet")] + use crate::wire::EthernetAddress; + #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] + use crate::wire::Ieee802154Address; static PREFIX_OPT_BYTES: [u8; 32] = [ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, @@ -678,8 +683,9 @@ mod test { assert_eq!(NdiscOption::new_checked(&bytes), Err(Error)); } + #[cfg(feature = "medium-ethernet")] #[test] - fn test_repr_parse_link_layer_opt() { + fn test_repr_parse_link_layer_opt_ethernet() { let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34]; let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); { @@ -697,6 +703,29 @@ mod test { } } + #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] + #[test] + fn test_repr_parse_link_layer_opt_ieee802154() { + let mut bytes = [ + 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + let addr = Ieee802154Address::Extended([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + { + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Ok(Repr::SourceLinkLayerAddr(addr.into())) + ); + } + bytes[0] = 0x02; + { + assert_eq!( + Repr::parse(&NdiscOption::new_unchecked(&bytes)), + Ok(Repr::TargetLinkLayerAddr(addr.into())) + ); + } + } + #[test] fn test_repr_parse_prefix_info() { let repr = Repr::PrefixInformation(PrefixInformation { From 51af32ebd1f63f61d917c16810a742568c0072af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 19:40:28 +0100 Subject: [PATCH 507/566] iface: make MulticastError public. --- src/iface/interface/mod.rs | 3 +++ src/iface/mod.rs | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index ea937c7e2..21a2196fa 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -18,6 +18,9 @@ mod ipv6; #[cfg(feature = "proto-igmp")] mod igmp; +#[cfg(feature = "proto-igmp")] +pub use igmp::MulticastError; + use core::cmp; use core::result::Result; use heapless::{LinearMap, Vec}; diff --git a/src/iface/mod.rs b/src/iface/mod.rs index e5cb832b5..02c982f62 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -13,6 +13,9 @@ mod route; mod socket_meta; mod socket_set; +#[cfg(feature = "proto-igmp")] +pub use self::interface::MulticastError; pub use self::interface::{Config, Interface, InterfaceInner as Context}; + pub use self::route::{Route, RouteTableFull, Routes}; -pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; +pub use self::socket_set::{SocketHandle, SocketSet, SocketStorage}; From 9027825c16c9c3fbadb7663e56d64b590fc95d5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 20:01:06 +0100 Subject: [PATCH 508/566] Release v0.9.1 --- CHANGELOG.md | 8 +++++++- Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53df2cb16..29ae5ea4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.1] - 2023-02-08 + +- iface: make MulticastError public. (#747) +- Fix parsing of ieee802154 link layer address for NDISC options (#746) + ## [0.9.0] - 2023-02-06 - Minimum Supported Rust Version (MSRV) **bumped** from 1.56 to 1.65 @@ -196,7 +201,8 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith - Use #[non_exhaustive] for enums and structs ([409](https://github.com/smoltcp-rs/smoltcp/pull/409), [411](https://github.com/smoltcp-rs/smoltcp/pull/411)) - Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413)) -[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.0...HEAD +[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.1...HEAD +[0.9.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.2...v0.9.0 [0.8.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/smoltcp-rs/smoltcp/compare/v0.8.0...v0.8.1 diff --git a/Cargo.toml b/Cargo.toml index 0673cbfc4..c57cbb350 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smoltcp" -version = "0.9.0" +version = "0.9.1" edition = "2021" rust-version = "1.65" authors = ["whitequark "] From cce9ae779ce9b9938961fd444d21a465f7cbec28 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Feb 2023 00:11:57 +0100 Subject: [PATCH 509/566] tcp: do not count window updates as duplicate acks. --- src/socket/tcp.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index bcec24d84..8ed60c6c4 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -1699,7 +1699,9 @@ impl<'a> Socket<'a> { TcpControl::Syn => 0, _ => self.remote_win_scale.unwrap_or(0), }; - self.remote_win_len = (repr.window_len as usize) << (scale as usize); + let new_remote_win_len = (repr.window_len as usize) << (scale as usize); + let is_window_update = new_remote_win_len != self.remote_win_len; + self.remote_win_len = new_remote_win_len; if ack_len > 0 { // Dequeue acknowledged octets. @@ -1731,7 +1733,8 @@ impl<'a> Socket<'a> { Some(last_rx_ack) if repr.payload.is_empty() && last_rx_ack == ack_number - && ack_number < self.remote_last_seq => + && ack_number < self.remote_last_seq + && !is_window_update => { // Increment duplicate ACK count self.local_rx_dup_acks = self.local_rx_dup_acks.saturating_add(1); @@ -5484,6 +5487,66 @@ mod test { ); } + #[test] + fn test_fast_retransmit_duplicate_detection_with_window_update() { + let mut s = socket_established(); + + s.send_slice(b"abc").unwrap(); // This is lost + recv!(s, time 1000, Ok(TcpRepr { + seq_number: LOCAL_SEQ + 1, + ack_number: Some(REMOTE_SEQ + 1), + payload: &b"abc"[..], + ..RECV_TEMPL + })); + + // Normal ACK of previously received segment + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); + // First duplicate + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); + // Second duplicate + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + ..SEND_TEMPL + } + ); + + assert_eq!(s.local_rx_dup_acks, 2, "duplicate ACK counter is not set"); + + // This packet has a window update, hence should not be detected + // as a duplicate ACK and should reset the duplicate ACK count + send!( + s, + TcpRepr { + seq_number: REMOTE_SEQ + 1, + ack_number: Some(LOCAL_SEQ + 1), + window_len: 400, + ..SEND_TEMPL + } + ); + + assert_eq!( + s.local_rx_dup_acks, 0, + "duplicate ACK counter is not reset when receiving a window update" + ); + } + #[test] fn test_fast_retransmit_duplicate_detection() { let mut s = socket_established(); From 61e7d44de3ad40da9534a820606af76fe45f5ba3 Mon Sep 17 00:00:00 2001 From: Jarred Allen Date: Thu, 2 Feb 2023 13:19:49 -0800 Subject: [PATCH 510/566] Add support for rebinding to DHCP implementation --- src/socket/dhcpv4.rs | 187 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 39 deletions(-) diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index e88d783b4..d796efaa7 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -81,8 +81,20 @@ struct RenewState { /// Renew timer. When reached, we will start attempting /// to renew this lease with the DHCP server. - /// Must be less or equal than `expires_at`. + /// + /// Must be less or equal than `rebind_at`. renew_at: Instant, + + /// Rebind timer. When reached, we will start broadcasting to renew + /// this lease with any DHCP server. + /// + /// Must be greater than or equal to `renew_at`, and less than or + /// equal to `expires_at`. + rebind_at: Instant, + + /// Whether the T2 time has elapsed + rebinding: bool, + /// Expiration timer. When reached, this lease is no longer valid, so it must be /// thrown away and the ethernet interface deconfigured. expires_at: Instant, @@ -268,7 +280,12 @@ impl<'a> Socket<'a> { let t = match &self.state { ClientState::Discovering(state) => state.retry_at, ClientState::Requesting(state) => state.retry_at, - ClientState::Renewing(state) => state.renew_at.min(state.expires_at), + ClientState::Renewing(state) => if state.rebinding { + state.rebind_at + } else { + state.renew_at.min(state.rebind_at) + } + .min(state.expires_at), }; PollAt::Time(t) } @@ -353,13 +370,15 @@ impl<'a> Socket<'a> { }); } (ClientState::Requesting(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = + if let Some((config, renew_at, rebind_at, expires_at)) = Self::parse_ack(cx.now(), &dhcp_repr, self.max_lease_duration, state.server) { self.state = ClientState::Renewing(RenewState { config, renew_at, + rebind_at, expires_at, + rebinding: false, }); self.config_changed(); } @@ -370,13 +389,15 @@ impl<'a> Socket<'a> { } } (ClientState::Renewing(state), DhcpMessageType::Ack) => { - if let Some((config, renew_at, expires_at)) = Self::parse_ack( + if let Some((config, renew_at, rebind_at, expires_at)) = Self::parse_ack( cx.now(), &dhcp_repr, self.max_lease_duration, state.config.server, ) { state.renew_at = renew_at; + state.rebind_at = rebind_at; + state.rebinding = false; state.expires_at = expires_at; // The `receive_packet_buffer` field isn't populated until // the client asks for the state, but receiving any packet @@ -412,7 +433,7 @@ impl<'a> Socket<'a> { dhcp_repr: &DhcpRepr, max_lease_duration: Option, server: ServerInfo, - ) -> Option<(Config<'static>, Instant, Instant)> { + ) -> Option<(Config<'static>, Instant, Instant, Instant)> { let subnet_mask = match dhcp_repr.subnet_mask { Some(subnet_mask) => subnet_mask, None => { @@ -465,26 +486,44 @@ impl<'a> Socket<'a> { packet: None, }; - // Set renew time as per RFC 2131: - // The renew time (T1) can be specified by the server using option 58: - let renew_duration = dhcp_repr - .renew_duration - .map(|d| Duration::from_secs(d as u64)) - // Since we don't follow the REBINDING part of the spec, when no - // explicit T1 time is given, we will also consider the rebinding - // time if it is given and less than the default. - .or_else(|| { - dhcp_repr - .rebind_duration - .map(|d| Duration::from_secs(d as u64).min(lease_duration / 2)) - }) - // Otherwise, we use the default T1 time, which is half the lease - // duration. - .unwrap_or(lease_duration / 2); + // Set renew and rebind times as per RFC 2131: + // Times T1 and T2 are configurable by the server through + // options. T1 defaults to (0.5 * duration_of_lease). T2 + // defaults to (0.875 * duration_of_lease). + let (renew_duration, rebind_duration) = match ( + dhcp_repr + .renew_duration + .map(|d| Duration::from_secs(d as u64)), + dhcp_repr + .rebind_duration + .map(|d| Duration::from_secs(d as u64)), + ) { + (Some(renew_duration), Some(rebind_duration)) => (renew_duration, rebind_duration), + (None, None) => (lease_duration / 2, lease_duration * 7 / 8), + // RFC 2131 does not say what to do if only one value is + // provided, so: + + // If only T1 is provided, set T2 to be 0.75 through the gap + // between T1 and the duration of the lease. If T1 is set to + // the default (0.5 * duration_of_lease), then T2 will also + // be set to the default (0.875 * duration_of_lease). + (Some(renew_duration), None) => ( + renew_duration, + renew_duration + (lease_duration - renew_duration) * 3 / 4, + ), + + // If only T2 is provided, then T1 will be set to be + // whichever is smaller of the default (0.5 * + // duration_of_lease) or T2. + (None, Some(rebind_duration)) => { + ((lease_duration / 2).min(rebind_duration), rebind_duration) + } + }; let renew_at = now + renew_duration; + let rebind_at = now + rebind_duration; let expires_at = now + lease_duration; - Some((config, renew_at, expires_at)) + Some((config, renew_at, rebind_at, expires_at)) } #[cfg(not(test))] @@ -607,19 +646,25 @@ impl<'a> Socket<'a> { Ok(()) } ClientState::Renewing(state) => { - if state.expires_at <= cx.now() { + let now = cx.now(); + if state.expires_at <= now { net_debug!("DHCP lease expired"); self.reset(); // return Ok so we get polled again return Ok(()); } - if cx.now() < state.renew_at { + if now < state.renew_at || state.rebinding && now < state.rebind_at { return Ok(()); } + state.rebinding |= now >= state.rebind_at; + ipv4_repr.src_addr = state.config.address.address(); - ipv4_repr.dst_addr = state.config.server.address; + // Renewing is unicast to the original server, rebinding is broadcast + if !state.rebinding { + ipv4_repr.dst_addr = state.config.server.address; + } dhcp_repr.message_type = DhcpMessageType::Request; dhcp_repr.client_ip = state.config.address.address(); @@ -632,11 +677,20 @@ impl<'a> Socket<'a> { // 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 = cx.now() - + self - .retry_config - .min_renew_timeout - .max((state.expires_at - cx.now()) / 2); + if state.rebinding { + state.rebind_at = now + + self + .retry_config + .min_renew_timeout + .max((state.expires_at - now) / 2); + } else { + state.renew_at = now + + self + .retry_config + .min_renew_timeout + .max((state.rebind_at - now) / 2) + .min(state.rebind_at - now); + } self.transaction_id = next_transaction_id; Ok(()) @@ -832,6 +886,14 @@ mod test { hop_limit: 64, }; + const IP_BROADCAST_ADDRESSED: Ipv4Repr = Ipv4Repr { + src_addr: MY_IP, + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Udp, + payload_len: 0, + hop_limit: 64, + }; + const IP_SERVER_BROADCAST: Ipv4Repr = Ipv4Repr { src_addr: SERVER_IP, dst_addr: Ipv4Address::BROADCAST, @@ -971,6 +1033,18 @@ mod test { ..DHCP_DEFAULT }; + const DHCP_REBIND: DhcpRepr = DhcpRepr { + message_type: DhcpMessageType::Request, + client_identifier: Some(MY_MAC), + // NO server_identifier in renew requests, only in first one! + client_ip: MY_IP, + max_size: Some(1432), + + requested_ip: None, + parameter_request_list: Some(&[1, 3, 6]), + ..DHCP_DEFAULT + }; + // =========================================================================================// // Tests @@ -1008,6 +1082,8 @@ mod test { packet: None, }, renew_at: Instant::from_secs(500), + rebind_at: Instant::from_secs(875), + rebinding: false, expires_at: Instant::from_secs(1000), }); @@ -1043,6 +1119,7 @@ mod test { match &s.state { ClientState::Renewing(r) => { assert_eq!(r.renew_at, Instant::from_secs(500)); + assert_eq!(r.rebind_at, Instant::from_secs(875)); assert_eq!(r.expires_at, Instant::from_secs(1000)); } _ => panic!("Invalid state"), @@ -1078,6 +1155,7 @@ mod test { match &s.state { ClientState::Renewing(r) => { assert_eq!(r.renew_at, Instant::from_secs(500)); + assert_eq!(r.rebind_at, Instant::from_secs(875)); assert_eq!(r.expires_at, Instant::from_secs(1000)); } _ => panic!("Invalid state"), @@ -1189,35 +1267,66 @@ mod test { } #[test] - fn test_renew_retransmit() { + fn test_renew_rebind_retransmit() { let mut s = socket_bound(); recv!(s, []); + // First renew attempt at T1 + recv!(s, time 499_000, []); recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); - recv!(s, time 749_000, []); - recv!(s, time 750_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt at half way to T2 + recv!(s, time 687_000, []); + recv!(s, time 687_500, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt at half way again to T2 + recv!(s, time 781_000, []); + recv!(s, time 781_250, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt 60s later (minimum interval) + recv!(s, time 841_000, []); + recv!(s, time 841_250, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // No more renews due to minimum interval recv!(s, time 874_000, []); - recv!(s, time 875_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // First rebind attempt + recv!(s, time 875_000, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); + // Next rebind attempt half way to expiry + recv!(s, time 937_000, []); + recv!(s, time 937_500, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); + // Next rebind attempt 60s later (minimum interval) + recv!(s, time 997_000, []); + recv!(s, time 997_500, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); // check it still works - send!(s, time 875_000, (IP_RECV, UDP_RECV, dhcp_ack())); + send!(s, time 999_000, (IP_RECV, UDP_RECV, dhcp_ack())); match &s.state { ClientState::Renewing(r) => { // NOW the expiration gets bumped - assert_eq!(r.renew_at, Instant::from_secs(875 + 500)); - assert_eq!(r.expires_at, Instant::from_secs(875 + 1000)); + assert_eq!(r.renew_at, Instant::from_secs(999 + 500)); + assert_eq!(r.expires_at, Instant::from_secs(999 + 1000)); } _ => panic!("Invalid state"), } } #[test] - fn test_renew_timeout() { + fn test_renew_rebind_timeout() { let mut s = socket_bound(); recv!(s, []); + // First renew attempt at T1 recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); - recv!(s, time 999_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt at half way to T2 + recv!(s, time 687_500, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt at half way again to T2 + recv!(s, time 781_250, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // Next renew attempt 60s later (minimum interval) + recv!(s, time 841_250, [(IP_SEND, UDP_SEND, DHCP_RENEW)]); + // TODO uncomment below part of test + // // First rebind attempt + // recv!(s, time 875_000, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); + // // Next rebind attempt half way to expiry + // recv!(s, time 937_500, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); + // // Next rebind attempt 60s later (minimum interval) + // recv!(s, time 997_500, [(IP_BROADCAST_ADDRESSED, UDP_SEND, DHCP_REBIND)]); + // No more rebinds due to minimum interval recv!(s, time 1_000_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]); match &s.state { ClientState::Discovering(_) => {} From f65351adfa92db5193f368368cb668bac721fe43 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 2 Mar 2023 13:37:57 +0100 Subject: [PATCH 511/566] implement Display and Error for error types --- src/socket/tcp.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index bcec24d84..342f6697c 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -29,6 +29,18 @@ pub enum ListenError { Unaddressable, } +impl Display for ListenError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ListenError::InvalidState => write!(f, "invalid state"), + ListenError::Unaddressable => write!(f, "unaddressable destination"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ListenError {} + /// Error returned by [`Socket::connect`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -37,6 +49,18 @@ pub enum ConnectError { Unaddressable, } +impl Display for ConnectError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConnectError::InvalidState => write!(f, "invalid state"), + ConnectError::Unaddressable => write!(f, "unaddressable destination"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConnectError {} + /// Error returned by [`Socket::send`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -44,6 +68,17 @@ pub enum SendError { InvalidState, } +impl Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SendError::InvalidState => write!(f, "invalid state"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + /// Error returned by [`Socket::recv`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -52,6 +87,18 @@ pub enum RecvError { Finished, } +impl Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + RecvError::InvalidState => write!(f, "invalid state"), + RecvError::Finished => write!(f, "operation finished"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + /// A TCP socket ring buffer. pub type SocketBuffer<'a> = RingBuffer<'a, u8>; From dc3c44410bd5a6962ac21e185b3d6d3ea477877e Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Tue, 7 Mar 2023 10:51:39 +0100 Subject: [PATCH 512/566] Remove extern crate calls --- examples/dns.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/dns.rs b/examples/dns.rs index 1c30ab1d3..8bf8b2c85 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -1,10 +1,3 @@ -#[macro_use] -extern crate log; -extern crate byteorder; -extern crate env_logger; -extern crate getopts; -extern crate smoltcp; - mod utils; use smoltcp::iface::{Config, Interface, SocketSet}; @@ -75,7 +68,7 @@ fn main() { loop { let timestamp = Instant::now(); - debug!("timestamp {:?}", timestamp); + log::debug!("timestamp {:?}", timestamp); iface.poll(timestamp, &mut device, &mut sockets); From 4e4567d2388db6f2d27c2e34f836bd741ac7fbc3 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Tue, 7 Mar 2023 10:51:59 +0100 Subject: [PATCH 513/566] Bump env_logger dev-dependency --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c57cbb350..ced10be46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ cfg-if = "1.0.0" heapless = "0.7.8" [dev-dependencies] -env_logger = "0.9" +env_logger = "0.10" getopts = "0.2" rand = "0.8" url = "2.0" From ed0a770d9315b1badf730564e47636adb23cfa31 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 20 Mar 2023 10:10:48 +0100 Subject: [PATCH 514/566] Better defmt of Instant, Duration and Ipv6Address This changes the defmt formatting of Instant, Duration and Ipv6Address. They now have the same display as fmt::Display. --- src/time.rs | 16 +++++++++++-- src/wire/ipv6.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/time.rs b/src/time.rs index 99f8308b6..318dacacb 100644 --- a/src/time.rs +++ b/src/time.rs @@ -22,7 +22,6 @@ use core::{fmt, ops}; /// * A value less than `0` indicates a time before the starting /// point. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Instant { micros: i64, } @@ -134,6 +133,13 @@ impl fmt::Display for Instant { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Instant { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{}.{:03}s", self.secs(), self.millis()); + } +} + impl ops::Add for Instant { type Output = Instant; @@ -172,7 +178,6 @@ impl ops::Sub for Instant { /// A relative amount of time. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Duration { micros: u64, } @@ -230,6 +235,13 @@ impl fmt::Display for Duration { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Duration { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{}.{:03}s", self.secs(), self.millis()); + } +} + impl ops::Add for Duration { type Output = Duration; diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 8fe8d8133..3be45112f 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -27,7 +27,6 @@ pub const IPV4_MAPPED_PREFIX_SIZE: usize = ADDR_SIZE - 4; // 4 == ipv4::ADDR_SIZ /// A sixteen-octet IPv6 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Address(pub [u8; ADDR_SIZE]); impl Address { @@ -305,6 +304,67 @@ impl fmt::Display for Address { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Address { + fn format(&self, f: defmt::Formatter) { + if self.is_ipv4_mapped() { + return defmt::write!( + f, + "::ffff:{}.{}.{}.{}", + self.0[IPV4_MAPPED_PREFIX_SIZE + 0], + self.0[IPV4_MAPPED_PREFIX_SIZE + 1], + self.0[IPV4_MAPPED_PREFIX_SIZE + 2], + self.0[IPV4_MAPPED_PREFIX_SIZE + 3] + ); + } + + // The string representation of an IPv6 address should + // collapse a series of 16 bit sections that evaluate + // to 0 to "::" + // + // See https://tools.ietf.org/html/rfc4291#section-2.2 + // for details. + enum State { + Head, + HeadBody, + Tail, + TailBody, + } + let mut words = [0u16; 8]; + self.write_parts(&mut words); + let mut state = State::Head; + for word in words.iter() { + state = match (*word, &state) { + // Once a u16 equal to zero write a double colon and + // skip to the next non-zero u16. + (0, &State::Head) | (0, &State::HeadBody) => { + defmt::write!(f, "::"); + State::Tail + } + // Continue iterating without writing any characters until + // we hit a non-zero value. + (0, &State::Tail) => State::Tail, + // When the state is Head or Tail write a u16 in hexadecimal + // without the leading colon if the value is not 0. + (_, &State::Head) => { + defmt::write!(f, "{:x}", word); + State::HeadBody + } + (_, &State::Tail) => { + defmt::write!(f, "{:x}", word); + State::TailBody + } + // Write the u16 with a leading colon when parsing a value + // that isn't the first in a section + (_, &State::HeadBody) | (_, &State::TailBody) => { + defmt::write!(f, ":{:x}", word); + state + } + } + } + } +} + #[cfg(feature = "proto-ipv4")] /// Convert the given IPv4 address into a IPv4-mapped IPv6 address impl From for Address { From 80a7e1807585fd194f34fccef48985405b1b79a5 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 20 Mar 2023 11:11:40 +0100 Subject: [PATCH 515/566] modify defmt formatting for Cidr --- src/wire/ipv4.rs | 2 +- src/wire/ipv6.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index b039704b1..1027fc262 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -274,7 +274,7 @@ impl fmt::Display for Cidr { #[cfg(feature = "defmt")] impl defmt::Format for Cidr { fn format(&self, f: defmt::Formatter) { - defmt::write!(f, "{:?}/{=u8}", self.address, self.prefix_len); + defmt::write!(f, "{}/{=u8}", self.address, self.prefix_len); } } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 3be45112f..135cbffd3 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -379,7 +379,6 @@ impl From for Address { /// A specification of an IPv6 CIDR block, containing an address and a variable-length /// subnet masking prefix length. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Cidr { address: Address, prefix_len: u8, @@ -444,6 +443,13 @@ impl fmt::Display for Cidr { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Cidr { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{}/{=u8}", self.address, self.prefix_len); + } +} + /// A read/write wrapper around an Internet Protocol version 6 packet buffer. #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 91fc759cb54eb4cbc68ea05a356140c7003d47a7 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 20 Mar 2023 14:51:00 +0100 Subject: [PATCH 516/566] Change defmt formatting for IEEE802154 address --- src/wire/ieee802154.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 947793d72..1ac1c4583 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -82,13 +82,34 @@ impl Pan { /// A IEEE 802.15.4 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Address { Absent, Short([u8; 2]), Extended([u8; 8]), } +#[cfg(feature = "defmt")] +impl defmt::Format for Address { + fn format(&self, f: defmt::Formatter) { + match self { + Self::Absent => defmt::write!(f, "not-present"), + Self::Short(bytes) => defmt::write!(f, "{:02x}:{:02x}", bytes[0], bytes[1]), + Self::Extended(bytes) => defmt::write!( + f, + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + bytes[0], + bytes[1], + bytes[2], + bytes[3], + bytes[4], + bytes[5], + bytes[6], + bytes[7] + ), + } + } +} + #[cfg(test)] impl Default for Address { fn default() -> Self { @@ -171,10 +192,10 @@ impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Absent => write!(f, "not-present"), - Self::Short(bytes) => write!(f, "{:02x}-{:02x}", bytes[0], bytes[1]), + Self::Short(bytes) => write!(f, "{:02x}:{:02x}", bytes[0], bytes[1]), Self::Extended(bytes) => write!( f, - "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}", + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] ), } From d7d6294a571fae0cbe8ce55bbfecdd3bbeb93a06 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Thu, 23 Mar 2023 17:35:34 +0800 Subject: [PATCH 517/566] Add Hash trait for enum_with_unknown macro --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 7e324a571..7ab733ab3 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -41,7 +41,7 @@ macro_rules! enum_with_unknown { ),+ $(,)? } ) => { - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] $( #[$enum_attr] )* pub enum $name { From 9b525ae6d24442bf486279ed21108a12b1de65c8 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Fri, 24 Mar 2023 22:40:43 +0800 Subject: [PATCH 518/566] add Error trait for some struct --- src/iface/route.rs | 9 +++++++++ src/socket/udp.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/iface/route.rs b/src/iface/route.rs index 138f987b3..123c6950c 100644 --- a/src/iface/route.rs +++ b/src/iface/route.rs @@ -12,6 +12,15 @@ use crate::wire::{Ipv6Address, Ipv6Cidr}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RouteTableFull; +impl core::fmt::Display for RouteTableFull { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Route table full") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RouteTableFull {} + /// A prefix of addresses that should be routed via a router #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 6d8cc55be..bbf2e37bd 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -23,6 +23,18 @@ pub enum BindError { Unaddressable, } +impl core::fmt::Display for BindError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BindError::InvalidState => write!(f, "invalid state"), + BindError::Unaddressable => write!(f, "unaddressable"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for BindError {} + /// Error returned by [`Socket::send`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -31,6 +43,18 @@ pub enum SendError { BufferFull, } +impl core::fmt::Display for SendError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + SendError::Unaddressable => write!(f, "unaddressable"), + SendError::BufferFull => write!(f, "buffer full"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + /// Error returned by [`Socket::recv`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -38,6 +62,17 @@ pub enum RecvError { Exhausted, } +impl core::fmt::Display for RecvError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + RecvError::Exhausted => write!(f, "exhausted"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + /// A User Datagram Protocol socket. /// /// A UDP socket is bound to a specific endpoint, and owns transmit and receive From bf1132d27a6b2adf5f8bd9b4f88e37d7f500f7fe Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Sun, 26 Mar 2023 19:28:37 +0800 Subject: [PATCH 519/566] implement Display and Error for error types --- src/iface/fragmentation.rs | 19 +++++++++++++++++++ src/iface/interface/igmp.rs | 13 +++++++++++++ src/socket/dns.rs | 25 +++++++++++++++++++++++++ src/socket/icmp.rs | 35 +++++++++++++++++++++++++++++++++++ src/socket/raw.rs | 34 ++++++++++++++++++++++++++++++++++ src/storage/assembler.rs | 9 +++++++++ 6 files changed, 135 insertions(+) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index ace0a22d2..3abd2133d 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -18,11 +18,30 @@ type Buffer = [u8; REASSEMBLY_BUFFER_SIZE]; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssemblerError; +impl fmt::Display for AssemblerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "AssemblerError") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AssemblerError {} + + /// Packet assembler is full #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssemblerFullError; +impl fmt::Display for AssemblerFullError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "AssemblerFullError") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AssemblerFullError {} + /// Holds different fragments of one packet, used for assembling fragmented packets. /// /// The buffer used for the `PacketAssembler` should either be dynamically sized (ex: Vec) diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index 3e9fa726f..a2ce2c1ec 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -17,6 +17,19 @@ pub enum MulticastError { Ipv6NotSupported, } +impl core::fmt::Display for MulticastError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + MulticastError::Exhausted => write!(f, "Exhausted"), + MulticastError::GroupTableFull => write!(f, "GroupTableFull"), + MulticastError::Ipv6NotSupported => write!(f, "Ipv6NotSupported"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for MulticastError {} + impl Interface { /// Add an address to a list of subscribed multicast IP addresses. /// diff --git a/src/socket/dns.rs b/src/socket/dns.rs index ac21db30f..ca267b055 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -36,6 +36,19 @@ pub enum StartQueryError { NameTooLong, } +impl core::fmt::Display for StartQueryError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + StartQueryError::NoFreeSlot => write!(f, "No free slot"), + StartQueryError::InvalidName => write!(f, "Invalid name"), + StartQueryError::NameTooLong => write!(f, "Name too long"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for StartQueryError {} + /// Error returned by [`Socket::get_query_result`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -46,6 +59,18 @@ pub enum GetQueryResultError { Failed, } +impl core::fmt::Display for GetQueryResultError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + GetQueryResultError::Pending => write!(f, "Query is not done yet"), + GetQueryResultError::Failed => write!(f, "Query failed"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for GetQueryResultError {} + /// State for an in-progress DNS query. /// /// The only reason this struct is public is to allow the socket state diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index c9ec30f21..7d5cbc886 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -24,6 +24,18 @@ pub enum BindError { Unaddressable, } +impl core::fmt::Display for BindError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + BindError::InvalidState => write!(f, "invalid state"), + BindError::Unaddressable => write!(f, "unaddressable"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for BindError {} + /// Error returned by [`Socket::send`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -32,6 +44,18 @@ pub enum SendError { BufferFull, } +impl core::fmt::Display for SendError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + SendError::Unaddressable => write!(f, "unaddressable"), + SendError::BufferFull => write!(f, "buffer full"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + /// Error returned by [`Socket::recv`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -39,6 +63,17 @@ pub enum RecvError { Exhausted, } +impl core::fmt::Display for RecvError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + RecvError::Exhausted => write!(f, "exhausted"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + /// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for /// more details. /// diff --git a/src/socket/raw.rs b/src/socket/raw.rs index aa7a218b2..98282c9cd 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -22,6 +22,18 @@ pub enum BindError { Unaddressable, } +impl core::fmt::Display for BindError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + BindError::InvalidState => write!(f, "invalid state"), + BindError::Unaddressable => write!(f, "unaddressable"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for BindError {} + /// Error returned by [`Socket::send`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -29,6 +41,17 @@ pub enum SendError { BufferFull, } +impl core::fmt::Display for SendError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + SendError::BufferFull => write!(f, "buffer full"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SendError {} + /// Error returned by [`Socket::recv`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -36,6 +59,17 @@ pub enum RecvError { Exhausted, } +impl core::fmt::Display for RecvError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + RecvError::Exhausted => write!(f, "exhausted"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for RecvError {} + /// A UDP packet metadata. pub type PacketMetadata = crate::storage::PacketMetadata<()>; diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index c1348f1f0..168ba923b 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -5,6 +5,15 @@ use crate::config::ASSEMBLER_MAX_SEGMENT_COUNT; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct TooManyHolesError; +impl fmt::Display for TooManyHolesError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "too many holes") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TooManyHolesError {} + /// A contiguous chunk of absent data, followed by a contiguous chunk of present data. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 2b90103c10b05abcdebed5c05e9d90b4d0c324b0 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Sun, 26 Mar 2023 19:40:29 +0800 Subject: [PATCH 520/566] Update fragmentation.rs --- src/iface/fragmentation.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/iface/fragmentation.rs b/src/iface/fragmentation.rs index 3abd2133d..9870ab07e 100644 --- a/src/iface/fragmentation.rs +++ b/src/iface/fragmentation.rs @@ -27,7 +27,6 @@ impl fmt::Display for AssemblerError { #[cfg(feature = "std")] impl std::error::Error for AssemblerError {} - /// Packet assembler is full #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 4b1e8b174cb45238a618f637df139ffc548c383a Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 29 Mar 2023 15:46:08 +0200 Subject: [PATCH 521/566] Better impls defmt for some structs Sometimer, the defmt implementation was not readable when debugging. I changed it such that the defmt implelementation matches the Display implementation (which was more readable). --- src/storage/assembler.rs | 31 +++++++++++++++-- src/wire/ieee802154.rs | 74 ++++++++++++++++++++++++++++++++++------ src/wire/ipv6.rs | 15 +++++++- src/wire/sixlowpan.rs | 73 +++++++++++++++++++++++++++++++++++++-- src/wire/tcp.rs | 32 +++++++++++++++-- src/wire/udp.rs | 23 +++++++++++-- 6 files changed, 227 insertions(+), 21 deletions(-) diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 168ba923b..1577d1392 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -16,7 +16,6 @@ impl std::error::Error for TooManyHolesError {} /// A contiguous chunk of absent data, followed by a contiguous chunk of present data. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Contig { hole_size: usize, data_size: usize, @@ -37,6 +36,21 @@ impl fmt::Display for Contig { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Contig { + fn format(&self, fmt: defmt::Formatter) { + if self.has_hole() { + defmt::write!(fmt, "({})", self.hole_size); + } + if self.has_hole() && self.has_data() { + defmt::write!(fmt, " "); + } + if self.has_data() { + defmt::write!(fmt, "{}", self.data_size); + } + } +} + impl Contig { const fn empty() -> Contig { Contig { @@ -81,7 +95,6 @@ impl Contig { /// /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer. #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Assembler { contigs: [Contig; ASSEMBLER_MAX_SEGMENT_COUNT], } @@ -100,6 +113,20 @@ impl fmt::Display for Assembler { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Assembler { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "[ "); + for contig in self.contigs.iter() { + if !contig.has_data() { + break; + } + defmt::write!(fmt, "{} ", contig); + } + defmt::write!(fmt, "]"); + } +} + // Invariant on Assembler::contigs: // - There's an index `i` where all contigs before have data, and all contigs after don't (are unused). // - All contigs with data must have hole_size != 0, except the first. diff --git a/src/wire/ieee802154.rs b/src/wire/ieee802154.rs index 1ac1c4583..2431313f8 100644 --- a/src/wire/ieee802154.rs +++ b/src/wire/ieee802154.rs @@ -66,7 +66,6 @@ impl fmt::Display for AddressingMode { /// A IEEE 802.15.4 PAN. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Pan(pub u16); impl Pan { @@ -80,6 +79,19 @@ impl Pan { } } +impl fmt::Display for Pan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:0x}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Pan { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} + /// A IEEE 802.15.4 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum Address { @@ -759,16 +771,56 @@ impl + AsMut<[u8]>> Frame { impl> fmt::Display for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "IEEE802.15.4 frame type={} seq={:2x?} dst_pan={:x?} dest={:x?} src_pan={:?} src={:x?}", - self.frame_type(), - self.sequence_number(), - self.dst_pan_id(), - self.dst_addr(), - self.src_pan_id(), - self.src_addr(), - ) + write!(f, "IEEE802.15.4 frame type={}", self.frame_type())?; + + if let Some(seq) = self.sequence_number() { + write!(f, " seq={:02x}", seq)?; + } + + if let Some(pan) = self.dst_pan_id() { + write!(f, " dst-pan={}", pan)?; + } + + if let Some(pan) = self.src_pan_id() { + write!(f, " src-pan={}", pan)?; + } + + if let Some(addr) = self.dst_addr() { + write!(f, " dst={}", addr)?; + } + + if let Some(addr) = self.src_addr() { + write!(f, " src={}", addr)?; + } + + Ok(()) + } +} + +#[cfg(feature = "defmt")] +impl> defmt::Format for Frame { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "IEEE802.15.4 frame type={}", self.frame_type()); + + if let Some(seq) = self.sequence_number() { + defmt::write!(f, " seq={:02x}", seq); + } + + if let Some(pan) = self.dst_pan_id() { + defmt::write!(f, " dst-pan={}", pan); + } + + if let Some(pan) = self.src_pan_id() { + defmt::write!(f, " src-pan={}", pan); + } + + if let Some(addr) = self.dst_addr() { + defmt::write!(f, " dst={}", addr); + } + + if let Some(addr) = self.src_addr() { + defmt::write!(f, " src={}", addr); + } } } diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 135cbffd3..d624f246d 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -721,7 +721,6 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an Internet Protocol version 6 packet header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { /// IPv6 address of the source node. pub src_addr: Address, @@ -783,6 +782,20 @@ impl fmt::Display for Repr { } } +#[cfg(feature = "defmt")] +impl defmt::Format for Repr { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "IPv6 src={} dst={} nxt_hdr={} hop_limit={}", + self.src_addr, + self.dst_addr, + self.next_header, + self.hop_limit + ) + } +} + use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; // TODO: This is very similar to the implementation for IPv4. Make diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 6614ad3d6..abc7896de 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -405,12 +405,38 @@ pub mod frag { /// A high-level representation of a 6LoWPAN Fragment header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Repr { FirstFragment { size: u16, tag: u16 }, Fragment { size: u16, tag: u16, offset: u8 }, } + impl core::fmt::Display for Repr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Repr::FirstFragment { size, tag } => { + write!(f, "FirstFrag size={size} tag={tag}") + } + Repr::Fragment { size, tag, offset } => { + write!(f, "NthFrag size={size} tag={tag} offset={offset}") + } + } + } + } + + #[cfg(feature = "defmt")] + impl defmt::Format for Repr { + fn format(&self, fmt: defmt::Formatter) { + match self { + Repr::FirstFragment { size, tag } => { + defmt::write!(fmt, "FirstFrag size={} tag={}", size, tag); + } + Repr::Fragment { size, tag, offset } => { + defmt::write!(fmt, "NthFrag size={} tag={} offset={}", size, tag, offset); + } + } + } + } + impl Repr { /// Parse a 6LoWPAN Fragment header. pub fn parse>(packet: &Packet) -> Result { @@ -456,12 +482,30 @@ pub mod frag { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum NextHeader { Compressed, Uncompressed(IpProtocol), } +impl core::fmt::Display for NextHeader { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + NextHeader::Compressed => write!(f, "compressed"), + NextHeader::Uncompressed(protocol) => write!(f, "{protocol}"), + } + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for NextHeader { + fn format(&self, fmt: defmt::Formatter) { + match self { + NextHeader::Compressed => defmt::write!(fmt, "compressed"), + NextHeader::Uncompressed(protocol) => defmt::write!(fmt, "{}", protocol), + } + } +} + pub mod iphc { //! Implementation of IP Header Compression from [RFC 6282 § 3.1]. //! It defines the compression of IPv6 headers. @@ -1145,7 +1189,6 @@ pub mod iphc { /// A high-level representation of a 6LoWPAN IPHC header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub src_addr: ipv6::Address, pub ll_src_addr: Option, @@ -1159,6 +1202,30 @@ pub mod iphc { pub flow_label: Option, } + impl core::fmt::Display for Repr { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "IPHC src={} dst={} nxt-hdr={} hop-limit={}", + self.src_addr, self.dst_addr, self.next_header, self.hop_limit + ) + } + } + + #[cfg(feature = "defmt")] + impl defmt::Format for Repr { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "IPHC src={} dst={} nxt-hdr={} hop-limit={}", + self.src_addr, + self.dst_addr, + self.next_header, + self.hop_limit + ); + } + } + impl Repr { /// Parse a 6LoWPAN IPHC header and return a high-level representation. /// diff --git a/src/wire/tcp.rs b/src/wire/tcp.rs index fff61bd1e..bcc6fbc59 100644 --- a/src/wire/tcp.rs +++ b/src/wire/tcp.rs @@ -11,7 +11,6 @@ use crate::wire::{IpAddress, IpProtocol}; /// A sequence number is a monotonically advancing integer modulo 232. /// Sequence numbers do not have a discontiguity when compared pairwise across a signed overflow. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SeqNumber(pub i32); impl fmt::Display for SeqNumber { @@ -20,6 +19,13 @@ impl fmt::Display for SeqNumber { } } +#[cfg(feature = "defmt")] +impl defmt::Format for SeqNumber { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{}", self.0 as u32); + } +} + impl ops::Add for SeqNumber { type Output = SeqNumber; @@ -771,7 +777,6 @@ impl Control { /// A high-level representation of a Transmission Control Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub src_port: u16, pub dst_port: u16, @@ -1064,6 +1069,29 @@ impl<'a> fmt::Display for Repr<'a> { } } +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Repr<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "TCP src={} dst={}", self.src_port, self.dst_port); + match self.control { + Control::Syn => defmt::write!(fmt, " syn"), + Control::Fin => defmt::write!(fmt, " fin"), + Control::Rst => defmt::write!(fmt, " rst"), + Control::Psh => defmt::write!(fmt, " psh"), + Control::None => (), + } + defmt::write!(fmt, " seq={}", self.seq_number); + if let Some(ack_number) = self.ack_number { + defmt::write!(fmt, " ack={}", ack_number); + } + defmt::write!(fmt, " win={}", self.window_len); + defmt::write!(fmt, " len={}", self.payload.len()); + if let Some(max_seg_size) = self.max_seg_size { + defmt::write!(fmt, " mss={}", max_seg_size); + } + } +} + use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { diff --git a/src/wire/udp.rs b/src/wire/udp.rs index 8f0095155..77f9f84b3 100644 --- a/src/wire/udp.rs +++ b/src/wire/udp.rs @@ -8,7 +8,6 @@ use crate::wire::{IpAddress, IpProtocol}; /// A read/write wrapper around an User Datagram Protocol packet buffer. #[derive(Debug, PartialEq, Eq, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Packet> { buffer: T, } @@ -208,7 +207,6 @@ impl> AsRef<[u8]> for Packet { /// A high-level representation of an User Datagram Protocol packet. #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { pub src_port: u16, pub dst_port: u16, @@ -305,12 +303,33 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> { } } +#[cfg(feature = "defmt")] +impl<'a, T: AsRef<[u8]> + ?Sized> defmt::Format for Packet<&'a T> { + fn format(&self, fmt: defmt::Formatter) { + // Cannot use Repr::parse because we don't have the IP addresses. + defmt::write!( + fmt, + "UDP src={} dst={} len={}", + self.src_port(), + self.dst_port(), + self.payload().len() + ); + } +} + impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UDP src={} dst={}", self.src_port, self.dst_port) } } +#[cfg(feature = "defmt")] +impl defmt::Format for Repr { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "UDP src={} dst={}", self.src_port, self.dst_port); + } +} + use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; impl> PrettyPrint for Packet { From f95fbca74341569b057b95cfe3519e10ad431c8e Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 29 Mar 2023 16:10:37 +0200 Subject: [PATCH 522/566] Put IEEE802154 in separate iface module --- src/iface/interface/ieee802154.rs | 90 +++++++++++++++++++++++++ src/iface/interface/mod.rs | 6 +- src/iface/interface/sixlowpan.rs | 106 +++++------------------------- 3 files changed, 112 insertions(+), 90 deletions(-) create mode 100644 src/iface/interface/ieee802154.rs diff --git a/src/iface/interface/ieee802154.rs b/src/iface/interface/ieee802154.rs new file mode 100644 index 000000000..19faed799 --- /dev/null +++ b/src/iface/interface/ieee802154.rs @@ -0,0 +1,90 @@ +use super::*; + +use crate::phy::TxToken; +use crate::wire::*; + +impl InterfaceInner { + pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( + &mut self, + sockets: &mut SocketSet, + sixlowpan_payload: &'payload T, + _fragments: &'output mut FragmentsBuffer, + ) -> Option> { + let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); + let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); + + if ieee802154_repr.frame_type != Ieee802154FrameType::Data { + return None; + } + + // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this + // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. + // We always accept the broadcast PAN id. + if self.pan_id.is_some() + && ieee802154_repr.dst_pan_id != self.pan_id + && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) + { + net_debug!( + "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)", + ieee802154_repr + ); + return None; + } + + match ieee802154_frame.payload() { + Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, _fragments), + None => None, + } + } + + pub(super) fn dispatch_ieee802154( + &mut self, + ll_dst_a: Ieee802154Address, + tx_token: Tx, + packet: IpPacket, + frag: &mut Fragmenter, + ) { + let ll_src_a = self.hardware_addr.unwrap().ieee802154_or_panic(); + + // Create the IEEE802.15.4 header. + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(ll_dst_a), + src_pan_id: self.pan_id, + src_addr: Some(ll_src_a), + }; + + self.dispatch_sixlowpan(tx_token, packet, ieee_repr, frag); + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub(super) fn dispatch_ieee802154_frag( + &mut self, + tx_token: Tx, + frag: &mut Fragmenter, + ) { + // Create the IEEE802.15.4 header. + let ieee_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(self.get_sequence_number()), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: self.pan_id, + dst_addr: Some(frag.sixlowpan.ll_dst_addr), + src_pan_id: self.pan_id, + src_addr: Some(frag.sixlowpan.ll_src_addr), + }; + + self.dispatch_sixlowpan_frag(tx_token, ieee_repr, frag); + } +} diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 21a2196fa..7ab5b553a 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -7,13 +7,15 @@ mod tests; #[cfg(feature = "medium-ethernet")] mod ethernet; -#[cfg(feature = "proto-sixlowpan")] -mod sixlowpan; +#[cfg(feature = "medium-ieee802154")] +mod ieee802154; #[cfg(feature = "proto-ipv4")] mod ipv4; #[cfg(feature = "proto-ipv6")] mod ipv6; +#[cfg(feature = "proto-sixlowpan")] +mod sixlowpan; #[cfg(feature = "proto-igmp")] mod igmp; diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 767d0a446..b288b9956 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -1,7 +1,6 @@ use super::*; use crate::phy::ChecksumCapabilities; -use crate::phy::TxToken; use crate::wire::*; // Max len of non-fragmented packets after decompression (including ipv6 header and payload) @@ -9,40 +8,6 @@ use crate::wire::*; pub(crate) const MAX_DECOMPRESSED_LEN: usize = 1500; impl InterfaceInner { - #[cfg(feature = "medium-ieee802154")] - pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( - &mut self, - sockets: &mut SocketSet, - sixlowpan_payload: &'payload T, - _fragments: &'output mut FragmentsBuffer, - ) -> Option> { - let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); - let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); - - if ieee802154_repr.frame_type != Ieee802154FrameType::Data { - return None; - } - - // Drop frames when the user has set a PAN id and the PAN id from frame is not equal to this - // When the user didn't set a PAN id (so it is None), then we accept all PAN id's. - // We always accept the broadcast PAN id. - if self.pan_id.is_some() - && ieee802154_repr.dst_pan_id != self.pan_id - && ieee802154_repr.dst_pan_id != Some(Ieee802154Pan::BROADCAST) - { - net_debug!( - "IEEE802.15.4: dropping {:?} because not our PAN id (or not broadcast)", - ieee802154_repr - ); - return None; - } - - match ieee802154_frame.payload() { - Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, _fragments), - None => None, - } - } - pub(super) fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, @@ -53,7 +18,10 @@ impl InterfaceInner { let payload = match check!(SixlowpanPacket::dispatch(payload)) { #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] SixlowpanPacket::FragmentHeader => { - net_debug!("Fragmentation is not supported, use the `proto-sixlowpan-fragmentation` feature to add support."); + net_debug!( + "Fragmentation is not supported, \ + use the `proto-sixlowpan-fragmentation` feature to add support." + ); return None; } #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -257,51 +225,29 @@ impl InterfaceInner { Ok(decompressed_size) } - #[cfg(feature = "medium-ieee802154")] - pub(super) fn dispatch_ieee802154( + pub(super) fn dispatch_sixlowpan( &mut self, - ll_dst_a: Ieee802154Address, tx_token: Tx, packet: IpPacket, + ieee_repr: Ieee802154Repr, frag: &mut Fragmenter, ) { - // We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. - // Whenever this packet is to big to fit in the IEEE802.15.4 packet, then we need to - // fragment it. - let ll_src_a = self.hardware_addr.unwrap().ieee802154_or_panic(); - let ip_repr = packet.ip_repr(); let (src_addr, dst_addr) = match (ip_repr.src_addr(), ip_repr.dst_addr()) { (IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => (src_addr, dst_addr), #[allow(unreachable_patterns)] _ => { - net_debug!("dispatch_ieee802154: dropping because src or dst addrs are not ipv6."); - return; + unreachable!() } }; - // Create the IEEE802.15.4 header. - let ieee_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(self.get_sequence_number()), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: self.pan_id, - dst_addr: Some(ll_dst_a), - src_pan_id: self.pan_id, - src_addr: Some(ll_src_a), - }; - // Create the 6LoWPAN IPHC header. let iphc_repr = SixlowpanIphcRepr { src_addr, - ll_src_addr: Some(ll_src_a), + ll_src_addr: ieee_repr.src_addr, dst_addr, - ll_dst_addr: Some(ll_dst_a), + ll_dst_addr: ieee_repr.dst_addr, next_header: match &packet { IpPacket::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6), #[cfg(feature = "socket-tcp")] @@ -364,14 +310,15 @@ impl InterfaceInner { if pkt.buffer.len() < total_size { net_debug!( - "dispatch_ieee802154: dropping, fragmentation buffer is too small, at least {} needed", - total_size - ); + "dispatch_ieee802154: dropping, \ + fragmentation buffer is too small, at least {} needed", + total_size + ); return; } - pkt.sixlowpan.ll_dst_addr = ll_dst_a; - pkt.sixlowpan.ll_src_addr = ll_src_a; + pkt.sixlowpan.ll_dst_addr = ieee_repr.dst_addr.unwrap(); + pkt.sixlowpan.ll_src_addr = ieee_repr.src_addr.unwrap(); let mut iphc_packet = SixlowpanIphcPacket::new_unchecked(&mut pkt.buffer[..iphc_repr.buffer_len()]); @@ -537,30 +484,13 @@ impl InterfaceInner { } } - #[cfg(all( - feature = "medium-ieee802154", - feature = "proto-sixlowpan-fragmentation" - ))] - pub(super) fn dispatch_ieee802154_frag( + #[cfg(feature = "proto-sixlowpan-fragmentation")] + pub(super) fn dispatch_sixlowpan_frag( &mut self, tx_token: Tx, + ieee_repr: Ieee802154Repr, frag: &mut Fragmenter, ) { - // Create the IEEE802.15.4 header. - let ieee_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(self.get_sequence_number()), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: self.pan_id, - dst_addr: Some(frag.sixlowpan.ll_dst_addr), - src_pan_id: self.pan_id, - src_addr: Some(frag.sixlowpan.ll_src_addr), - }; - // Create the FRAG_N header. let fragn = SixlowpanFragRepr::Fragment { size: frag.sixlowpan.datagram_size, From d2fd7ffe0d41adf80a23881408efa63ee29eaca7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 29 Mar 2023 16:18:08 -0500 Subject: [PATCH 523/566] Bump minor version of heapless --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ced10be46..0b513c7b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3", optional = true } cfg-if = "1.0.0" -heapless = "0.7.8" +heapless = "0.7.15" [dev-dependencies] env_logger = "0.10" From 27c6f611bbeece4352c080c11ef47314f2d2b487 Mon Sep 17 00:00:00 2001 From: Will McLeish <71392578+wmcleish@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:24:36 +0000 Subject: [PATCH 524/566] added opcode and flag enums to dns export --- src/wire/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 5929424b3..5102d7c74 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -251,7 +251,7 @@ pub use self::dhcpv4::{ }; #[cfg(feature = "proto-dns")] -pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType}; +pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType, Opcode as DnsOpCode, Flags as DnsFlags}; /// Parsing a packet failed. /// From f7e499323dd43c71d52c795870ab9844cd7f329f Mon Sep 17 00:00:00 2001 From: Will McLeish <71392578+wmcleish@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:03:17 +0000 Subject: [PATCH 525/566] formatting --- src/wire/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 5102d7c74..84a3ce685 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -251,7 +251,10 @@ pub use self::dhcpv4::{ }; #[cfg(feature = "proto-dns")] -pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType, Opcode as DnsOpCode, Flags as DnsFlags}; +pub use self::dns::{ + Flags as DnsFlags, Opcode as DnsOpCode, Packet as DnsPacket, Repr as DnsRepr, + Type as DnsQueryType + }; /// Parsing a packet failed. /// From e9ce9e59f5fa36b946fa8830203d5eeafbad6a2e Mon Sep 17 00:00:00 2001 From: Will McLeish <71392578+wmcleish@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:09:35 +0000 Subject: [PATCH 526/566] formatting --- src/wire/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 84a3ce685..5d2c69cb3 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -252,9 +252,9 @@ pub use self::dhcpv4::{ #[cfg(feature = "proto-dns")] pub use self::dns::{ - Flags as DnsFlags, Opcode as DnsOpCode, Packet as DnsPacket, Repr as DnsRepr, - Type as DnsQueryType - }; + Flags as DnsFlags, Opcode as DnsOpCode, Packet as DnsPacket, Repr as DnsRepr, + Type as DnsQueryType +}; /// Parsing a packet failed. /// From 91823243aef8cf5b7ceb8995e803a96322be09a0 Mon Sep 17 00:00:00 2001 From: Will McLeish <71392578+wmcleish@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:18:01 +0000 Subject: [PATCH 527/566] formatting --- src/wire/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 5d2c69cb3..bd777e841 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -253,7 +253,7 @@ pub use self::dhcpv4::{ #[cfg(feature = "proto-dns")] pub use self::dns::{ Flags as DnsFlags, Opcode as DnsOpCode, Packet as DnsPacket, Repr as DnsRepr, - Type as DnsQueryType + Type as DnsQueryType, }; /// Parsing a packet failed. From 3f4210c2191b7cc5c072eb75bc6bd5a3eccb0b5e Mon Sep 17 00:00:00 2001 From: Will McLeish <71392578+wmcleish@users.noreply.github.com> Date: Mon, 3 Apr 2023 19:13:01 -0700 Subject: [PATCH 528/566] Update from comments Co-authored-by: Dario Nieuwenhuis --- src/wire/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index bd777e841..6a308a9f9 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -252,7 +252,7 @@ pub use self::dhcpv4::{ #[cfg(feature = "proto-dns")] pub use self::dns::{ - Flags as DnsFlags, Opcode as DnsOpCode, Packet as DnsPacket, Repr as DnsRepr, + Flags as DnsFlags, Opcode as DnsOpcode, Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType, }; From 099f5f7c2a7d5f2f1d6a1d08704c3b721845f03c Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 8 Feb 2023 11:36:54 +0100 Subject: [PATCH 529/566] Don't use Option for neighbor cache anymore --- examples/benchmark.rs | 11 ++- examples/client.rs | 11 ++- examples/dhcp_client.rs | 11 ++- examples/dns.rs | 11 ++- examples/httpclient.rs | 11 ++- examples/loopback.rs | 11 ++- examples/multicast.rs | 11 ++- examples/ping.rs | 11 ++- examples/server.rs | 12 ++- examples/sixlowpan.rs | 16 ++-- examples/sixlowpan_benchmark.rs | 16 ++-- src/iface/interface/ethernet.rs | 4 +- src/iface/interface/ieee802154.rs | 2 +- src/iface/interface/ipv4.rs | 13 ++-- src/iface/interface/ipv6.rs | 21 ++--- src/iface/interface/mod.rs | 122 +++++++++++------------------- src/iface/interface/tests.rs | 18 ++--- src/socket/dhcpv4.rs | 4 +- src/wire/mod.rs | 40 +++++++++- 19 files changed, 192 insertions(+), 164 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 9f908987f..b0cedcfbc 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -88,11 +88,14 @@ fn main() { let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 65535]); let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer); - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/client.rs b/examples/client.rs index 8543ae065..5422aded9 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -29,11 +29,14 @@ fn main() { let port = u16::from_str(&matches.free[1]).expect("invalid port format"); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 96654241a..d118219d9 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -28,11 +28,14 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); // Create sockets diff --git a/examples/dns.rs b/examples/dns.rs index 8bf8b2c85..da066aaf3 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -24,11 +24,14 @@ fn main() { let name = &matches.free[0]; // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 1e5a21864..751916da4 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -29,11 +29,14 @@ fn main() { let url = Url::parse(&matches.free[1]).expect("invalid url format"); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/loopback.rs b/examples/loopback.rs index 1a4dee883..9bca85e55 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -10,7 +10,7 @@ use core::str; use log::{debug, error, info}; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{Loopback, Medium}; +use smoltcp::phy::{Device, Loopback, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; @@ -83,8 +83,13 @@ fn main() { }; // Create interface - let mut config = Config::new(); - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/multicast.rs b/examples/multicast.rs index 639fa432f..ec7ff4354 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -28,11 +28,14 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/ping.rs b/examples/ping.rs index beac56257..7e6cbdb01 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -105,11 +105,14 @@ fn main() { ); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/server.rs b/examples/server.rs index f2bbc8032..67df63927 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -24,11 +24,15 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => todo!(), + }; + config.random_seed = rand::random(); - if device.capabilities().medium == Medium::Ethernet { - config.hardware_addr = Some(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()); - } let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 964a8562d..5b41116cf 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -47,11 +47,11 @@ use std::os::unix::io::AsRawFd; use std::str; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; +use smoltcp::phy::{wait as phy_wait, Device, Medium, RawSocket}; use smoltcp::socket::tcp; use smoltcp::socket::udp; use smoltcp::time::Instant; -use smoltcp::wire::{Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp::wire::{EthernetAddress, Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; fn main() { utils::setup_logging(""); @@ -67,10 +67,16 @@ fn main() { utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false); // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => Config::new( + Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into(), + ), + }; config.random_seed = rand::random(); - config.hardware_addr = - Some(Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into()); config.pan_id = Some(Ieee802154Pan(0xbeef)); let mut iface = Interface::new(config, &mut device); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 8b9cf842d..a389ca94d 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -47,9 +47,9 @@ use std::os::unix::io::AsRawFd; use std::str; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Medium, RawSocket}; +use smoltcp::phy::{wait as phy_wait, Device, Medium, RawSocket}; use smoltcp::socket::tcp; -use smoltcp::wire::{Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp::wire::{EthernetAddress, Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; //For benchmark use smoltcp::time::{Duration, Instant}; @@ -147,10 +147,16 @@ fn main() { }; // Create interface - let mut config = Config::new(); + let mut config = match device.capabilities().medium { + Medium::Ethernet => { + Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()) + } + Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), + Medium::Ieee802154 => Config::new( + Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into(), + ), + }; config.random_seed = rand::random(); - config.hardware_addr = - Some(Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into()); config.pan_id = Some(Ieee802154Pan(0xbeef)); let mut iface = Interface::new(config, &mut device); diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index b769909ce..bf8496fe5 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -22,7 +22,7 @@ impl InterfaceInner { // Ignore any packets not directed to our hardware address or any of the multicast groups. if !eth_frame.dst_addr().is_broadcast() && !eth_frame.dst_addr().is_multicast() - && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr.unwrap() + && HardwareAddress::Ethernet(eth_frame.dst_addr()) != self.hardware_addr { return None; } @@ -64,7 +64,7 @@ impl InterfaceInner { debug_assert!(tx_buffer.as_ref().len() == tx_len); let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); + let src_addr = self.hardware_addr.ethernet_or_panic(); frame.set_src_addr(src_addr); f(frame); diff --git a/src/iface/interface/ieee802154.rs b/src/iface/interface/ieee802154.rs index 19faed799..78023001b 100644 --- a/src/iface/interface/ieee802154.rs +++ b/src/iface/interface/ieee802154.rs @@ -44,7 +44,7 @@ impl InterfaceInner { packet: IpPacket, frag: &mut Fragmenter, ) { - let ll_src_a = self.hardware_addr.unwrap().ieee802154_or_panic(); + let ll_src_a = self.hardware_addr.ieee802154_or_panic(); // Create the IEEE802.15.4 header. let ieee_repr = Ieee802154Repr { diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index bd1d6e1de..50aff2838 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -74,7 +74,9 @@ impl InterfaceInner { #[cfg(feature = "socket-dhcpv4")] { - if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() { + if ipv4_repr.next_header == IpProtocol::Udp + && matches!(self.caps.medium, Medium::Ethernet) + { let udp_packet = check!(UdpPacket::new_checked(ip_payload)); if let Some(dhcp_socket) = sockets .items_mut() @@ -206,17 +208,14 @@ impl InterfaceInner { // We fill from requests too because if someone is requesting our address they // are probably going to talk to us, so we avoid having to request their address // when we later reply to them. - self.neighbor_cache.as_mut().unwrap().fill( + self.neighbor_cache.fill( source_protocol_addr.into(), source_hardware_addr.into(), timestamp, ); if operation == ArpOperation::Request { - let src_hardware_addr = match self.hardware_addr { - Some(HardwareAddress::Ethernet(addr)) => addr, - _ => unreachable!(), - }; + let src_hardware_addr = self.hardware_addr.ethernet_or_panic(); Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, @@ -352,7 +351,7 @@ impl InterfaceInner { let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); + let src_addr = self.hardware_addr.ethernet_or_panic(); frame.set_src_addr(src_addr); frame.set_dst_addr(frag.ipv4.dst_hardware_addr); diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index 220ee13b2..c57e06918 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -192,17 +192,9 @@ impl InterfaceInner { return None; } if flags.contains(NdiscNeighborFlags::OVERRIDE) - || !self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&ip_addr, self.now) - .found() + || !self.neighbor_cache.lookup(&ip_addr, self.now).found() { - self.neighbor_cache - .as_mut() - .unwrap() - .fill(ip_addr, lladdr, self.now) + self.neighbor_cache.fill(ip_addr, lladdr, self.now) } } None @@ -217,11 +209,8 @@ impl InterfaceInner { if !lladdr.is_unicast() || !target_addr.is_unicast() { return None; } - self.neighbor_cache.as_mut().unwrap().fill( - ip_repr.src_addr.into(), - lladdr, - self.now, - ); + self.neighbor_cache + .fill(ip_repr.src_addr.into(), lladdr, self.now); } if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { @@ -229,7 +218,7 @@ impl InterfaceInner { flags: NdiscNeighborFlags::SOLICITED, target_addr, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - lladdr: Some(self.hardware_addr.unwrap().into()), + lladdr: Some(self.hardware_addr.into()), }); let ip_repr = Ipv6Repr { src_addr: target_addr, diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 7ab5b553a..34ad4bd24 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -240,9 +240,8 @@ pub struct InterfaceInner { rand: Rand, #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Option, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: Option, + neighbor_cache: NeighborCache, + hardware_addr: HardwareAddress, #[cfg(feature = "medium-ieee802154")] sequence_no: u8, #[cfg(feature = "medium-ieee802154")] @@ -280,8 +279,7 @@ pub struct Config { /// /// # Panics /// Creating the interface panics if the address is not unicast. - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - pub hardware_addr: Option, + pub hardware_addr: HardwareAddress, /// Set the IEEE802.15.4 PAN ID the interface will use. /// @@ -291,23 +289,16 @@ pub struct Config { } impl Config { - pub fn new() -> Self { + pub fn new(hardware_addr: HardwareAddress) -> Self { Config { random_seed: 0, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr: None, + hardware_addr, #[cfg(feature = "medium-ieee802154")] pan_id: None, } } } -impl Default for Config { - fn default() -> Self { - Self::new() - } -} - #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(feature = "medium-ethernet")] @@ -476,29 +467,11 @@ impl Interface { { let caps = device.capabilities(); - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let hardware_addr = match caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => Some( - config - .hardware_addr - .expect("hardware_addr required option was not set"), - ), - #[cfg(feature = "medium-ip")] - Medium::Ip => { - assert!( - config.hardware_addr.is_none(), - "hardware_addr is set, but device medium is IP" - ); - None - } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => Some( - config - .hardware_addr - .expect("hardware_addr required option was not set"), - ), - }; + assert_eq!( + config.hardware_addr.medium(), + caps.medium, + "The hardware address does not match the medium of the interface." + ); let mut rand = Rand::new(config.random_seed); @@ -548,14 +521,13 @@ impl Interface { inner: InterfaceInner { now: Instant::from_secs(0), caps, - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - hardware_addr, + hardware_addr: config.hardware_addr, ip_addrs: Vec::new(), #[cfg(feature = "proto-ipv4")] any_ip: false, routes: Routes::new(), #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: Some(NeighborCache::new()), + neighbor_cache: NeighborCache::new(), #[cfg(feature = "proto-igmp")] ipv4_multicast_groups: LinearMap::new(), #[cfg(feature = "proto-igmp")] @@ -599,7 +571,7 @@ impl Interface { || self.inner.caps.medium == Medium::Ieee802154 ); - self.inner.hardware_addr.unwrap() + self.inner.hardware_addr } /// Set the HardwareAddress address of the interface. @@ -621,7 +593,7 @@ impl Interface { ); InterfaceInner::check_hardware_addr(&addr); - self.inner.hardware_addr = Some(addr); + self.inner.hardware_addr = addr; } /// Get the IP addresses of the interface. @@ -1050,7 +1022,7 @@ impl InterfaceInner { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn hardware_addr(&self) -> Option { + pub(crate) fn hardware_addr(&self) -> HardwareAddress { self.hardware_addr } @@ -1169,19 +1141,31 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv4-fragmentation")] ipv4_id: 1, + #[cfg(all( + feature = "medium-ip", + not(feature = "medium-ethernet"), + not(feature = "medium-ieee802154") + ))] + hardware_addr: crate::wire::HardwareAddress::Ip, + #[cfg(feature = "medium-ethernet")] - hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( - crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), - )), - #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] - hardware_addr: Some(crate::wire::HardwareAddress::Ieee802154( + hardware_addr: crate::wire::HardwareAddress::Ethernet(crate::wire::EthernetAddress([ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + ])), + + #[cfg(all( + not(feature = "medium-ip"), + not(feature = "medium-ethernet"), + feature = "medium-ieee802154" + ))] + hardware_addr: crate::wire::HardwareAddress::Ieee802154( crate::wire::Ieee802154Address::Extended([ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x2, 0x2, ]), - )), + ), #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - neighbor_cache: None, + neighbor_cache: NeighborCache::new(), #[cfg(feature = "proto-igmp")] igmp_report_state: IgmpReportState::Inactive, @@ -1199,7 +1183,7 @@ impl InterfaceInner { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] fn check_hardware_addr(addr: &HardwareAddress) { if !addr.is_unicast() { - panic!("Ethernet address {addr} is not unicast") + panic!("Hardware address {addr} is not unicast") } } @@ -1503,19 +1487,9 @@ impl InterfaceInner { match self.route(addr, self.now) { Some(_routed_addr) => match self.caps.medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => self - .neighbor_cache - .as_ref() - .unwrap() - .lookup(&_routed_addr, self.now) - .found(), + Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(), #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => self - .neighbor_cache - .as_ref() - .unwrap() - .lookup(&_routed_addr, self.now) - .found(), + Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(), #[cfg(feature = "medium-ip")] Medium::Ip => true, }, @@ -1584,12 +1558,7 @@ impl InterfaceInner { .route(dst_addr, self.now) .ok_or(DispatchError::NoRoute)?; - match self - .neighbor_cache - .as_mut() - .unwrap() - .lookup(&dst_addr, self.now) - { + match self.neighbor_cache.lookup(&dst_addr, self.now) { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending), _ => (), // XXX @@ -1602,7 +1571,7 @@ impl InterfaceInner { "address {} not in neighbor cache, sending ARP request", dst_addr ); - let src_hardware_addr = self.hardware_addr.unwrap().ethernet_or_panic(); + let src_hardware_addr = self.hardware_addr.ethernet_or_panic(); let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, @@ -1634,7 +1603,7 @@ impl InterfaceInner { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, - lladdr: Some(self.hardware_addr.unwrap().into()), + lladdr: Some(self.hardware_addr.into()), }); let packet = IpPacket::Icmpv6(( @@ -1659,15 +1628,13 @@ impl InterfaceInner { } // The request got dispatched, limit the rate on the cache. - self.neighbor_cache.as_mut().unwrap().limit_rate(self.now); + self.neighbor_cache.limit_rate(self.now); Err(DispatchError::NeighborPending) } fn flush_cache(&mut self) { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - if let Some(cache) = self.neighbor_cache.as_mut() { - cache.flush() - } + self.neighbor_cache.flush() } fn dispatch_ip( @@ -1722,8 +1689,7 @@ impl InterfaceInner { frag, )? { (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - #[cfg(feature = "medium-ieee802154")] - (HardwareAddress::Ieee802154(_), _) => unreachable!(), + (_, _) => unreachable!(), } } _ => (EthernetAddress([0; 6]), tx_token), @@ -1734,7 +1700,7 @@ impl InterfaceInner { let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { let mut frame = EthernetFrame::new_unchecked(tx_buffer); - let src_addr = self.hardware_addr.unwrap().ethernet_or_panic(); + let src_addr = self.hardware_addr.ethernet_or_panic(); frame.set_src_addr(src_addr); frame.set_dst_addr(dst_hardware_addr); diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index cf4c655fd..3345dae73 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -39,7 +39,7 @@ fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ip); - let mut config = Config::new(); + let config = Config::new(HardwareAddress::Ip); let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv4")] @@ -64,8 +64,7 @@ fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ethernet); - let mut config = Config::new(); - config.hardware_addr = Some(EthernetAddress::default().into()); + let config = Config::new(HardwareAddress::Ethernet(EthernetAddress::default())); let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv4")] @@ -90,8 +89,7 @@ fn create_ieee802154<'a>() -> (Interface, SocketSet<'a>, Loopback) { // Create a basic device let mut device = Loopback::new(Medium::Ieee802154); - let mut config = Config::new(); - config.hardware_addr = Some(Ieee802154Address::default().into()); + let config = Config::new(HardwareAddress::Ieee802154(Ieee802154Address::default())); let mut iface = Interface::new(config, &mut device); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv6")] @@ -133,11 +131,11 @@ impl TxToken for MockTxToken { } #[test] -#[should_panic(expected = "hardware_addr required option was not set")] -#[cfg(all(feature = "medium-ethernet"))] +#[should_panic(expected = "The hardware address does not match the medium of the interface.")] +#[cfg(all(feature = "medium-ip", feature = "medium-ethernet"))] fn test_new_panic() { let mut device = Loopback::new(Medium::Ethernet); - let config = Config::new(); + let config = Config::new(HardwareAddress::Ip); Interface::new(config, &mut device); } @@ -1377,7 +1375,7 @@ fn test_echo_request_sixlowpan_128_bytes() { assert_eq!(iface.inner.caps.medium, Medium::Ieee802154); let now = iface.inner.now(); - iface.inner.neighbor_cache.as_mut().unwrap().fill( + iface.inner.neighbor_cache.fill( Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0]).into(), HardwareAddress::Ieee802154(Ieee802154Address::default()), now, @@ -1503,7 +1501,7 @@ fn test_echo_request_sixlowpan_128_bytes() { ))) ); - iface.inner.neighbor_cache.as_mut().unwrap().fill( + iface.inner.neighbor_cache.fill( IpAddress::Ipv6(Ipv6Address([ 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, ])), diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index d796efaa7..2609621eb 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -317,7 +317,7 @@ impl<'a> Socket<'a> { } }; - let Some(HardwareAddress::Ethernet(ethernet_addr)) = cx.hardware_addr() else { + let HardwareAddress::Ethernet(ethernet_addr) = cx.hardware_addr() else { panic!("using DHCPv4 socket with a non-ethernet hardware address."); }; @@ -542,7 +542,7 @@ impl<'a> Socket<'a> { { // note: Dhcpv4Socket is only usable in ethernet mediums, so the // unwrap can never fail. - let Some(HardwareAddress::Ethernet(ethernet_addr)) = cx.hardware_addr() else { + let HardwareAddress::Ethernet(ethernet_addr) = cx.hardware_addr() else { panic!("using DHCPv4 socket with a non-ethernet hardware address."); }; diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 6a308a9f9..616af949e 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -275,20 +275,32 @@ impl fmt::Display for Error { pub type Result = core::result::Result; /// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +#[cfg(any( + feature = "medium-ip", + feature = "medium-ethernet", + feature = "medium-ieee802154" +))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum HardwareAddress { + #[cfg(feature = "medium-ip")] + Ip, #[cfg(feature = "medium-ethernet")] Ethernet(EthernetAddress), #[cfg(feature = "medium-ieee802154")] Ieee802154(Ieee802154Address), } -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +#[cfg(any( + feature = "medium-ip", + feature = "medium-ethernet", + feature = "medium-ieee802154" +))] impl HardwareAddress { pub const fn as_bytes(&self) -> &[u8] { match self { + #[cfg(feature = "medium-ip")] + HardwareAddress::Ip => unreachable!(), #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => addr.as_bytes(), #[cfg(feature = "medium-ieee802154")] @@ -299,6 +311,8 @@ impl HardwareAddress { /// Query wether the address is an unicast address. pub fn is_unicast(&self) -> bool { match self { + #[cfg(feature = "medium-ip")] + HardwareAddress::Ip => unreachable!(), #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => addr.is_unicast(), #[cfg(feature = "medium-ieee802154")] @@ -309,6 +323,8 @@ impl HardwareAddress { /// Query wether the address is a broadcast address. pub fn is_broadcast(&self) -> bool { match self { + #[cfg(feature = "medium-ip")] + HardwareAddress::Ip => unreachable!(), #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => addr.is_broadcast(), #[cfg(feature = "medium-ieee802154")] @@ -333,12 +349,30 @@ impl HardwareAddress { _ => panic!("HardwareAddress is not Ethernet."), } } + + #[inline] + pub(crate) fn medium(&self) -> Medium { + match self { + #[cfg(feature = "medium-ip")] + HardwareAddress::Ip => Medium::Ip, + #[cfg(feature = "medium-ethernet")] + HardwareAddress::Ethernet(_) => Medium::Ethernet, + #[cfg(feature = "medium-ieee802154")] + HardwareAddress::Ieee802154(_) => Medium::Ieee802154, + } + } } -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +#[cfg(any( + feature = "medium-ip", + feature = "medium-ethernet", + feature = "medium-ieee802154" +))] impl core::fmt::Display for HardwareAddress { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { + #[cfg(feature = "medium-ip")] + HardwareAddress::Ip => write!(f, "no hardware addr"), #[cfg(feature = "medium-ethernet")] HardwareAddress::Ethernet(addr) => write!(f, "{addr}"), #[cfg(feature = "medium-ieee802154")] From 572b22b4f2154032e9364c7d9bd5ce1eb19701f6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Apr 2023 18:55:24 +0200 Subject: [PATCH 530/566] Clippy fix. --- src/wire/sixlowpan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index abc7896de..80b25c408 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -700,7 +700,7 @@ pub mod iphc { match self.tf_field() { 0b00 | 0b10 => { let start = self.ip_fields_start() as usize; - Some(self.buffer.as_ref()[start..][0] & 0b1111_11) + Some(self.buffer.as_ref()[start..][0] & 0b111111) } 0b01 | 0b11 => None, _ => unreachable!(), From 99adcf7506b836cd6131cbb586afd542780d34e4 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 7 Apr 2023 10:07:34 +0200 Subject: [PATCH 531/566] Fix incorrect test packet The packet had 0x12 for the length, which is not the same as the variable name suggests. Signed-off-by: Thibaut Vandervelden --- src/wire/ipv6hopbyhop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6hopbyhop.rs index 96b81b231..c604d7791 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6hopbyhop.rs @@ -219,7 +219,7 @@ mod test { // A Hop-by-Hop Option header with a PadN option of option data length 12. static REPR_PACKET_PAD12: [u8; 16] = [ - 0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x06, 0x1, 0x1, 0x0C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ]; #[test] From af1d94aa4c02499e24393de02cbabe83671e8cb2 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 7 Apr 2023 12:53:10 +0200 Subject: [PATCH 532/566] Add wire representation for the RPL protocol. Signed-off-by: Thibaut Vandervelden --- Cargo.toml | 1 + src/macros.rs | 96 ++ src/socket/icmp.rs | 2 +- src/wire/icmpv6.rs | 83 +- src/wire/mod.rs | 9 + src/wire/rpl.rs | 2686 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 2870 insertions(+), 7 deletions(-) create mode 100644 src/wire/rpl.rs diff --git a/Cargo.toml b/Cargo.toml index 0b513c7b6..9ee30c2ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "proto-igmp" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4"] "proto-ipv6" = [] +"proto-rpl" = [] "proto-sixlowpan" = ["proto-ipv6"] "proto-sixlowpan-fragmentation" = ["proto-sixlowpan", "_proto-fragmentation"] "proto-dns" = [] diff --git a/src/macros.rs b/src/macros.rs index 7ab733ab3..e899d24ec 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -71,3 +71,99 @@ macro_rules! enum_with_unknown { } } } + +#[cfg(feature = "proto-rpl")] +macro_rules! get { + ($buffer:expr, into: $into:ty, fun: $fun:ident, field: $field:expr $(,)?) => { + { + <$into>::$fun(&$buffer.as_ref()[$field]) + } + }; + + ($buffer:expr, into: $into:ty, field: $field:expr $(,)?) => { + get!($buffer, into: $into, field: $field, shift: 0, mask: 0b1111_1111) + }; + + ($buffer:expr, into: $into:ty, field: $field:expr, mask: $bit_mask:expr $(,)?) => { + get!($buffer, into: $into, field: $field, shift: 0, mask: $bit_mask) + }; + + ($buffer:expr, into: $into:ty, field: $field:expr, shift: $bit_shift:expr, mask: $bit_mask:expr $(,)?) => { + { + <$into>::from((&$buffer.as_ref()[$field] >> $bit_shift) & $bit_mask) + } + }; + + ($buffer:expr, field: $field:expr $(,)?) => { + get!($buffer, field: $field, shift: 0, mask: 0b1111_1111) + }; + + ($buffer:expr, field: $field:expr, mask: $bit_mask:expr $(,)?) => { + get!($buffer, field: $field, shift: 0, mask: $bit_mask) + }; + + ($buffer:expr, field: $field:expr, shift: $bit_shift:expr, mask: $bit_mask:expr $(,)?) + => + { + { + (&$buffer.as_ref()[$field] >> $bit_shift) & $bit_mask + } + }; + + ($buffer:expr, u16, field: $field:expr $(,)?) => { + { + NetworkEndian::read_u16(&$buffer.as_ref()[$field]) + } + }; + + ($buffer:expr, bool, field: $field:expr, shift: $bit_shift:expr, mask: $bit_mask:expr $(,)?) => { + { + (($buffer.as_ref()[$field] >> $bit_shift) & $bit_mask) == 0b1 + } + }; + + ($buffer:expr, u32, field: $field:expr $(,)?) => { + { + NetworkEndian::read_u32(&$buffer.as_ref()[$field]) + } + }; +} + +#[cfg(feature = "proto-rpl")] +macro_rules! set { + ($buffer:expr, address: $address:ident, field: $field:expr $(,)?) => {{ + $buffer.as_mut()[$field].copy_from_slice($address.as_bytes()); + }}; + + ($buffer:expr, $value:ident, field: $field:expr $(,)?) => { + set!($buffer, $value, field: $field, shift: 0, mask: 0b1111_1111) + }; + + ($buffer:expr, $value:ident, field: $field:expr, mask: $bit_mask:expr $(,)?) => { + set!($buffer, $value, field: $field, shift: 0, mask: $bit_mask) + }; + + ($buffer:expr, $value:ident, field: $field:expr, shift: $bit_shift:expr, mask: $bit_mask:expr $(,)?) => {{ + let raw = + ($buffer.as_ref()[$field] & !($bit_mask << $bit_shift)) | ($value << $bit_shift); + $buffer.as_mut()[$field] = raw; + }}; + + ($buffer:expr, $value:ident, bool, field: $field:expr, mask: $bit_mask:expr $(,)?) => { + set!($buffer, $value, bool, field: $field, shift: 0, mask: $bit_mask); + }; + + ($buffer:expr, $value:ident, bool, field: $field:expr, shift: $bit_shift:expr, mask: $bit_mask:expr $(,)?) => {{ + let raw = ($buffer.as_ref()[$field] & !($bit_mask << $bit_shift)) + | (if $value { 0b1 } else { 0b0 } << $bit_shift); + $buffer.as_mut()[$field] = raw; + }}; + + ($buffer:expr, $value:ident, u16, field: $field:expr $(,)?) => {{ + NetworkEndian::write_u16(&mut $buffer.as_mut()[$field], $value); + }}; + + ($buffer:expr, $value:ident, u32, field: $field:expr $(,)?) => {{ + NetworkEndian::write_u32(&mut $buffer.as_mut()[$field], $value); + }}; +} diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 7d5cbc886..b6867585e 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -474,7 +474,7 @@ impl<'a> Socket<'a> { } pub(crate) fn process(&mut self, _cx: &mut Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) { - match *icmp_repr { + match icmp_repr { #[cfg(feature = "proto-ipv4")] IcmpRepr::Ipv4(icmp_repr) => { net_trace!("icmp: receiving {} octets", icmp_repr.buffer_len()); diff --git a/src/wire/icmpv6.rs b/src/wire/icmpv6.rs index fa5416e58..d7130aa6a 100644 --- a/src/wire/icmpv6.rs +++ b/src/wire/icmpv6.rs @@ -7,6 +7,8 @@ use crate::wire::ip::checksum; use crate::wire::MldRepr; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] use crate::wire::NdiscRepr; +#[cfg(feature = "proto-rpl")] +use crate::wire::RplRepr; use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr}; enum_with_unknown! { @@ -37,7 +39,9 @@ enum_with_unknown! { /// Redirect Redirect = 0x89, /// Multicast Listener Report - MldReport = 0x8f + MldReport = 0x8f, + /// RPL Control Message + RplControl = 0x9b, } } @@ -94,6 +98,7 @@ impl fmt::Display for Message { Message::Redirect => write!(f, "redirect"), Message::MldQuery => write!(f, "multicast listener query"), Message::MldReport => write!(f, "multicast listener report"), + Message::RplControl => write!(f, "RPL control message"), Message::Unknown(id) => write!(f, "{id}"), } } @@ -265,11 +270,68 @@ impl> Packet { /// Returns `Err(Error)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); - if len < field::HEADER_END || len < self.header_len() { - Err(Error) - } else { - Ok(()) + + if len < 4 { + return Err(Error); } + + match self.msg_type() { + Message::DstUnreachable + | Message::PktTooBig + | Message::TimeExceeded + | Message::ParamProblem + | Message::EchoRequest + | Message::EchoReply + | Message::MldQuery + | Message::RouterSolicit + | Message::RouterAdvert + | Message::NeighborSolicit + | Message::NeighborAdvert + | Message::Redirect + | Message::MldReport => { + if len < field::HEADER_END || len < self.header_len() { + return Err(Error); + } + } + #[cfg(feature = "proto-rpl")] + Message::RplControl => match super::rpl::RplControlMessage::from(self.msg_code()) { + super::rpl::RplControlMessage::DodagInformationSolicitation => { + // TODO(thvdveld): replace magic number + if len < 6 { + return Err(Error); + } + } + super::rpl::RplControlMessage::DodagInformationObject => { + // TODO(thvdveld): replace magic number + if len < 28 { + return Err(Error); + } + } + super::rpl::RplControlMessage::DestinationAdvertisementObject => { + // TODO(thvdveld): replace magic number + if len < 8 || (self.dao_dodag_id_present() && len < 24) { + return Err(Error); + } + } + super::rpl::RplControlMessage::DestinationAdvertisementObjectAck => { + // TODO(thvdveld): replace magic number + if len < 8 || (self.dao_dodag_id_present() && len < 24) { + return Err(Error); + } + } + super::rpl::RplControlMessage::SecureDodagInformationSolicitation + | super::rpl::RplControlMessage::SecureDodagInformationObject + | super::rpl::RplControlMessage::SecureDesintationAdvertismentObject + | super::rpl::RplControlMessage::SecureDestinationAdvertisementObjectAck + | super::rpl::RplControlMessage::ConsistencyCheck => return Err(Error), + super::rpl::RplControlMessage::Unknown(_) => return Err(Error), + }, + #[cfg(not(feature = "proto-rpl"))] + Message::RplControl => return Err(Error), + Message::Unknown(_) => return Err(Error), + } + + Ok(()) } /// Consume the packet, returning the underlying buffer. @@ -535,6 +597,8 @@ pub enum Repr<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] Ndisc(NdiscRepr<'a>), Mld(MldRepr<'a>), + #[cfg(feature = "proto-rpl")] + Rpl(RplRepr<'a>), } impl<'a> Repr<'a> { @@ -620,12 +684,14 @@ impl<'a> Repr<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc), (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), + #[cfg(feature = "proto-rpl")] + (Message::RplControl, _) => RplRepr::parse(packet).map(Repr::Rpl), _ => Err(Error), } } /// Return the length of a packet that will be emitted from this high-level representation. - pub const fn buffer_len(&self) -> usize { + pub fn buffer_len(&self) -> usize { match self { &Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } @@ -639,6 +705,8 @@ impl<'a> Repr<'a> { #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] &Repr::Ndisc(ndisc) => ndisc.buffer_len(), &Repr::Mld(mld) => mld.buffer_len(), + #[cfg(feature = "proto-rpl")] + Repr::Rpl(rpl) => rpl.buffer_len(), } } @@ -734,6 +802,9 @@ impl<'a> Repr<'a> { Repr::Ndisc(ndisc) => ndisc.emit(packet), Repr::Mld(mld) => mld.emit(packet), + + #[cfg(feature = "proto-rpl")] + Repr::Rpl(ref rpl) => rpl.emit(packet), } if checksum_caps.icmpv6.tx() { diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 616af949e..cb835b55b 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -120,6 +120,8 @@ mod ndisc; any(feature = "medium-ethernet", feature = "medium-ieee802154") ))] mod ndiscoption; +#[cfg(feature = "proto-rpl")] +mod rpl; #[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] mod sixlowpan; mod tcp; @@ -142,6 +144,13 @@ pub use self::arp::{ Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr, }; +#[cfg(feature = "proto-rpl")] +pub use self::rpl::{ + data::HopByHopOption as RplHopByHopRepr, data::Packet as RplHopByHopPacket, + options::Packet as RplOptionPacket, options::Repr as RplOptionRepr, + InstanceId as RplInstanceId, Repr as RplRepr, +}; + #[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] pub use self::sixlowpan::{ frag::{Key as SixlowpanFragKey, Packet as SixlowpanFragPacket, Repr as SixlowpanFragRepr}, diff --git a/src/wire/rpl.rs b/src/wire/rpl.rs new file mode 100644 index 000000000..7028b923f --- /dev/null +++ b/src/wire/rpl.rs @@ -0,0 +1,2686 @@ +//! Implementation of the RPL packet formats. See [RFC 6550 § 6]. +//! +//! [RFC 6550 § 6]: https://datatracker.ietf.org/doc/html/rfc6550#section-6 + +use byteorder::{ByteOrder, NetworkEndian}; + +use super::{Error, Result}; +use crate::wire::icmpv6::Packet; +use crate::wire::ipv6::Address; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum InstanceId { + Global(u8), + Local(u8), +} + +impl From for InstanceId { + fn from(val: u8) -> Self { + const MASK: u8 = 0b0111_1111; + + if ((val >> 7) & 0xb1) == 0b0 { + Self::Global(val & MASK) + } else { + Self::Local(val & MASK) + } + } +} + +impl From for u8 { + fn from(val: InstanceId) -> Self { + match val { + InstanceId::Global(val) => 0b0000_0000 | val, + InstanceId::Local(val) => 0b1000_0000 | val, + } + } +} + +impl InstanceId { + /// Return the real part of the ID. + pub fn id(&self) -> u8 { + match self { + Self::Global(val) => *val, + Self::Local(val) => *val, + } + } + + /// Returns `true` when the DODAG ID is the destination address of the IPv6 packet. + #[inline] + pub fn dodag_is_destination(&self) -> bool { + match self { + Self::Global(_) => false, + Self::Local(val) => ((val >> 6) & 0b1) == 0b1, + } + } + + /// Returns `true` when the DODAG ID is the source address of the IPv6 packet. + /// + /// *NOTE*: this only makes sence when using a local RPL Instance ID and the packet is not a + /// RPL control message. + #[inline] + pub fn dodag_is_source(&self) -> bool { + !self.dodag_is_destination() + } +} + +mod field { + use crate::wire::field::*; + + pub const RPL_INSTANCE_ID: usize = 4; + + // DODAG information solicitation fields (DIS) + pub const DIS_FLAGS: usize = 4; + pub const DIS_RESERVED: usize = 5; + + // DODAG information object fields (DIO) + pub const DIO_VERSION_NUMBER: usize = 5; + pub const DIO_RANK: Field = 6..8; + pub const DIO_GROUNDED: usize = 8; + pub const DIO_MOP: usize = 8; + pub const DIO_PRF: usize = 8; + pub const DIO_DTSN: usize = 9; + //pub const DIO_FLAGS: usize = 10; + //pub const DIO_RESERVED: usize = 11; + pub const DIO_DODAG_ID: Field = 12..12 + 16; + + // Destination advertisment object (DAO) + pub const DAO_K: usize = 5; + pub const DAO_D: usize = 5; + //pub const DAO_FLAGS: usize = 5; + //pub const DAO_RESERVED: usize = 6; + pub const DAO_SEQUENCE: usize = 7; + pub const DAO_DODAG_ID: Field = 8..8 + 16; + + // Destination advertisment object ack (DAO-ACK) + pub const DAO_ACK_D: usize = 5; + //pub const DAO_ACK_RESERVED: usize = 5; + pub const DAO_ACK_SEQUENCE: usize = 6; + pub const DAO_ACK_STATUS: usize = 7; + pub const DAO_ACK_DODAG_ID: Field = 8..8 + 16; +} + +enum_with_unknown! { + /// RPL Control Message subtypes. + pub enum RplControlMessage(u8) { + DodagInformationSolicitation = 0x00, + DodagInformationObject = 0x01, + DestinationAdvertisementObject = 0x02, + DestinationAdvertisementObjectAck = 0x03, + SecureDodagInformationSolicitation = 0x80, + SecureDodagInformationObject = 0x81, + SecureDesintationAdvertismentObject = 0x82, + SecureDestinationAdvertisementObjectAck = 0x83, + ConsistencyCheck = 0x8a, + } +} + +impl core::fmt::Display for RplControlMessage { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + RplControlMessage::DodagInformationSolicitation => { + write!(f, "DODAG information solicitation (DIS)") + } + RplControlMessage::DodagInformationObject => { + write!(f, "DODAG information object (DIO)") + } + RplControlMessage::DestinationAdvertisementObject => { + write!(f, "destination advertisment object (DAO)") + } + RplControlMessage::DestinationAdvertisementObjectAck => write!( + f, + "destination advertisment object acknowledgement (DAO-ACK)" + ), + RplControlMessage::SecureDodagInformationSolicitation => { + write!(f, "secure DODAG information solicitation (DIS)") + } + RplControlMessage::SecureDodagInformationObject => { + write!(f, "secure DODAG information object (DIO)") + } + RplControlMessage::SecureDesintationAdvertismentObject => { + write!(f, "secure destination advertisment object (DAO)") + } + RplControlMessage::SecureDestinationAdvertisementObjectAck => write!( + f, + "secure destination advertisment object acknowledgement (DAO-ACK)" + ), + RplControlMessage::ConsistencyCheck => write!(f, "consistency check (CC)"), + RplControlMessage::Unknown(id) => write!(f, "{}", id), + } + } +} + +impl> Packet { + /// Return the RPL instance ID. + #[inline] + pub fn rpl_instance_id(&self) -> InstanceId { + get!(self.buffer, into: InstanceId, field: field::RPL_INSTANCE_ID) + } +} + +impl<'p, T: AsRef<[u8]> + ?Sized> Packet<&'p T> { + /// Return a pointer to the options. + pub fn options(&self) -> Result<&'p [u8]> { + let len = self.buffer.as_ref().len(); + match RplControlMessage::from(self.msg_code()) { + RplControlMessage::DodagInformationSolicitation if len < field::DIS_RESERVED + 1 => { + return Err(Error) + } + RplControlMessage::DodagInformationObject if len < field::DIO_DODAG_ID.end => { + return Err(Error) + } + RplControlMessage::DestinationAdvertisementObject + if self.dao_dodag_id_present() && len < field::DAO_DODAG_ID.end => + { + return Err(Error) + } + RplControlMessage::DestinationAdvertisementObject if len < field::DAO_SEQUENCE + 1 => { + return Err(Error) + } + RplControlMessage::DestinationAdvertisementObjectAck + if self.dao_dodag_id_present() && len < field::DAO_ACK_DODAG_ID.end => + { + return Err(Error) + } + RplControlMessage::DestinationAdvertisementObjectAck + if len < field::DAO_ACK_STATUS + 1 => + { + return Err(Error) + } + RplControlMessage::SecureDodagInformationSolicitation + | RplControlMessage::SecureDodagInformationObject + | RplControlMessage::SecureDesintationAdvertismentObject + | RplControlMessage::SecureDestinationAdvertisementObjectAck + | RplControlMessage::ConsistencyCheck => return Err(Error), + RplControlMessage::Unknown(_) => return Err(Error), + _ => {} + } + + let buffer = &self.buffer.as_ref(); + Ok(match RplControlMessage::from(self.msg_code()) { + RplControlMessage::DodagInformationSolicitation => &buffer[field::DIS_RESERVED + 1..], + RplControlMessage::DodagInformationObject => &buffer[field::DIO_DODAG_ID.end..], + RplControlMessage::DestinationAdvertisementObject if self.dao_dodag_id_present() => { + &buffer[field::DAO_DODAG_ID.end..] + } + RplControlMessage::DestinationAdvertisementObject => &buffer[field::DAO_SEQUENCE + 1..], + RplControlMessage::DestinationAdvertisementObjectAck if self.dao_dodag_id_present() => { + &buffer[field::DAO_ACK_DODAG_ID.end..] + } + RplControlMessage::DestinationAdvertisementObjectAck => { + &buffer[field::DAO_ACK_STATUS + 1..] + } + RplControlMessage::SecureDodagInformationSolicitation + | RplControlMessage::SecureDodagInformationObject + | RplControlMessage::SecureDesintationAdvertismentObject + | RplControlMessage::SecureDestinationAdvertisementObjectAck + | RplControlMessage::ConsistencyCheck => unreachable!(), + RplControlMessage::Unknown(_) => unreachable!(), + }) + } +} + +impl + AsMut<[u8]>> Packet { + /// Set the RPL Instance ID field. + #[inline] + pub fn set_rpl_instance_id(&mut self, value: u8) { + set!(self.buffer, value, field: field::RPL_INSTANCE_ID) + } +} + +impl<'p, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'p mut T> { + /// Return a pointer to the options. + pub fn options_mut(&mut self) -> &mut [u8] { + match RplControlMessage::from(self.msg_code()) { + RplControlMessage::DodagInformationSolicitation => { + &mut self.buffer.as_mut()[field::DIS_RESERVED + 1..] + } + RplControlMessage::DodagInformationObject => { + &mut self.buffer.as_mut()[field::DIO_DODAG_ID.end..] + } + RplControlMessage::DestinationAdvertisementObject => { + if self.dao_dodag_id_present() { + &mut self.buffer.as_mut()[field::DAO_DODAG_ID.end..] + } else { + &mut self.buffer.as_mut()[field::DAO_SEQUENCE + 1..] + } + } + RplControlMessage::DestinationAdvertisementObjectAck => { + if self.dao_dodag_id_present() { + &mut self.buffer.as_mut()[field::DAO_ACK_DODAG_ID.end..] + } else { + &mut self.buffer.as_mut()[field::DAO_ACK_STATUS + 1..] + } + } + RplControlMessage::SecureDodagInformationSolicitation + | RplControlMessage::SecureDodagInformationObject + | RplControlMessage::SecureDesintationAdvertismentObject + | RplControlMessage::SecureDestinationAdvertisementObjectAck + | RplControlMessage::ConsistencyCheck => todo!("Secure messages not supported"), + RplControlMessage::Unknown(_) => todo!(), + } + } +} + +/// Getters for the DODAG information solicitation (DIS) message. +/// +/// ```txt +/// 0 1 2 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Flags | Reserved | Option(s)... +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// ``` +impl> Packet { + /// Return the DIS flags field. + #[inline] + pub fn dis_flags(&self) -> u8 { + get!(self.buffer, field: field::DIS_FLAGS) + } + + /// Return the DIS reserved field. + #[inline] + pub fn dis_reserved(&self) -> u8 { + get!(self.buffer, field: field::DIS_RESERVED) + } +} + +/// Setters for the DODAG information solicitation (DIS) message. +impl + AsMut<[u8]>> Packet { + /// Clear the DIS flags field. + pub fn clear_dis_flags(&mut self) { + self.buffer.as_mut()[field::DIS_FLAGS] = 0; + } + + /// Clear the DIS rserved field. + pub fn clear_dis_reserved(&mut self) { + self.buffer.as_mut()[field::DIS_RESERVED] = 0; + } +} + +enum_with_unknown! { + pub enum ModeOfOperation(u8) { + NoDownwardRoutesMaintained = 0x00, + NonStoringMode = 0x01, + StoringModeWithoutMulticast = 0x02, + StoringModeWithMulticast = 0x03, + } +} + +impl Default for ModeOfOperation { + fn default() -> Self { + Self::StoringModeWithoutMulticast + } +} + +/// Getters for the DODAG information object (DIO) message. +/// +/// ```txt +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | RPLInstanceID |Version Number | Rank | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// |G|0| MOP | Prf | DTSN | Flags | Reserved | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | | +/// + + +/// | | +/// + DODAGID + +/// | | +/// + + +/// | | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Option(s)... +/// +-+-+-+-+-+-+-+-+ +/// ``` +impl> Packet { + /// Return the Version Number field. + #[inline] + pub fn dio_version_number(&self) -> u8 { + get!(self.buffer, field: field::DIO_VERSION_NUMBER) + } + + /// Return the Rank field. + #[inline] + pub fn dio_rank(&self) -> u16 { + get!(self.buffer, u16, field: field::DIO_RANK) + } + + /// Return the value of the Grounded flag. + #[inline] + pub fn dio_grounded(&self) -> bool { + get!(self.buffer, bool, field: field::DIO_GROUNDED, shift: 7, mask: 0b01) + } + + /// Return the mode of operation field. + #[inline] + pub fn dio_mode_of_operation(&self) -> ModeOfOperation { + get!(self.buffer, into: ModeOfOperation, field: field::DIO_MOP, shift: 3, mask: 0b111) + } + + /// Return the DODAG preference field. + #[inline] + pub fn dio_dodag_preference(&self) -> u8 { + get!(self.buffer, field: field::DIO_PRF, mask: 0b111) + } + + /// Return the destination advertisment trigger sequence number. + #[inline] + pub fn dio_dest_adv_trigger_seq_number(&self) -> u8 { + get!(self.buffer, field: field::DIO_DTSN) + } + + /// Return the DODAG id, which is an IPv6 address. + #[inline] + pub fn dio_dodag_id(&self) -> Address { + get!( + self.buffer, + into: Address, + fun: from_bytes, + field: field::DIO_DODAG_ID + ) + } +} + +/// Setters for the DODAG information object (DIO) message. +impl + AsMut<[u8]>> Packet { + /// Set the Version Number field. + #[inline] + pub fn set_dio_version_number(&mut self, value: u8) { + set!(self.buffer, value, field: field::DIO_VERSION_NUMBER) + } + + /// Set the Rank field. + #[inline] + pub fn set_dio_rank(&mut self, value: u16) { + set!(self.buffer, value, u16, field: field::DIO_RANK) + } + + /// Set the value of the Grounded flag. + #[inline] + pub fn set_dio_grounded(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::DIO_GROUNDED, shift: 7, mask: 0b01) + } + + /// Set the mode of operation field. + #[inline] + pub fn set_dio_mode_of_operation(&mut self, mode: ModeOfOperation) { + let raw = (self.buffer.as_ref()[field::DIO_MOP] & !(0b111 << 3)) | (u8::from(mode) << 3); + self.buffer.as_mut()[field::DIO_MOP] = raw; + } + + /// Set the DODAG preference field. + #[inline] + pub fn set_dio_dodag_preference(&mut self, value: u8) { + set!(self.buffer, value, field: field::DIO_PRF, mask: 0b111) + } + + /// Set the destination advertisment trigger sequence number. + #[inline] + pub fn set_dio_dest_adv_trigger_seq_number(&mut self, value: u8) { + set!(self.buffer, value, field: field::DIO_DTSN) + } + + /// Set the DODAG id, which is an IPv6 address. + #[inline] + pub fn set_dio_dodag_id(&mut self, address: Address) { + set!(self.buffer, address: address, field: field::DIO_DODAG_ID) + } +} + +/// Getters for the Destination Advertisment Object (DAO) message. +/// +/// ```txt +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | RPLInstanceID |K|D| Flags | Reserved | DAOSequence | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | | +/// + + +/// | | +/// + DODAGID* + +/// | | +/// + + +/// | | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Option(s)... +/// +-+-+-+-+-+-+-+-+ +/// ``` +impl> Packet { + /// Returns the Expect DAO-ACK flag. + #[inline] + pub fn dao_ack_request(&self) -> bool { + get!(self.buffer, bool, field: field::DAO_K, shift: 7, mask: 0b1) + } + + /// Returns the flag indicating that the DODAG ID is present or not. + #[inline] + pub fn dao_dodag_id_present(&self) -> bool { + get!(self.buffer, bool, field: field::DAO_D, shift: 6, mask: 0b1) + } + + /// Returns the DODAG sequence flag. + #[inline] + pub fn dao_dodag_sequence(&self) -> u8 { + get!(self.buffer, field: field::DAO_SEQUENCE) + } + + /// Returns the DODAG ID, an IPv6 address, when it is present. + #[inline] + pub fn dao_dodag_id(&self) -> Option
{ + if self.dao_dodag_id_present() { + Some(Address::from_bytes( + &self.buffer.as_ref()[field::DAO_DODAG_ID], + )) + } else { + None + } + } +} + +/// Setters for the Destination Advertisment Object (DAO) message. +impl + AsMut<[u8]>> Packet { + /// Set the Expect DAO-ACK flag. + #[inline] + pub fn set_dao_ack_request(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::DAO_K, shift: 7, mask: 0b1,) + } + + /// Set the flag indicating that the DODAG ID is present or not. + #[inline] + pub fn set_dao_dodag_id_present(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::DAO_D, shift: 6, mask: 0b1) + } + + /// Set the DODAG sequence flag. + #[inline] + pub fn set_dao_dodag_sequence(&mut self, value: u8) { + set!(self.buffer, value, field: field::DAO_SEQUENCE) + } + + /// Set the DODAG ID. + #[inline] + pub fn set_dao_dodag_id(&mut self, address: Option
) { + match address { + Some(address) => { + self.buffer.as_mut()[field::DAO_DODAG_ID].copy_from_slice(address.as_bytes()); + self.set_dao_dodag_id_present(true); + } + None => { + self.set_dao_dodag_id_present(false); + } + } + } +} + +/// Getters for the Destination Advertisment Object acknowledgement (DAO-ACK) message. +/// +/// ```txt +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | RPLInstanceID |D| Reserved | DAOSequence | Status | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | | +/// + + +/// | | +/// + DODAGID* + +/// | | +/// + + +/// | | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Option(s)... +/// +-+-+-+-+-+-+-+-+ +/// ``` +impl> Packet { + /// Returns the flag indicating that the DODAG ID is present or not. + #[inline] + pub fn dao_ack_dodag_id_present(&self) -> bool { + get!(self.buffer, bool, field: field::DAO_ACK_D, shift: 6, mask: 0b1) + } + + /// Return the DODAG sequence number. + #[inline] + pub fn dao_ack_sequence(&self) -> u8 { + get!(self.buffer, field: field::DAO_ACK_SEQUENCE) + } + + /// Return the DOA status field. + #[inline] + pub fn dao_ack_status(&self) -> u8 { + get!(self.buffer, field: field::DAO_ACK_STATUS) + } + + /// Returns the DODAG ID, an IPv6 address, when it is present. + #[inline] + pub fn dao_ack_dodag_id(&self) -> Option
{ + if self.dao_ack_dodag_id_present() { + Some(Address::from_bytes( + &self.buffer.as_ref()[field::DAO_ACK_DODAG_ID], + )) + } else { + None + } + } +} + +/// Setters for the Destination Advertisment Object acknowledgement (DAO-ACK) message. +impl + AsMut<[u8]>> Packet { + /// Set the flag indicating that the DODAG ID is present or not. + #[inline] + pub fn set_dao_ack_dodag_id_present(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::DAO_ACK_D, shift: 6, mask: 0b1) + } + + /// Set the DODAG sequence number. + #[inline] + pub fn set_dao_ack_sequence(&mut self, value: u8) { + set!(self.buffer, value, field: field::DAO_ACK_SEQUENCE) + } + + /// Set the DOA status field. + #[inline] + pub fn set_dao_ack_status(&mut self, value: u8) { + set!(self.buffer, value, field: field::DAO_ACK_STATUS) + } + + /// Set the DODAG ID. + #[inline] + pub fn set_dao_ack_dodag_id(&mut self, address: Option
) { + match address { + Some(address) => { + self.buffer.as_mut()[field::DAO_ACK_DODAG_ID].copy_from_slice(address.as_bytes()); + self.set_dao_ack_dodag_id_present(true); + } + None => { + self.set_dao_ack_dodag_id_present(false); + } + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Repr<'p> { + DodagInformationSolicitation { + options: &'p [u8], + }, + DodagInformationObject { + rpl_instance_id: InstanceId, + version_number: u8, + rank: u16, + grounded: bool, + mode_of_operation: ModeOfOperation, + dodag_preference: u8, + dtsn: u8, + dodag_id: Address, + options: &'p [u8], + }, + DestinationAdvertisementObject { + rpl_instance_id: InstanceId, + expect_ack: bool, + sequence: u8, + dodag_id: Option
, + options: &'p [u8], + }, + DestinationAdvertisementObjectAck { + rpl_instance_id: InstanceId, + sequence: u8, + status: u8, + dodag_id: Option
, + }, +} + +impl core::fmt::Display for Repr<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Repr::DodagInformationSolicitation { .. } => { + write!(f, "DIS")?; + } + Repr::DodagInformationObject { + rpl_instance_id, + version_number, + rank, + grounded, + mode_of_operation, + dodag_preference, + dtsn, + dodag_id, + .. + } => { + write!( + f, + "DIO \ + IID={rpl_instance_id:?} \ + V={version_number} \ + R={rank} \ + G={grounded} \ + MOP={mode_of_operation:?} \ + Pref={dodag_preference} \ + DTSN={dtsn} \ + DODAGID={dodag_id}" + )?; + } + Repr::DestinationAdvertisementObject { + rpl_instance_id, + expect_ack, + sequence, + dodag_id, + .. + } => { + write!( + f, + "DAO \ + IID={rpl_instance_id:?} \ + Ack={expect_ack} \ + Seq={sequence} \ + DODAGID={dodag_id:?}", + )?; + } + Repr::DestinationAdvertisementObjectAck { + rpl_instance_id, + sequence, + status, + dodag_id, + .. + } => { + write!( + f, + "DAO-ACK \ + IID={rpl_instance_id:?} \ + Seq={sequence} \ + Status={status} \ + DODAGID={dodag_id:?}", + )?; + } + }; + + Ok(()) + } +} + +impl<'p> Repr<'p> { + pub fn set_options(&mut self, options: &'p [u8]) { + let opts = match self { + Repr::DodagInformationSolicitation { options } => options, + Repr::DodagInformationObject { options, .. } => options, + Repr::DestinationAdvertisementObject { options, .. } => options, + Repr::DestinationAdvertisementObjectAck { .. } => unreachable!(), + }; + + *opts = options; + } + + pub fn parse + ?Sized>(packet: &Packet<&'p T>) -> Result { + let options = packet.options()?; + match RplControlMessage::from(packet.msg_code()) { + RplControlMessage::DodagInformationSolicitation => { + Ok(Repr::DodagInformationSolicitation { options }) + } + RplControlMessage::DodagInformationObject => Ok(Repr::DodagInformationObject { + rpl_instance_id: packet.rpl_instance_id(), + version_number: packet.dio_version_number(), + rank: packet.dio_rank(), + grounded: packet.dio_grounded(), + mode_of_operation: packet.dio_mode_of_operation(), + dodag_preference: packet.dio_dodag_preference(), + dtsn: packet.dio_dest_adv_trigger_seq_number(), + dodag_id: packet.dio_dodag_id(), + options, + }), + RplControlMessage::DestinationAdvertisementObject => { + Ok(Repr::DestinationAdvertisementObject { + rpl_instance_id: packet.rpl_instance_id(), + expect_ack: packet.dao_ack_request(), + sequence: packet.dao_dodag_sequence(), + dodag_id: packet.dao_dodag_id(), + options, + }) + } + RplControlMessage::DestinationAdvertisementObjectAck => { + Ok(Repr::DestinationAdvertisementObjectAck { + rpl_instance_id: packet.rpl_instance_id(), + sequence: packet.dao_ack_sequence(), + status: packet.dao_ack_status(), + dodag_id: packet.dao_ack_dodag_id(), + }) + } + RplControlMessage::SecureDodagInformationSolicitation + | RplControlMessage::SecureDodagInformationObject + | RplControlMessage::SecureDesintationAdvertismentObject + | RplControlMessage::SecureDestinationAdvertisementObjectAck + | RplControlMessage::ConsistencyCheck => Err(Error), + RplControlMessage::Unknown(_) => Err(Error), + } + } + + pub fn buffer_len(&self) -> usize { + let mut len = 4 + match self { + Repr::DodagInformationSolicitation { .. } => 2, + Repr::DodagInformationObject { .. } => 24, + Repr::DestinationAdvertisementObject { dodag_id, .. } => { + if dodag_id.is_some() { + 20 + } else { + 4 + } + } + Repr::DestinationAdvertisementObjectAck { dodag_id, .. } => { + if dodag_id.is_some() { + 20 + } else { + 4 + } + } + }; + + let opts = match self { + Repr::DodagInformationSolicitation { options } => &options[..], + Repr::DodagInformationObject { options, .. } => &options[..], + Repr::DestinationAdvertisementObject { options, .. } => &options[..], + Repr::DestinationAdvertisementObjectAck { .. } => &[], + }; + + len += opts.len(); + + len + } + + pub fn emit + AsMut<[u8]> + ?Sized>(&self, packet: &mut Packet<&mut T>) { + packet.set_msg_type(crate::wire::icmpv6::Message::RplControl); + + match self { + Repr::DodagInformationSolicitation { .. } => { + packet.set_msg_code(RplControlMessage::DodagInformationSolicitation.into()); + packet.clear_dis_flags(); + packet.clear_dis_reserved(); + } + Repr::DodagInformationObject { + rpl_instance_id, + version_number, + rank, + grounded, + mode_of_operation, + dodag_preference, + dtsn, + dodag_id, + .. + } => { + packet.set_msg_code(RplControlMessage::DodagInformationObject.into()); + packet.set_rpl_instance_id((*rpl_instance_id).into()); + packet.set_dio_version_number(*version_number); + packet.set_dio_rank(*rank); + packet.set_dio_grounded(*grounded); + packet.set_dio_mode_of_operation(*mode_of_operation); + packet.set_dio_dodag_preference(*dodag_preference); + packet.set_dio_dest_adv_trigger_seq_number(*dtsn); + packet.set_dio_dodag_id(*dodag_id); + } + Repr::DestinationAdvertisementObject { + rpl_instance_id, + expect_ack, + sequence, + dodag_id, + .. + } => { + packet.set_msg_code(RplControlMessage::DestinationAdvertisementObject.into()); + packet.set_rpl_instance_id((*rpl_instance_id).into()); + packet.set_dao_ack_request(*expect_ack); + packet.set_dao_dodag_sequence(*sequence); + packet.set_dao_dodag_id(*dodag_id); + } + Repr::DestinationAdvertisementObjectAck { + rpl_instance_id, + sequence, + status, + dodag_id, + .. + } => { + packet.set_msg_code(RplControlMessage::DestinationAdvertisementObjectAck.into()); + packet.set_rpl_instance_id((*rpl_instance_id).into()); + packet.set_dao_ack_sequence(*sequence); + packet.set_dao_ack_status(*status); + packet.set_dao_ack_dodag_id(*dodag_id); + } + } + + let options = match self { + Repr::DodagInformationSolicitation { options } => &options[..], + Repr::DodagInformationObject { options, .. } => &options[..], + Repr::DestinationAdvertisementObject { options, .. } => &options[..], + Repr::DestinationAdvertisementObjectAck { .. } => &[], + }; + + packet.options_mut().copy_from_slice(options); + } +} + +pub mod options { + use byteorder::{ByteOrder, NetworkEndian}; + + use super::{Error, InstanceId, Result}; + use crate::wire::ipv6::Address; + + /// A read/write wrapper around a RPL Control Message Option. + #[derive(Debug, Clone)] + pub struct Packet> { + buffer: T, + } + + enum_with_unknown! { + pub enum OptionType(u8) { + Pad1 = 0x00, + PadN = 0x01, + DagMetricContainer = 0x02, + RouteInformation = 0x03, + DodagConfiguration = 0x04, + RplTarget = 0x05, + TransitInformation = 0x06, + SolicitedInformation = 0x07, + PrefixInformation = 0x08, + RplTargetDescriptor = 0x09, + } + } + + impl From<&Repr<'_>> for OptionType { + fn from(repr: &Repr) -> Self { + match repr { + Repr::Pad1 => Self::Pad1, + Repr::PadN(_) => Self::PadN, + Repr::DagMetricContainer => Self::DagMetricContainer, + Repr::RouteInformation { .. } => Self::RouteInformation, + Repr::DodagConfiguration { .. } => Self::DodagConfiguration, + Repr::RplTarget { .. } => Self::RplTarget, + Repr::TransitInformation { .. } => Self::TransitInformation, + Repr::SolicitedInformation { .. } => Self::SolicitedInformation, + Repr::PrefixInformation { .. } => Self::PrefixInformation, + Repr::RplTargetDescriptor { .. } => Self::RplTargetDescriptor, + } + } + } + + mod field { + use crate::wire::field::*; + + // Generic fields. + pub const TYPE: usize = 0; + pub const LENGTH: usize = 1; + + pub const PADN: Rest = 2..; + + // Route Information fields. + pub const ROUTE_INFO_PREFIX_LENGTH: usize = 2; + pub const ROUTE_INFO_RESERVED: usize = 3; + pub const ROUTE_INFO_PREFERENCE: usize = 3; + pub const ROUTE_INFO_LIFETIME: Field = 4..9; + + // DODAG Configuration fields. + pub const DODAG_CONF_FLAGS: usize = 2; + pub const DODAG_CONF_AUTHENTICATION_ENABLED: usize = 2; + pub const DODAG_CONF_PATH_CONTROL_SIZE: usize = 2; + pub const DODAG_CONF_DIO_INTERVAL_DOUBLINGS: usize = 3; + pub const DODAG_CONF_DIO_INTERVAL_MINIMUM: usize = 4; + pub const DODAG_CONF_DIO_REDUNDANCY_CONSTANT: usize = 5; + pub const DODAG_CONF_DIO_MAX_RANK_INCREASE: Field = 6..8; + pub const DODAG_CONF_MIN_HOP_RANK_INCREASE: Field = 8..10; + pub const DODAG_CONF_OBJECTIVE_CODE_POINT: Field = 10..12; + pub const DODAG_CONF_DEFAULT_LIFETIME: usize = 13; + pub const DODAG_CONF_LIFETIME_UNIT: Field = 14..16; + + // RPL Target fields. + pub const RPL_TARGET_FLAGS: usize = 2; + pub const RPL_TARGET_PREFIX_LENGTH: usize = 3; + + // Transit Information fields. + pub const TRANSIT_INFO_FLAGS: usize = 2; + pub const TRANSIT_INFO_EXTERNAL: usize = 2; + pub const TRANSIT_INFO_PATH_CONTROL: usize = 3; + pub const TRANSIT_INFO_PATH_SEQUENCE: usize = 4; + pub const TRANSIT_INFO_PATH_LIFETIME: usize = 5; + pub const TRANSIT_INFO_PARENT_ADDRESS: Field = 6..6 + 16; + + // Solicited Information fields. + pub const SOLICITED_INFO_RPL_INSTANCE_ID: usize = 2; + pub const SOLICITED_INFO_FLAGS: usize = 3; + pub const SOLICITED_INFO_VERSION_PREDICATE: usize = 3; + pub const SOLICITED_INFO_INSTANCE_ID_PREDICATE: usize = 3; + pub const SOLICITED_INFO_DODAG_ID_PREDICATE: usize = 3; + pub const SOLICITED_INFO_DODAG_ID: Field = 4..20; + pub const SOLICITED_INFO_VERSION_NUMBER: usize = 20; + + // Prefix Information fields. + pub const PREFIX_INFO_PREFIX_LENGTH: usize = 2; + pub const PREFIX_INFO_RESERVED1: usize = 3; + pub const PREFIX_INFO_ON_LINK: usize = 3; + pub const PREFIX_INFO_AUTONOMOUS_CONF: usize = 3; + pub const PREFIX_INFO_ROUTER_ADDRESS_FLAG: usize = 3; + pub const PREFIX_INFO_VALID_LIFETIME: Field = 4..8; + pub const PREFIX_INFO_PREFERRED_LIFETIME: Field = 8..12; + pub const PREFIX_INFO_RESERVED2: Field = 12..16; + pub const PREFIX_INFO_PREFIX: Field = 16..16 + 16; + + // RPL Target Descriptor fields. + pub const TARGET_DESCRIPTOR: Field = 2..6; + } + + /// Getters for the RPL Control Message Options. + impl> Packet { + /// Imbue a raw octet buffer with RPL Control Message Option structure. + #[inline] + pub fn new_unchecked(buffer: T) -> Self { + Packet { buffer } + } + + #[inline] + pub fn new_checked(buffer: T) -> Result { + if buffer.as_ref().is_empty() { + return Err(Error); + } + + Ok(Packet { buffer }) + } + + /// Return the type field. + #[inline] + pub fn option_type(&self) -> OptionType { + OptionType::from(self.buffer.as_ref()[field::TYPE]) + } + + /// Return the length field. + #[inline] + pub fn option_length(&self) -> u8 { + get!(self.buffer, field: field::LENGTH) + } + } + + impl<'p, T: AsRef<[u8]> + ?Sized> Packet<&'p T> { + /// Return a pointer to the next option. + #[inline] + pub fn next_option(&self) -> Option<&'p [u8]> { + if !self.buffer.as_ref().is_empty() { + match self.option_type() { + OptionType::Pad1 => Some(&self.buffer.as_ref()[1..]), + OptionType::Unknown(_) => unreachable!(), + _ => { + let len = self.option_length(); + Some(&self.buffer.as_ref()[2 + len as usize..]) + } + } + } else { + None + } + } + } + + impl + AsMut<[u8]>> Packet { + /// Set the Option Type field. + #[inline] + pub fn set_option_type(&mut self, option_type: OptionType) { + self.buffer.as_mut()[field::TYPE] = option_type.into(); + } + + /// Set the Option Length field. + #[inline] + pub fn set_option_length(&mut self, length: u8) { + self.buffer.as_mut()[field::LENGTH] = length; + } + } + + impl + AsMut<[u8]>> Packet { + #[inline] + pub fn clear_padn(&mut self, size: u8) { + for b in &mut self.buffer.as_mut()[field::PADN][..size as usize] { + *b = 0; + } + } + } + + /// Getters for the DAG Metric Container Option Message. + + /// Getters for the Route Information Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x03 | Option Length | Prefix Length |Resvd|Prf|Resvd| + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Route Lifetime | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | | + /// . Prefix (Variable Length) . + /// . . + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the Prefix Length field. + #[inline] + pub fn prefix_length(&self) -> u8 { + get!(self.buffer, field: field::ROUTE_INFO_PREFIX_LENGTH) + } + + /// Return the Route Preference field. + #[inline] + pub fn route_preference(&self) -> u8 { + (self.buffer.as_ref()[field::ROUTE_INFO_PREFERENCE] & 0b0001_1000) >> 3 + } + + /// Return the Route Lifetime field. + #[inline] + pub fn route_lifetime(&self) -> u32 { + get!(self.buffer, u32, field: field::ROUTE_INFO_LIFETIME) + } + } + + impl<'p, T: AsRef<[u8]> + ?Sized> Packet<&'p T> { + /// Return the Prefix field. + #[inline] + pub fn prefix(&self) -> &'p [u8] { + let option_len = self.option_length(); + &self.buffer.as_ref()[field::ROUTE_INFO_LIFETIME.end..] + [..option_len as usize - field::ROUTE_INFO_LIFETIME.end] + } + } + + /// Setters for the Route Information Option Message. + impl + AsMut<[u8]>> Packet { + /// Set the Prefix Length field. + #[inline] + pub fn set_route_info_prefix_length(&mut self, value: u8) { + set!(self.buffer, value, field: field::ROUTE_INFO_PREFIX_LENGTH) + } + + /// Set the Route Preference field. + #[inline] + pub fn set_route_info_route_preference(&mut self, _value: u8) { + todo!(); + } + + /// Set the Route Lifetime field. + #[inline] + pub fn set_route_info_route_lifetime(&mut self, value: u32) { + set!(self.buffer, value, u32, field: field::ROUTE_INFO_LIFETIME) + } + + /// Set the prefix field. + #[inline] + pub fn set_route_info_prefix(&mut self, _prefix: &[u8]) { + todo!(); + } + + /// Clear the reserved field. + #[inline] + pub fn clear_route_info_reserved(&mut self) { + self.buffer.as_mut()[field::ROUTE_INFO_RESERVED] = 0; + } + } + + /// Getters for the DODAG Configuration Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x04 |Opt Length = 14| Flags |A| PCS | DIOIntDoubl. | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | DIOIntMin. | DIORedun. | MaxRankIncrease | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | MinHopRankIncrease | OCP | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Reserved | Def. Lifetime | Lifetime Unit | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the Authentication Enabled field. + #[inline] + pub fn authentication_enabled(&self) -> bool { + get!( + self.buffer, + bool, + field: field::DODAG_CONF_AUTHENTICATION_ENABLED, + shift: 3, + mask: 0b1 + ) + } + + /// Return the Path Control Size field. + #[inline] + pub fn path_control_size(&self) -> u8 { + get!(self.buffer, field: field::DODAG_CONF_PATH_CONTROL_SIZE, mask: 0b111) + } + + /// Return the DIO Interval Doublings field. + #[inline] + pub fn dio_interval_doublings(&self) -> u8 { + get!(self.buffer, field: field::DODAG_CONF_DIO_INTERVAL_DOUBLINGS) + } + + /// Return the DIO Interval Minimum field. + #[inline] + pub fn dio_interval_minimum(&self) -> u8 { + get!(self.buffer, field: field::DODAG_CONF_DIO_INTERVAL_MINIMUM) + } + + /// Return the DIO Redundancy Constant field. + #[inline] + pub fn dio_redundancy_constant(&self) -> u8 { + get!( + self.buffer, + field: field::DODAG_CONF_DIO_REDUNDANCY_CONSTANT + ) + } + + /// Return the Max Rank Increase field. + #[inline] + pub fn max_rank_increase(&self) -> u16 { + get!( + self.buffer, + u16, + field: field::DODAG_CONF_DIO_MAX_RANK_INCREASE + ) + } + + /// Return the Minimum Hop Rank Increase field. + #[inline] + pub fn minimum_hop_rank_increase(&self) -> u16 { + get!( + self.buffer, + u16, + field: field::DODAG_CONF_MIN_HOP_RANK_INCREASE + ) + } + + /// Return the Objective Code Point field. + #[inline] + pub fn objective_code_point(&self) -> u16 { + get!( + self.buffer, + u16, + field: field::DODAG_CONF_OBJECTIVE_CODE_POINT + ) + } + + /// Return the Default Lifetime field. + #[inline] + pub fn default_lifetime(&self) -> u8 { + get!(self.buffer, field: field::DODAG_CONF_DEFAULT_LIFETIME) + } + + /// Return the Lifetime Unit field. + #[inline] + pub fn lifetime_unit(&self) -> u16 { + get!(self.buffer, u16, field: field::DODAG_CONF_LIFETIME_UNIT) + } + } + + /// Getters for the DODAG Configuration Option Message. + impl + AsMut<[u8]>> Packet { + /// Clear the Flags field. + #[inline] + pub fn clear_dodag_conf_flags(&mut self) { + self.buffer.as_mut()[field::DODAG_CONF_FLAGS] = 0; + } + + /// Set the Authentication Enabled field. + #[inline] + pub fn set_dodag_conf_authentication_enabled(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::DODAG_CONF_AUTHENTICATION_ENABLED, + shift: 3, + mask: 0b1 + ) + } + + /// Set the Path Control Size field. + #[inline] + pub fn set_dodag_conf_path_control_size(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::DODAG_CONF_PATH_CONTROL_SIZE, + mask: 0b111 + ) + } + + /// Set the DIO Interval Doublings field. + #[inline] + pub fn set_dodag_conf_dio_interval_doublings(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::DODAG_CONF_DIO_INTERVAL_DOUBLINGS + ) + } + + /// Set the DIO Interval Minimum field. + #[inline] + pub fn set_dodag_conf_dio_interval_minimum(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::DODAG_CONF_DIO_INTERVAL_MINIMUM + ) + } + + /// Set the DIO Redundancy Constant field. + #[inline] + pub fn set_dodag_conf_dio_redundancy_constant(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::DODAG_CONF_DIO_REDUNDANCY_CONSTANT + ) + } + + /// Set the Max Rank Increase field. + #[inline] + pub fn set_dodag_conf_max_rank_increase(&mut self, value: u16) { + set!( + self.buffer, + value, + u16, + field: field::DODAG_CONF_DIO_MAX_RANK_INCREASE + ) + } + + /// Set the Minimum Hop Rank Increase field. + #[inline] + pub fn set_dodag_conf_minimum_hop_rank_increase(&mut self, value: u16) { + set!( + self.buffer, + value, + u16, + field: field::DODAG_CONF_MIN_HOP_RANK_INCREASE + ) + } + + /// Set the Objective Code Point field. + #[inline] + pub fn set_dodag_conf_objective_code_point(&mut self, value: u16) { + set!( + self.buffer, + value, + u16, + field: field::DODAG_CONF_OBJECTIVE_CODE_POINT + ) + } + + /// Set the Default Lifetime field. + #[inline] + pub fn set_dodag_conf_default_lifetime(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::DODAG_CONF_DEFAULT_LIFETIME + ) + } + + /// Set the Lifetime Unit field. + #[inline] + pub fn set_dodag_conf_lifetime_unit(&mut self, value: u16) { + set!( + self.buffer, + value, + u16, + field: field::DODAG_CONF_LIFETIME_UNIT + ) + } + } + + /// Getters for the RPL Target Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x05 | Option Length | Flags | Prefix Length | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | | + /// + + + /// | Target Prefix (Variable Length) | + /// . . + /// . . + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the Target Prefix Length field. + pub fn target_prefix_length(&self) -> u8 { + get!(self.buffer, field: field::RPL_TARGET_PREFIX_LENGTH) + } + } + + impl<'p, T: AsRef<[u8]> + ?Sized> Packet<&'p T> { + /// Return the Target Prefix field. + #[inline] + pub fn target_prefix(&self) -> &'p [u8] { + let option_len = self.option_length(); + &self.buffer.as_ref()[field::RPL_TARGET_PREFIX_LENGTH + 1..] + [..option_len as usize - field::RPL_TARGET_PREFIX_LENGTH + 1] + } + } + + /// Setters for the RPL Target Option Message. + impl + AsMut<[u8]>> Packet { + /// Clear the Flags field. + #[inline] + pub fn clear_rpl_target_flags(&mut self) { + self.buffer.as_mut()[field::RPL_TARGET_FLAGS] = 0; + } + + /// Set the Target Prefix Length field. + #[inline] + pub fn set_rpl_target_prefix_length(&mut self, value: u8) { + set!(self.buffer, value, field: field::RPL_TARGET_PREFIX_LENGTH) + } + + /// Set the Target Prefix field. + #[inline] + pub fn set_rpl_target_prefix(&mut self, prefix: &[u8]) { + self.buffer.as_mut()[field::RPL_TARGET_PREFIX_LENGTH + 1..][..prefix.len()] + .copy_from_slice(prefix); + } + } + + /// Getters for the Transit Information Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x06 | Option Length |E| Flags | Path Control | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Path Sequence | Path Lifetime | | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + /// | | + /// + + + /// | | + /// + Parent Address* + + /// | | + /// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the External flag. + #[inline] + pub fn is_external(&self) -> bool { + get!( + self.buffer, + bool, + field: field::TRANSIT_INFO_EXTERNAL, + shift: 7, + mask: 0b1, + ) + } + + /// Return the Path Control field. + #[inline] + pub fn path_control(&self) -> u8 { + get!(self.buffer, field: field::TRANSIT_INFO_PATH_CONTROL) + } + + /// Return the Path Sequence field. + #[inline] + pub fn path_sequence(&self) -> u8 { + get!(self.buffer, field: field::TRANSIT_INFO_PATH_SEQUENCE) + } + + /// Return the Path Lifetime field. + #[inline] + pub fn path_lifetime(&self) -> u8 { + get!(self.buffer, field: field::TRANSIT_INFO_PATH_LIFETIME) + } + + /// Return the Parent Address field. + #[inline] + pub fn parent_address(&self) -> Option
{ + if self.option_length() > 5 { + Some(Address::from_bytes( + &self.buffer.as_ref()[field::TRANSIT_INFO_PARENT_ADDRESS], + )) + } else { + None + } + } + } + + /// Setters for the Transit Information Option Message. + impl + AsMut<[u8]>> Packet { + /// Clear the Flags field. + #[inline] + pub fn clear_transit_info_flags(&mut self) { + self.buffer.as_mut()[field::TRANSIT_INFO_FLAGS] = 0; + } + + /// Set the External flag. + #[inline] + pub fn set_transit_info_is_external(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::TRANSIT_INFO_EXTERNAL, + shift: 7, + mask: 0b1 + ) + } + + /// Set the Path Control field. + #[inline] + pub fn set_transit_info_path_control(&mut self, value: u8) { + set!(self.buffer, value, field: field::TRANSIT_INFO_PATH_CONTROL) + } + + /// Set the Path Sequence field. + #[inline] + pub fn set_transit_info_path_sequence(&mut self, value: u8) { + set!(self.buffer, value, field: field::TRANSIT_INFO_PATH_SEQUENCE) + } + + /// Set the Path Lifetime field. + #[inline] + pub fn set_transit_info_path_lifetime(&mut self, value: u8) { + set!(self.buffer, value, field: field::TRANSIT_INFO_PATH_LIFETIME) + } + + /// Set the Parent Address field. + #[inline] + pub fn set_transit_info_parent_address(&mut self, address: Address) { + self.buffer.as_mut()[field::TRANSIT_INFO_PARENT_ADDRESS] + .copy_from_slice(address.as_bytes()); + } + } + + /// Getters for the Solicited Information Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x07 |Opt Length = 19| RPLInstanceID |V|I|D| Flags | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | | + /// + + + /// | | + /// + DODAGID + + /// | | + /// + + + /// | | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// |Version Number | + /// +-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the RPL Instance ID field. + #[inline] + pub fn rpl_instance_id(&self) -> u8 { + get!(self.buffer, field: field::SOLICITED_INFO_RPL_INSTANCE_ID) + } + + /// Return the Version Predicate flag. + #[inline] + pub fn version_predicate(&self) -> bool { + get!( + self.buffer, + bool, + field: field::SOLICITED_INFO_VERSION_PREDICATE, + shift: 7, + mask: 0b1, + ) + } + + /// Return the Instance ID Predicate flag. + #[inline] + pub fn instance_id_predicate(&self) -> bool { + get!( + self.buffer, + bool, + field: field::SOLICITED_INFO_INSTANCE_ID_PREDICATE, + shift: 6, + mask: 0b1, + ) + } + + /// Return the DODAG Predicate ID flag. + #[inline] + pub fn dodag_id_predicate(&self) -> bool { + get!( + self.buffer, + bool, + field: field::SOLICITED_INFO_DODAG_ID_PREDICATE, + shift: 5, + mask: 0b1, + ) + } + + /// Return the DODAG ID field. + #[inline] + pub fn dodag_id(&self) -> Address { + get!( + self.buffer, + into: Address, + fun: from_bytes, + field: field::SOLICITED_INFO_DODAG_ID + ) + } + + /// Return the Version Number field. + #[inline] + pub fn version_number(&self) -> u8 { + get!(self.buffer, field: field::SOLICITED_INFO_VERSION_NUMBER) + } + } + + /// Setters for the Solicited Information Option Message. + impl + AsMut<[u8]>> Packet { + /// Clear the Flags field. + #[inline] + pub fn clear_solicited_info_flags(&mut self) { + self.buffer.as_mut()[field::SOLICITED_INFO_FLAGS] = 0; + } + + /// Set the RPL Instance ID field. + #[inline] + pub fn set_solicited_info_rpl_instance_id(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::SOLICITED_INFO_RPL_INSTANCE_ID + ) + } + + /// Set the Version Predicate flag. + #[inline] + pub fn set_solicited_info_version_predicate(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::SOLICITED_INFO_VERSION_PREDICATE, + shift: 7, + mask: 0b1 + ) + } + + /// Set the Instance ID Predicate flag. + #[inline] + pub fn set_solicited_info_instance_id_predicate(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::SOLICITED_INFO_INSTANCE_ID_PREDICATE, + shift: 6, + mask: 0b1 + ) + } + + /// Set the DODAG Predicate ID flag. + #[inline] + pub fn set_solicited_info_dodag_id_predicate(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::SOLICITED_INFO_DODAG_ID_PREDICATE, + shift: 5, + mask: 0b1 + ) + } + + /// Set the DODAG ID field. + #[inline] + pub fn set_solicited_info_dodag_id(&mut self, address: Address) { + set!( + self.buffer, + address: address, + field: field::SOLICITED_INFO_DODAG_ID + ) + } + + /// Set the Version Number field. + #[inline] + pub fn set_solicited_info_version_number(&mut self, value: u8) { + set!( + self.buffer, + value, + field: field::SOLICITED_INFO_VERSION_NUMBER + ) + } + } + + /// Getters for the Prefix Information Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x08 |Opt Length = 30| Prefix Length |L|A|R|Reserved1| + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Valid Lifetime | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Preferred Lifetime | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Reserved2 | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | | + /// + + + /// | | + /// + Prefix + + /// | | + /// + + + /// | | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the Prefix Length field. + #[inline] + pub fn prefix_info_prefix_length(&self) -> u8 { + get!(self.buffer, field: field::PREFIX_INFO_PREFIX_LENGTH) + } + + /// Return the On-Link flag. + #[inline] + pub fn on_link(&self) -> bool { + get!( + self.buffer, + bool, + field: field::PREFIX_INFO_ON_LINK, + shift: 7, + mask: 0b1, + ) + } + + /// Return the Autonomous Address-Configuration flag. + #[inline] + pub fn autonomous_address_configuration(&self) -> bool { + get!( + self.buffer, + bool, + field: field::PREFIX_INFO_AUTONOMOUS_CONF, + shift: 6, + mask: 0b1, + ) + } + + /// Return the Router Address flag. + #[inline] + pub fn router_address(&self) -> bool { + get!( + self.buffer, + bool, + field: field::PREFIX_INFO_ROUTER_ADDRESS_FLAG, + shift: 5, + mask: 0b1, + ) + } + + /// Return the Valid Lifetime field. + #[inline] + pub fn valid_lifetime(&self) -> u32 { + get!(self.buffer, u32, field: field::PREFIX_INFO_VALID_LIFETIME) + } + + /// Return the Preferred Lifetime field. + #[inline] + pub fn preferred_lifetime(&self) -> u32 { + get!( + self.buffer, + u32, + field: field::PREFIX_INFO_PREFERRED_LIFETIME + ) + } + } + + impl<'p, T: AsRef<[u8]> + ?Sized> Packet<&'p T> { + /// Return the Prefix field. + #[inline] + pub fn destination_prefix(&self) -> &'p [u8] { + &self.buffer.as_ref()[field::PREFIX_INFO_PREFIX] + } + } + + /// Setters for the Prefix Information Option Message. + impl + AsMut<[u8]>> Packet { + /// Clear the reserved fields. + #[inline] + pub fn clear_prefix_info_reserved(&mut self) { + self.buffer.as_mut()[field::PREFIX_INFO_RESERVED1] = 0; + self.buffer.as_mut()[field::PREFIX_INFO_RESERVED2].copy_from_slice(&[0; 4]); + } + + /// Set the Prefix Length field. + #[inline] + pub fn set_prefix_info_prefix_length(&mut self, value: u8) { + set!(self.buffer, value, field: field::PREFIX_INFO_PREFIX_LENGTH) + } + + /// Set the On-Link flag. + #[inline] + pub fn set_prefix_info_on_link(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::PREFIX_INFO_ON_LINK, shift: 7, mask: 0b1) + } + + /// Set the Autonomous Address-Configuration flag. + #[inline] + pub fn set_prefix_info_autonomous_address_configuration(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::PREFIX_INFO_AUTONOMOUS_CONF, + shift: 6, + mask: 0b1 + ) + } + + /// Set the Router Address flag. + #[inline] + pub fn set_prefix_info_router_address(&mut self, value: bool) { + set!( + self.buffer, + value, + bool, + field: field::PREFIX_INFO_ROUTER_ADDRESS_FLAG, + shift: 5, + mask: 0b1 + ) + } + + /// Set the Valid Lifetime field. + #[inline] + pub fn set_prefix_info_valid_lifetime(&mut self, value: u32) { + set!( + self.buffer, + value, + u32, + field: field::PREFIX_INFO_VALID_LIFETIME + ) + } + + /// Set the Preferred Lifetime field. + #[inline] + pub fn set_prefix_info_preferred_lifetime(&mut self, value: u32) { + set!( + self.buffer, + value, + u32, + field: field::PREFIX_INFO_PREFERRED_LIFETIME + ) + } + + /// Set the Prefix field. + #[inline] + pub fn set_prefix_info_destination_prefix(&mut self, prefix: &[u8]) { + self.buffer.as_mut()[field::PREFIX_INFO_PREFIX].copy_from_slice(prefix); + } + } + + /// Getters for the RPL Target Descriptor Option Message. + /// + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Type = 0x09 |Opt Length = 4 | Descriptor + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// Descriptor (cont.) | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + impl> Packet { + /// Return the Descriptor field. + #[inline] + pub fn descriptor(&self) -> u32 { + get!(self.buffer, u32, field: field::TARGET_DESCRIPTOR) + } + } + + /// Setters for the RPL Target Descriptor Option Message. + impl + AsMut<[u8]>> Packet { + /// Set the Descriptor field. + #[inline] + pub fn set_rpl_target_descriptor_descriptor(&mut self, value: u32) { + set!(self.buffer, value, u32, field: field::TARGET_DESCRIPTOR) + } + } + + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum Repr<'p> { + Pad1, + PadN(u8), + DagMetricContainer, + RouteInformation { + prefix_length: u8, + preference: u8, + lifetime: u32, + prefix: &'p [u8], + }, + DodagConfiguration { + authentication_enabled: bool, + path_control_size: u8, + dio_interval_doublings: u8, + dio_interval_min: u8, + dio_redundancy_constant: u8, + max_rank_increase: u16, + minimum_hop_rank_increase: u16, + objective_code_point: u16, + default_lifetime: u8, + lifetime_unit: u16, + }, + RplTarget { + prefix_length: u8, + prefix: crate::wire::Ipv6Address, // FIXME: this is not the correct type, because the + // field can be an IPv6 address, a prefix or a + // multicast group. + }, + TransitInformation { + external: bool, + path_control: u8, + path_sequence: u8, + path_lifetime: u8, + parent_address: Option
, + }, + SolicitedInformation { + rpl_instance_id: InstanceId, + version_predicate: bool, + instance_id_predicate: bool, + dodag_id_predicate: bool, + dodag_id: Address, + version_number: u8, + }, + PrefixInformation { + prefix_length: u8, + on_link: bool, + autonomous_address_configuration: bool, + router_address: bool, + valid_lifetime: u32, + preferred_lifetime: u32, + destination_prefix: &'p [u8], + }, + RplTargetDescriptor { + descriptor: u32, + }, + } + + impl core::fmt::Display for Repr<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Repr::Pad1 => write!(f, "Pad1"), + Repr::PadN(n) => write!(f, "PadN({n})"), + Repr::DagMetricContainer => todo!(), + Repr::RouteInformation { + prefix_length, + preference, + lifetime, + prefix, + } => { + write!( + f, + "ROUTE INFO \ + PrefixLength={prefix_length} \ + Preference={preference} \ + Lifetime={lifetime} \ + Prefix={prefix:0x?}" + ) + } + Repr::DodagConfiguration { + dio_interval_doublings, + dio_interval_min, + dio_redundancy_constant, + max_rank_increase, + minimum_hop_rank_increase, + objective_code_point, + default_lifetime, + lifetime_unit, + .. + } => { + write!( + f, + "DODAG CONF \ + IntD={dio_interval_doublings} \ + IntMin={dio_interval_min} \ + RedCst={dio_redundancy_constant} \ + MaxRankIncr={max_rank_increase} \ + MinHopRankIncr={minimum_hop_rank_increase} \ + OCP={objective_code_point} \ + DefaultLifetime={default_lifetime} \ + LifeUnit={lifetime_unit}" + ) + } + Repr::RplTarget { + prefix_length, + prefix, + } => { + write!( + f, + "RPL Target \ + PrefixLength={prefix_length} \ + Prefix={prefix:0x?}" + ) + } + Repr::TransitInformation { + external, + path_control, + path_sequence, + path_lifetime, + parent_address, + } => { + write!( + f, + "Transit Info \ + External={external} \ + PathCtrl={path_control} \ + PathSqnc={path_sequence} \ + PathLifetime={path_lifetime} \ + Parent={parent_address:0x?}" + ) + } + Repr::SolicitedInformation { + rpl_instance_id, + version_predicate, + instance_id_predicate, + dodag_id_predicate, + dodag_id, + version_number, + } => { + write!( + f, + "Solicited Info \ + I={instance_id_predicate} \ + IID={rpl_instance_id:0x?} \ + D={dodag_id_predicate} \ + DODAGID={dodag_id} \ + V={version_predicate} \ + Version={version_number}" + ) + } + Repr::PrefixInformation { + prefix_length, + on_link, + autonomous_address_configuration, + router_address, + valid_lifetime, + preferred_lifetime, + destination_prefix, + } => { + write!( + f, + "Prefix Info \ + PrefixLength={prefix_length} \ + L={on_link} A={autonomous_address_configuration} R={router_address} \ + Valid={valid_lifetime} \ + Prefered={preferred_lifetime} \ + Prefix={destination_prefix:0x?}" + ) + } + Repr::RplTargetDescriptor { .. } => write!(f, "Target Descriptor"), + } + } + } + + impl<'p> Repr<'p> { + pub fn parse + ?Sized>(packet: &Packet<&'p T>) -> Result { + match packet.option_type() { + OptionType::Pad1 => Ok(Repr::Pad1), + OptionType::PadN => Ok(Repr::PadN(packet.option_length())), + OptionType::DagMetricContainer => todo!(), + OptionType::RouteInformation => Ok(Repr::RouteInformation { + prefix_length: packet.prefix_length(), + preference: packet.route_preference(), + lifetime: packet.route_lifetime(), + prefix: packet.prefix(), + }), + OptionType::DodagConfiguration => Ok(Repr::DodagConfiguration { + authentication_enabled: packet.authentication_enabled(), + path_control_size: packet.path_control_size(), + dio_interval_doublings: packet.dio_interval_doublings(), + dio_interval_min: packet.dio_interval_minimum(), + dio_redundancy_constant: packet.dio_redundancy_constant(), + max_rank_increase: packet.max_rank_increase(), + minimum_hop_rank_increase: packet.minimum_hop_rank_increase(), + objective_code_point: packet.objective_code_point(), + default_lifetime: packet.default_lifetime(), + lifetime_unit: packet.lifetime_unit(), + }), + OptionType::RplTarget => Ok(Repr::RplTarget { + prefix_length: packet.target_prefix_length(), + prefix: crate::wire::Ipv6Address::from_bytes(packet.target_prefix()), + }), + OptionType::TransitInformation => Ok(Repr::TransitInformation { + external: packet.is_external(), + path_control: packet.path_control(), + path_sequence: packet.path_sequence(), + path_lifetime: packet.path_lifetime(), + parent_address: packet.parent_address(), + }), + OptionType::SolicitedInformation => Ok(Repr::SolicitedInformation { + rpl_instance_id: InstanceId::from(packet.rpl_instance_id()), + version_predicate: packet.version_predicate(), + instance_id_predicate: packet.instance_id_predicate(), + dodag_id_predicate: packet.dodag_id_predicate(), + dodag_id: packet.dodag_id(), + version_number: packet.version_number(), + }), + OptionType::PrefixInformation => Ok(Repr::PrefixInformation { + prefix_length: packet.prefix_info_prefix_length(), + on_link: packet.on_link(), + autonomous_address_configuration: packet.autonomous_address_configuration(), + router_address: packet.router_address(), + valid_lifetime: packet.valid_lifetime(), + preferred_lifetime: packet.preferred_lifetime(), + destination_prefix: packet.destination_prefix(), + }), + OptionType::RplTargetDescriptor => Ok(Repr::RplTargetDescriptor { + descriptor: packet.descriptor(), + }), + OptionType::Unknown(_) => Err(Error), + } + } + + pub fn buffer_len(&self) -> usize { + match self { + Repr::Pad1 => 1, + Repr::PadN(size) => 2 + *size as usize, + Repr::DagMetricContainer => todo!(), + Repr::RouteInformation { prefix, .. } => 2 + 6 + prefix.len(), + Repr::DodagConfiguration { .. } => 2 + 14, + Repr::RplTarget { prefix, .. } => 2 + 2 + prefix.0.len(), + Repr::TransitInformation { parent_address, .. } => { + 2 + 4 + if parent_address.is_some() { 16 } else { 0 } + } + Repr::SolicitedInformation { .. } => 2 + 2 + 16 + 1, + Repr::PrefixInformation { .. } => 32, + Repr::RplTargetDescriptor { .. } => 2 + 4, + } + } + + pub fn emit + AsMut<[u8]> + ?Sized>(&self, packet: &mut Packet<&'p mut T>) { + let mut option_length = self.buffer_len() as u8; + + packet.set_option_type(self.into()); + + if !matches!(self, Repr::Pad1) { + option_length -= 2; + packet.set_option_length(option_length); + } + + match self { + Repr::Pad1 => {} + Repr::PadN(size) => { + packet.clear_padn(*size); + } + Repr::DagMetricContainer => { + unimplemented!(); + } + Repr::RouteInformation { + prefix_length, + preference, + lifetime, + prefix, + } => { + packet.clear_route_info_reserved(); + packet.set_route_info_prefix_length(*prefix_length); + packet.set_route_info_route_preference(*preference); + packet.set_route_info_route_lifetime(*lifetime); + packet.set_route_info_prefix(prefix); + } + Repr::DodagConfiguration { + authentication_enabled, + path_control_size, + dio_interval_doublings, + dio_interval_min, + dio_redundancy_constant, + max_rank_increase, + minimum_hop_rank_increase, + objective_code_point, + default_lifetime, + lifetime_unit, + } => { + packet.clear_dodag_conf_flags(); + packet.set_dodag_conf_authentication_enabled(*authentication_enabled); + packet.set_dodag_conf_path_control_size(*path_control_size); + packet.set_dodag_conf_dio_interval_doublings(*dio_interval_doublings); + packet.set_dodag_conf_dio_interval_minimum(*dio_interval_min); + packet.set_dodag_conf_dio_redundancy_constant(*dio_redundancy_constant); + packet.set_dodag_conf_max_rank_increase(*max_rank_increase); + packet.set_dodag_conf_minimum_hop_rank_increase(*minimum_hop_rank_increase); + packet.set_dodag_conf_objective_code_point(*objective_code_point); + packet.set_dodag_conf_default_lifetime(*default_lifetime); + packet.set_dodag_conf_lifetime_unit(*lifetime_unit); + } + Repr::RplTarget { + prefix_length, + prefix, + } => { + packet.clear_rpl_target_flags(); + packet.set_rpl_target_prefix_length(*prefix_length); + packet.set_rpl_target_prefix(prefix.as_bytes()); + } + Repr::TransitInformation { + external, + path_control, + path_sequence, + path_lifetime, + parent_address, + } => { + packet.clear_transit_info_flags(); + packet.set_transit_info_is_external(*external); + packet.set_transit_info_path_control(*path_control); + packet.set_transit_info_path_sequence(*path_sequence); + packet.set_transit_info_path_lifetime(*path_lifetime); + + if let Some(address) = parent_address { + packet.set_transit_info_parent_address(*address); + } + } + Repr::SolicitedInformation { + rpl_instance_id, + version_predicate, + instance_id_predicate, + dodag_id_predicate, + dodag_id, + version_number, + } => { + packet.clear_solicited_info_flags(); + packet.set_solicited_info_rpl_instance_id((*rpl_instance_id).into()); + packet.set_solicited_info_version_predicate(*version_predicate); + packet.set_solicited_info_instance_id_predicate(*instance_id_predicate); + packet.set_solicited_info_dodag_id_predicate(*dodag_id_predicate); + packet.set_solicited_info_version_number(*version_number); + packet.set_solicited_info_dodag_id(*dodag_id); + } + Repr::PrefixInformation { + prefix_length, + on_link, + autonomous_address_configuration, + router_address, + valid_lifetime, + preferred_lifetime, + destination_prefix, + } => { + packet.clear_prefix_info_reserved(); + packet.set_prefix_info_prefix_length(*prefix_length); + packet.set_prefix_info_on_link(*on_link); + packet.set_prefix_info_autonomous_address_configuration( + *autonomous_address_configuration, + ); + packet.set_prefix_info_router_address(*router_address); + packet.set_prefix_info_valid_lifetime(*valid_lifetime); + packet.set_prefix_info_preferred_lifetime(*preferred_lifetime); + packet.set_prefix_info_destination_prefix(destination_prefix); + } + Repr::RplTargetDescriptor { descriptor } => { + packet.set_rpl_target_descriptor_descriptor(*descriptor); + } + } + } + } +} + +pub mod data { + use super::{InstanceId, Result}; + use byteorder::{ByteOrder, NetworkEndian}; + + mod field { + use crate::wire::field::*; + + pub const FLAGS: usize = 0; + pub const INSTANCE_ID: usize = 1; + pub const SENDER_RANK: Field = 2..4; + } + + /// A read/write wrapper around a RPL Packet Information send with + /// an IPv6 Hop-by-Hop option, defined in RFC6553. + /// ```txt + /// 0 1 2 3 + /// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | Option Type | Opt Data Len | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// |O|R|F|0|0|0|0|0| RPLInstanceID | SenderRank | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// | (sub-TLVs) | + /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + /// ``` + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct Packet> { + buffer: T, + } + + impl> Packet { + #[inline] + pub fn new_unchecked(buffer: T) -> Self { + Self { buffer } + } + + #[inline] + pub fn new_checked(buffer: T) -> Result { + let packet = Self::new_unchecked(buffer); + packet.check_len()?; + Ok(packet) + } + + #[inline] + pub fn check_len(&self) -> Result<()> { + if self.buffer.as_ref().len() == 4 { + Ok(()) + } else { + Err(crate::wire::Error) + } + } + + #[inline] + pub fn is_down(&self) -> bool { + get!(self.buffer, bool, field: field::FLAGS, shift: 7, mask: 0b1) + } + + #[inline] + pub fn has_rank_error(&self) -> bool { + get!(self.buffer, bool, field: field::FLAGS, shift: 6, mask: 0b1) + } + + #[inline] + pub fn has_forwarding_error(&self) -> bool { + get!(self.buffer, bool, field: field::FLAGS, shift: 5, mask: 0b1) + } + + #[inline] + pub fn rpl_instance_id(&self) -> InstanceId { + get!(self.buffer, into: InstanceId, field: field::INSTANCE_ID) + } + + #[inline] + pub fn sender_rank(&self) -> u16 { + get!(self.buffer, u16, field: field::SENDER_RANK) + } + } + + impl + AsMut<[u8]>> Packet { + #[inline] + pub fn set_is_down(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::FLAGS, shift: 7, mask: 0b1) + } + + #[inline] + pub fn set_has_rank_error(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::FLAGS, shift: 6, mask: 0b1) + } + + #[inline] + pub fn set_has_forwarding_error(&mut self, value: bool) { + set!(self.buffer, value, bool, field: field::FLAGS, shift: 5, mask: 0b1) + } + + #[inline] + pub fn set_rpl_instance_id(&mut self, value: u8) { + set!(self.buffer, value, field: field::INSTANCE_ID) + } + + #[inline] + pub fn set_sender_rank(&mut self, value: u16) { + set!(self.buffer, value, u16, field: field::SENDER_RANK) + } + } + + /// A high-level representation of an IPv6 Extension Header Option. + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct HopByHopOption { + pub down: bool, + pub rank_error: bool, + pub forwarding_error: bool, + pub instance_id: InstanceId, + pub sender_rank: u16, + } + + impl HopByHopOption { + /// Parse an IPv6 Extension Header Option and return a high-level representation. + pub fn parse(opt: &Packet<&T>) -> Self + where + T: AsRef<[u8]> + ?Sized, + { + Self { + down: opt.is_down(), + rank_error: opt.has_rank_error(), + forwarding_error: opt.has_forwarding_error(), + instance_id: opt.rpl_instance_id(), + sender_rank: opt.sender_rank(), + } + } + + /// Return the length of a header that will be emitted from this high-level representation. + pub const fn buffer_len(&self) -> usize { + 4 + } + + /// Emit a high-level representation into an IPv6 Extension Header Option. + pub fn emit + AsMut<[u8]> + ?Sized>(&self, opt: &mut Packet<&mut T>) { + opt.set_is_down(self.down); + opt.set_has_rank_error(self.rank_error); + opt.set_has_forwarding_error(self.forwarding_error); + opt.set_rpl_instance_id(self.instance_id.into()); + opt.set_sender_rank(self.sender_rank); + } + } + + impl core::fmt::Display for HopByHopOption { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "down={} rank_error={} forw_error={} IID={:?} sender_rank={}", + self.down, + self.rank_error, + self.forwarding_error, + self.instance_id, + self.sender_rank + ) + } + } +} + +#[cfg(test)] +mod tests { + use super::options::{Packet as OptionPacket, Repr as OptionRepr}; + use super::Repr as RplRepr; + use super::*; + use crate::phy::ChecksumCapabilities; + use crate::wire::{icmpv6::*, *}; + + #[test] + fn dis_packet() { + let data = [0x7a, 0x3b, 0x3a, 0x1a, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00]; + + let ll_src_address = + Ieee802154Address::Extended([0x9e, 0xd3, 0xa2, 0x9c, 0x57, 0x1a, 0x4f, 0xe4]); + let ll_dst_address = Ieee802154Address::Short([0xff, 0xff]); + + let packet = SixlowpanIphcPacket::new_checked(&data).unwrap(); + let repr = + SixlowpanIphcRepr::parse(&packet, Some(ll_src_address), Some(ll_dst_address), &[]) + .unwrap(); + + let icmp_repr = match repr.next_header { + SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6) => { + let icmp_packet = Icmpv6Packet::new_checked(packet.payload()).unwrap(); + match Icmpv6Repr::parse( + &IpAddress::Ipv6(repr.src_addr), + &IpAddress::Ipv6(repr.dst_addr), + &icmp_packet, + &ChecksumCapabilities::ignored(), + ) { + Ok(icmp @ Icmpv6Repr::Rpl(RplRepr::DodagInformationSolicitation { .. })) => { + icmp + } + _ => unreachable!(), + } + } + _ => unreachable!(), + }; + + // We also try to emit the packet: + let mut buffer = vec![0u8; repr.buffer_len() + icmp_repr.buffer_len()]; + repr.emit(&mut SixlowpanIphcPacket::new_unchecked( + &mut buffer[..repr.buffer_len()], + )); + icmp_repr.emit( + &repr.src_addr.into(), + &repr.dst_addr.into(), + &mut Icmpv6Packet::new_unchecked( + &mut buffer[repr.buffer_len()..][..icmp_repr.buffer_len()], + ), + &ChecksumCapabilities::ignored(), + ); + + assert_eq!(&data[..], &buffer[..]); + } + + /// Parsing of DIO packets. + #[test] + fn dio_packet() { + let data = [ + 0x9b, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x80, 0x08, 0xf0, 0x00, 0x00, 0xfd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x04, 0x0e, 0x00, 0x08, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x1e, + 0x00, 0x3c, 0x08, 0x1e, 0x40, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let addr = Address::from_bytes(&[ + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, + ]); + + let dest_prefix = [ + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + + let packet = Packet::new_checked(&data[..]).unwrap(); + assert_eq!(packet.msg_type(), Message::RplControl); + assert_eq!( + RplControlMessage::from(packet.msg_code()), + RplControlMessage::DodagInformationObject + ); + + let mut dio_repr = RplRepr::parse(&packet).unwrap(); + match dio_repr { + RplRepr::DodagInformationObject { + rpl_instance_id, + version_number, + rank, + grounded, + mode_of_operation, + dodag_preference, + dtsn, + dodag_id, + .. + } => { + assert_eq!(rpl_instance_id, InstanceId::from(0)); + assert_eq!(version_number, 240); + assert_eq!(rank, 128); + assert!(!grounded); + assert_eq!(mode_of_operation, ModeOfOperation::NonStoringMode); + assert_eq!(dodag_preference, 0); + assert_eq!(dtsn, 240); + assert_eq!(dodag_id, addr); + } + _ => unreachable!(), + } + + let option = OptionPacket::new_unchecked(packet.options().unwrap()); + let dodag_conf_option = OptionRepr::parse(&option).unwrap(); + match dodag_conf_option { + OptionRepr::DodagConfiguration { + authentication_enabled, + path_control_size, + dio_interval_doublings, + dio_interval_min, + dio_redundancy_constant, + max_rank_increase, + minimum_hop_rank_increase, + objective_code_point, + default_lifetime, + lifetime_unit, + } => { + assert!(!authentication_enabled); + assert_eq!(path_control_size, 0); + assert_eq!(dio_interval_doublings, 8); + assert_eq!(dio_interval_min, 12); + assert_eq!(dio_redundancy_constant, 0); + assert_eq!(max_rank_increase, 1024); + assert_eq!(minimum_hop_rank_increase, 128); + assert_eq!(objective_code_point, 1); + assert_eq!(default_lifetime, 30); + assert_eq!(lifetime_unit, 60); + } + _ => unreachable!(), + } + + let option = OptionPacket::new_unchecked(option.next_option().unwrap()); + let prefix_info_option = OptionRepr::parse(&option).unwrap(); + match prefix_info_option { + OptionRepr::PrefixInformation { + prefix_length, + on_link, + autonomous_address_configuration, + valid_lifetime, + preferred_lifetime, + destination_prefix, + .. + } => { + assert_eq!(prefix_length, 64); + assert!(!on_link); + assert!(autonomous_address_configuration); + assert_eq!(valid_lifetime, u32::MAX); + assert_eq!(preferred_lifetime, u32::MAX); + assert_eq!(destination_prefix, &dest_prefix[..]); + } + _ => unreachable!(), + } + + let mut options_buffer = + vec![0u8; dodag_conf_option.buffer_len() + prefix_info_option.buffer_len()]; + + dodag_conf_option.emit(&mut OptionPacket::new_unchecked( + &mut options_buffer[..dodag_conf_option.buffer_len()], + )); + prefix_info_option.emit(&mut OptionPacket::new_unchecked( + &mut options_buffer[dodag_conf_option.buffer_len()..] + [..prefix_info_option.buffer_len()], + )); + + dio_repr.set_options(&options_buffer[..]); + + let mut buffer = vec![0u8; dio_repr.buffer_len()]; + dio_repr.emit(&mut Packet::new_unchecked(&mut buffer[..])); + + assert_eq!(&data[..], &buffer[..]); + } + + /// Parsing of DAO packets. + #[test] + fn dao_packet() { + let data = [ + 0x9b, 0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0xf1, 0x05, 0x12, 0x00, 0x80, 0xfd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, + 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + ]; + + let target_prefix = [ + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x02, + ]; + + let parent_addr = Address::from_bytes(&[ + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, + ]); + + let packet = Packet::new_checked(&data[..]).unwrap(); + let mut dao_repr = RplRepr::parse(&packet).unwrap(); + match dao_repr { + RplRepr::DestinationAdvertisementObject { + rpl_instance_id, + expect_ack, + sequence, + dodag_id, + .. + } => { + assert_eq!(rpl_instance_id, InstanceId::from(0)); + assert!(expect_ack); + assert_eq!(sequence, 241); + assert_eq!(dodag_id, None); + } + _ => unreachable!(), + } + + let option = OptionPacket::new_unchecked(packet.options().unwrap()); + + let rpl_target_option = OptionRepr::parse(&option).unwrap(); + match rpl_target_option { + OptionRepr::RplTarget { + prefix_length, + prefix, + } => { + assert_eq!(prefix_length, 128); + assert_eq!(prefix.as_bytes(), &target_prefix[..]); + } + _ => unreachable!(), + } + + let option = OptionPacket::new_unchecked(option.next_option().unwrap()); + let transit_info_option = OptionRepr::parse(&option).unwrap(); + match transit_info_option { + OptionRepr::TransitInformation { + external, + path_control, + path_sequence, + path_lifetime, + parent_address, + } => { + assert!(!external); + assert_eq!(path_control, 0); + assert_eq!(path_sequence, 0); + assert_eq!(path_lifetime, 30); + assert_eq!(parent_address, Some(parent_addr)); + } + _ => unreachable!(), + } + + let mut options_buffer = + vec![0u8; rpl_target_option.buffer_len() + transit_info_option.buffer_len()]; + + rpl_target_option.emit(&mut OptionPacket::new_unchecked( + &mut options_buffer[..rpl_target_option.buffer_len()], + )); + transit_info_option.emit(&mut OptionPacket::new_unchecked( + &mut options_buffer[rpl_target_option.buffer_len()..] + [..transit_info_option.buffer_len()], + )); + + dao_repr.set_options(&options_buffer[..]); + + let mut buffer = vec![0u8; dao_repr.buffer_len()]; + dao_repr.emit(&mut Packet::new_unchecked(&mut buffer[..])); + + assert_eq!(&data[..], &buffer[..]); + } + + /// Parsing of DAO-ACK packets. + #[test] + fn dao_ack_packet() { + let data = [0x9b, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00]; + + let packet = Packet::new_checked(&data[..]).unwrap(); + let dao_ack_repr = RplRepr::parse(&packet).unwrap(); + match dao_ack_repr { + RplRepr::DestinationAdvertisementObjectAck { + rpl_instance_id, + sequence, + status, + dodag_id, + .. + } => { + assert_eq!(rpl_instance_id, InstanceId::from(0)); + assert_eq!(sequence, 241); + assert_eq!(status, 0); + assert_eq!(dodag_id, None); + } + _ => unreachable!(), + } + + let mut buffer = vec![0u8; dao_ack_repr.buffer_len()]; + dao_ack_repr.emit(&mut Packet::new_unchecked(&mut buffer[..])); + + assert_eq!(&data[..], &buffer[..]); + } +} From 68a67fa2d582077a28bc445315d87175518c6806 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 14 Apr 2023 10:06:20 +0200 Subject: [PATCH 533/566] use shell script for CI tasks --- .github/workflows/clippy.yml | 25 -------- .github/workflows/test.yml | 108 ++++++++++++++--------------------- ci.sh | 106 ++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/clippy.yml create mode 100755 ci.sh diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml deleted file mode 100644 index 111b1ef99..000000000 --- a/.github/workflows/clippy.yml +++ /dev/null @@ -1,25 +0,0 @@ -on: - push: - branches: [ staging, trying ] - pull_request_target: - -name: Clippy check -jobs: - clippy: - runs-on: ubuntu-latest - permissions: - checks: write - steps: - - uses: actions/checkout@v2 - if: github.event_name == 'pull_request_target' - with: - ref: refs/pull/${{ github.event.number }}/head - - uses: actions/checkout@v2 - if: github.event_name != 'pull_request_target' - - run: sed -n 's,^rust-version = "\(.*\)"$,RUSTUP_TOOLCHAIN=\1,p' Cargo.toml >> $GITHUB_ENV - - run: rustup toolchain install $RUSTUP_TOOLCHAIN - - run: rustup component add clippy - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --tests --examples -- -D warnings diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2f87a5ccc..ba342127e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,82 +8,58 @@ name: Test jobs: tests: runs-on: ubuntu-22.04 - needs: [test, check] + needs: [check-msrv, test-msrv, test-stable, clippy] steps: - name: Done run: exit 0 - test: - runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.rust == 'nightly' }} - strategy: - matrix: - # Test on stable, MSRV, and nightly. - # Failure is permitted on nightly. - rust: - #- stable # TODO: enable again when "stable" is 1.66 or higher. - - 1.65.0 - - nightly - - features: - # Test default features. - - default - - # Test minimal featureset - - std proto-ipv4 - - # Test features chosen to be as orthogonal as possible. - - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp socket-dns - - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp - - std medium-ethernet proto-ipv4 proto-ipv4-fragmentation socket-raw socket-dns - - std medium-ethernet proto-ipv4 proto-igmp socket-raw socket-dns - - std medium-ethernet proto-ipv4 socket-udp socket-tcp socket-dns - - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp - - std medium-ethernet medium-ip medium-ieee802154 proto-ipv6 socket-udp socket-dns - - std medium-ethernet proto-ipv6 socket-tcp - - std medium-ethernet medium-ip proto-ipv4 socket-icmp socket-tcp - - std medium-ip proto-ipv6 socket-icmp socket-tcp - - std medium-ieee802154 proto-sixlowpan socket-udp - - std medium-ieee802154 proto-sixlowpan proto-sixlowpan-fragmentation socket-udp - - std medium-ip proto-ipv4 proto-ipv6 socket-tcp socket-udp - # Test features chosen to be as aggressive as possible. - - std medium-ethernet medium-ip medium-ieee802154 proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp socket-dns async + check-msrv: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: Run Checks MSRV + run: ./ci.sh check msrv - include: - # Test alloc feature which requires nightly. - - rust: nightly - features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp - env: - RUSTUP_TOOLCHAIN: "${{ matrix.rust }}" + test-msrv: + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - - name: Run Tests - run: cargo test --no-default-features --features "${{ matrix.features }}" + - name: Run Tests MSRV + run: ./ci.sh test msrv - check: + clippy: runs-on: ubuntu-22.04 - continue-on-error: ${{ matrix.rust == 'nightly' }} - strategy: - matrix: - # Test on stable, MSRV, and nightly. - # Failure is permitted on nightly. - rust: - #- stable # TODO: enable again when "stable" is 1.66 or higher. - - 1.65.0 - - nightly + steps: + - uses: actions/checkout@v2 + - name: Run Clippy + run: ./ci.sh clippy - features: - # These feature sets cannot run tests, so we only check they build. - - medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async - - defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async - - defmt alloc medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp socket-dns async + test-stable: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: Run Tests stable + run: ./ci.sh test stable - env: - # Set DEFMT_LOG to trace so that all net_{error, .., trace} messages - # are actually compiled and verified - DEFMT_LOG: "trace" - RUSTUP_TOOLCHAIN: "${{ matrix.rust }}" + test-nightly: + runs-on: ubuntu-22.04 + continue-on-error: true steps: - uses: actions/checkout@v2 - - name: Check - run: cargo check --no-default-features --features "${{ matrix.features }}" + - name: Run Tests nightly + run: ./ci.sh test nightly + + #check-stable: + #runs-on: ubuntu-22.04 + #steps: + #- uses: actions/checkout@v2 + #- name: Run Tests + #run: ./ci.sh check stable + + #check-nightly: + #runs-on: ubuntu-22.04 + #continue-on-error: true + #steps: + #- uses: actions/checkout@v2 + #- name: Run Tests + #run: ./ci.sh check nightly diff --git a/ci.sh b/ci.sh new file mode 100755 index 000000000..3db921a26 --- /dev/null +++ b/ci.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +set -eox pipefail + +export DEFMT_LOG=trace + +MSRV="1.65.0" + +RUSTC_VERSIONS=( + $MSRV + "stable" + "nightly" +) + +FEATURES_TEST=( + "default" + "std,proto-ipv4" + "std,medium-ethernet,phy-raw_socket,proto-ipv6,socket-udp,socket-dns" + "std,medium-ethernet,phy-tuntap_interface,proto-ipv6,socket-udp" + "std,medium-ethernet,proto-ipv4,proto-ipv4-fragmentation,socket-raw,socket-dns" + "std,medium-ethernet,proto-ipv4,proto-igmp,socket-raw,socket-dns" + "std,medium-ethernet,proto-ipv4,socket-udp,socket-tcp,socket-dns" + "std,medium-ethernet,proto-ipv4,proto-dhcpv4,socket-udp" + "std,medium-ethernet,medium-ip,medium-ieee802154,proto-ipv6,socket-udp,socket-dns" + "std,medium-ethernet,proto-ipv6,socket-tcp" + "std,medium-ethernet,medium-ip,proto-ipv4,socket-icmp,socket-tcp" + "std,medium-ip,proto-ipv6,socket-icmp,socket-tcp" + "std,medium-ieee802154,proto-sixlowpan,socket-udp" + "std,medium-ieee802154,proto-sixlowpan,proto-sixlowpan-fragmentation,socket-udp" + "std,medium-ip,proto-ipv4,proto-ipv6,socket-tcp,socket-udp" + "std,medium-ethernet,medium-ip,medium-ieee802154,proto-ipv4,proto-ipv6,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" +) + +FEATURES_TEST_NIGHTLY=( + "alloc,medium-ethernet,proto-ipv4,proto-ipv6,socket-raw,socket-udp,socket-tcp,socket-icmp" +) + +FEATURES_CHECK=( + "medium-ip,medium-ethernet,medium-ieee802154,proto-ipv6,proto-ipv6,proto-igmp,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" + "defmt,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,proto-igmp,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" + "defmt,alloc,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,proto-igmp,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" +) + +test() { + local version=$1 + rustup toolchain install $version + + for features in ${FEATURES_TEST[@]}; do + cargo +$version test --no-default-features --features "$features" + done + + if [[ $version == "nightly" ]]; then + for features in ${FEATURES_TEST_NIGHTLY[@]}; do + cargo +$version test --no-default-features --features "$features" + done + fi +} + +check() { + local version=$1 + rustup toolchain install $version + + export DEFMT_LOG="trace" + + for features in ${FEATURES_CHECK[@]}; do + cargo +$version check --no-default-features --features "$features" + done +} + +clippy() { + rustup toolchain install $MSRV + rustup component add clippy --toolchain=$MSRV + cargo +$MSRV clippy --tests --examples -- -D warnings +} + +if [[ $1 == "test" || $1 == "all" ]]; then + if [[ -n $2 ]]; then + if [[ $2 == "msrv" ]]; then + test $MSRV + else + test $2 + fi + else + for version in ${RUSTC_VERSIONS[@]}; do + test $version + done + fi +fi + +if [[ $1 == "check" || $1 == "all" ]]; then + if [[ -n $2 ]]; then + if [[ $2 == "msrv" ]]; then + check $MSRV + else + check $2 + fi + else + for version in ${RUSTC_VERSIONS[@]}; do + check $version + done + fi +fi + +if [[ $1 == "clippy" || $1 == "all" ]]; then + clippy +fi From 42dd475d3010837d3d315ba07e626fa3306755db Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Sun, 16 Apr 2023 16:16:03 +0200 Subject: [PATCH 534/566] Add code coverage using llvm-cov and codecov.io Signed-off-by: Thibaut Vandervelden --- .github/workflows/coverage.yml | 22 ++++++++++++++++++++++ ci.sh | 11 +++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000..79056061a --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,22 @@ +on: + push: + branches: [staging, trying] + pull_request: + +name: Coverage + +jobs: + coverage: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v2 + - name: Install `cargo llvm-cov` + uses: taiki-e/install-action@cargo-llvm-cov + - name: Run Coverage + run: ./ci.sh coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + #token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + files: lcov.info + fail_ci_if_error: false diff --git a/ci.sh b/ci.sh index 3db921a26..6b60a2cf1 100755 --- a/ci.sh +++ b/ci.sh @@ -73,6 +73,13 @@ clippy() { cargo +$MSRV clippy --tests --examples -- -D warnings } +coverage() { + for features in ${FEATURES_TEST[@]}; do + cargo llvm-cov --no-report --no-default-features --features "$features" + done + cargo llvm-cov report --lcov --output-path lcov.info +} + if [[ $1 == "test" || $1 == "all" ]]; then if [[ -n $2 ]]; then if [[ $2 == "msrv" ]]; then @@ -104,3 +111,7 @@ fi if [[ $1 == "clippy" || $1 == "all" ]]; then clippy fi + +if [[ $1 == "coverage" || $1 == "all" ]]; then + coverage +fi From a45a39e2a6b8c518788f68a1914322758589e4bc Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 7 Apr 2023 15:43:57 +0200 Subject: [PATCH 535/566] Add RPL Hop-by-Hop option to IPv6Option Signed-off-by: Thibaut Vandervelden --- src/iface/interface/ipv6.rs | 3 ++ src/wire/ipv6option.rs | 81 ++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index c57e06918..7b9a723ca 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -250,6 +250,9 @@ impl InterfaceInner { let opt_repr = check!(opt_repr); match opt_repr { Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), + #[cfg(feature = "proto-rpl")] + Ipv6OptionRepr::Rpl(_) => {} + Ipv6OptionRepr::Unknown { type_, .. } => { match Ipv6OptionFailureType::from(type_) { Ipv6OptionFailureType::Skip => (), diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index 963b066bf..e05989555 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -1,13 +1,18 @@ use super::{Error, Result}; +#[cfg(feature = "proto-rpl")] +use super::{RplHopByHopPacket, RplHopByHopRepr}; + use core::fmt; enum_with_unknown! { /// IPv6 Extension Header Option Type pub enum Type(u8) { /// 1 byte of padding - Pad1 = 0, + Pad1 = 0, /// Multiple bytes of padding - PadN = 1 + PadN = 1, + /// RPL Option + Rpl = 0x63, } } @@ -16,6 +21,7 @@ impl fmt::Display for Type { match *self { Type::Pad1 => write!(f, "Pad1"), Type::PadN => write!(f, "PadN"), + Type::Rpl => write!(f, "RPL"), Type::Unknown(id) => write!(f, "{id}"), } } @@ -220,6 +226,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> { pub enum Repr<'a> { Pad1, PadN(u8), + #[cfg(feature = "proto-rpl")] + Rpl(RplHopByHopRepr), Unknown { type_: Type, length: u8, @@ -236,6 +244,18 @@ impl<'a> Repr<'a> { match opt.option_type() { Type::Pad1 => Ok(Repr::Pad1), Type::PadN => Ok(Repr::PadN(opt.data_len())), + + #[cfg(feature = "proto-rpl")] + Type::Rpl => Ok(Repr::Rpl(RplHopByHopRepr::parse( + &RplHopByHopPacket::new_checked(opt.data())?, + ))), + #[cfg(not(feature = "proto-rpl"))] + Type::Rpl => Ok(Repr::Unknown { + type_: Type::Rpl, + length: opt.data_len(), + data: opt.data(), + }), + unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown { type_: unknown_type, length: opt.data_len(), @@ -249,6 +269,8 @@ impl<'a> Repr<'a> { match *self { Repr::Pad1 => 1, Repr::PadN(length) => field::DATA(length).end, + #[cfg(feature = "proto-rpl")] + Repr::Rpl(opt) => field::DATA(opt.buffer_len() as u8).end, Repr::Unknown { length, .. } => field::DATA(length).end, } } @@ -265,6 +287,14 @@ impl<'a> Repr<'a> { *x = 0 } } + #[cfg(feature = "proto-rpl")] + Repr::Rpl(rpl) => { + opt.set_option_type(Type::Rpl); + opt.set_data_len(4); + rpl.emit(&mut crate::wire::RplHopByHopPacket::new_unchecked( + opt.data_mut(), + )); + } Repr::Unknown { type_, length, @@ -344,6 +374,8 @@ impl<'a> fmt::Display for Repr<'a> { match *self { Repr::Pad1 => write!(f, "{} ", Type::Pad1), Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len), + #[cfg(feature = "proto-rpl")] + Repr::Rpl(rpl) => write!(f, "{} {rpl}", Type::Rpl), Repr::Unknown { type_, length, .. } => write!(f, "{type_} length={length} "), } } @@ -356,6 +388,8 @@ mod test { static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0]; static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0]; static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0]; + #[cfg(feature = "proto-rpl")] + static IPV6OPTION_BYTES_RPL: [u8; 6] = [0x63, 0x04, 0x00, 0x1e, 0x08, 0x00]; #[test] fn test_check_len() { @@ -396,6 +430,14 @@ mod test { Ok(()), Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len() ); + + #[cfg(feature = "proto-rpl")] + { + assert_eq!( + Ok(()), + Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL).check_len() + ); + } } #[test] @@ -437,6 +479,14 @@ mod test { // unrecognized option without length and data assert_eq!(Ipv6Option::new_checked(&bytes), Err(Error)); + + #[cfg(feature = "proto-rpl")] + { + let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL); + assert_eq!(opt.option_type(), Type::Rpl); + assert_eq!(opt.data_len(), 4); + assert_eq!(opt.data(), &[0x00, 0x1e, 0x08, 0x00]); + } } #[test] @@ -465,6 +515,23 @@ mod test { data: &data } ); + + #[cfg(feature = "proto-rpl")] + { + let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL); + let rpl = Repr::parse(&opt).unwrap(); + + assert_eq!( + rpl, + Repr::Rpl(crate::wire::RplHopByHopRepr { + down: false, + rank_error: false, + forwarding_error: false, + instance_id: crate::wire::RplInstanceId::from(0x1e), + sender_rank: 0x0800, + }) + ); + } } #[test] @@ -491,6 +558,16 @@ mod test { let mut opt = Ipv6Option::new_unchecked(&mut bytes); repr.emit(&mut opt); assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_UNKNOWN); + + #[cfg(feature = "proto-rpl")] + { + let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_RPL); + let rpl = Repr::parse(&opt).unwrap(); + let mut bytes = [0u8; 6]; + rpl.emit(&mut Ipv6Option::new_unchecked(&mut bytes)); + + assert_eq!(&bytes, &IPV6OPTION_BYTES_RPL); + } } #[test] From be7bd72d5bb961929a7f2c46afbe79650183caa6 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 14 Apr 2023 11:01:45 +0200 Subject: [PATCH 536/566] Implement the sequence counter from RFC6550 7.2 Signed-off-by: Thibaut Vandervelden --- src/iface/mod.rs | 2 + src/iface/rpl/consts.rs | 1 + src/iface/rpl/lollipop.rs | 189 ++++++++++++++++++++++++++++++++++++++ src/iface/rpl/mod.rs | 4 + 4 files changed, 196 insertions(+) create mode 100644 src/iface/rpl/consts.rs create mode 100644 src/iface/rpl/lollipop.rs create mode 100644 src/iface/rpl/mod.rs diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 02c982f62..710587ae0 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -10,6 +10,8 @@ mod interface; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] mod neighbor; mod route; +#[cfg(feature = "proto-rpl")] +mod rpl; mod socket_meta; mod socket_set; diff --git a/src/iface/rpl/consts.rs b/src/iface/rpl/consts.rs new file mode 100644 index 000000000..ec14e452e --- /dev/null +++ b/src/iface/rpl/consts.rs @@ -0,0 +1 @@ +pub const SEQUENCE_WINDOW: u8 = 16; diff --git a/src/iface/rpl/lollipop.rs b/src/iface/rpl/lollipop.rs new file mode 100644 index 000000000..4785c7725 --- /dev/null +++ b/src/iface/rpl/lollipop.rs @@ -0,0 +1,189 @@ +//! Implementation of sequence counters defined in [RFC 6550 § 7.2]. Values from 128 and greater +//! are used as a linear sequence to indicate a restart and bootstrap the counter. Values less than +//! or equal to 127 are used as a circular sequence number space of size 128. When operating in the +//! circular region, if sequence numbers are detected to be too far apart, then they are not +//! comparable. +//! +//! [RFC 6550 § 7.2]: https://datatracker.ietf.org/doc/html/rfc6550#section-7.2 + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SequenceCounter(u8); + +impl Default for SequenceCounter { + fn default() -> Self { + // RFC6550 7.2 recommends 240 (256 - SEQUENCE_WINDOW) as the initialization value of the + // counter. + Self(240) + } +} + +impl SequenceCounter { + /// Create a new sequence counter. + /// + /// Use `Self::default()` when a new sequence counter needs to be created with a value that is + /// recommended in RFC6550 7.2, being 240. + pub fn new(value: u8) -> Self { + Self(value) + } + + /// Return the value of the sequence counter. + pub fn value(&self) -> u8 { + self.0 + } + + /// Increment the sequence counter. + /// + /// When the sequence counter is greater than or equal to 128, the maximum value is 255. + /// When the sequence counter is less than 128, the maximum value is 127. + /// + /// When an increment of the sequence counter would cause the counter to increment beyond its + /// maximum value, the counter MUST wrap back to zero. + pub fn increment(&mut self) { + let max = if self.0 >= 128 { 255 } else { 127 }; + + self.0 = match self.0.checked_add(1) { + Some(val) if val <= max => val, + _ => 0, + }; + } +} + +impl PartialEq for SequenceCounter { + fn eq(&self, other: &Self) -> bool { + let a = self.value() as usize; + let b = other.value() as usize; + + if ((128..=255).contains(&a) && (0..=127).contains(&b)) + || ((128..=255).contains(&b) && (0..=127).contains(&a)) + { + false + } else { + let result = if a > b { a - b } else { b - a }; + + if result <= super::consts::SEQUENCE_WINDOW as usize { + // RFC1982 + a == b + } else { + // This case is actually not comparable. + false + } + } + } +} + +impl PartialOrd for SequenceCounter { + fn partial_cmp(&self, other: &Self) -> Option { + use super::consts::SEQUENCE_WINDOW; + use core::cmp::Ordering; + + let a = self.value() as usize; + let b = other.value() as usize; + + if (128..256).contains(&a) && (0..128).contains(&b) { + if 256 + b - a <= SEQUENCE_WINDOW as usize { + Some(Ordering::Less) + } else { + Some(Ordering::Greater) + } + } else if (128..256).contains(&b) && (0..128).contains(&a) { + if 256 + a - b <= SEQUENCE_WINDOW as usize { + Some(Ordering::Greater) + } else { + Some(Ordering::Less) + } + } else if ((0..128).contains(&a) && (0..128).contains(&b)) + || ((128..256).contains(&a) && (128..256).contains(&b)) + { + let result = if a > b { a - b } else { b - a }; + + if result <= SEQUENCE_WINDOW as usize { + // RFC1982 + a.partial_cmp(&b) + } else { + // This case is not comparable. + None + } + } else { + unreachable!(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sequence_counter_increment() { + let mut seq = SequenceCounter::new(253); + seq.increment(); + assert_eq!(seq.value(), 254); + seq.increment(); + assert_eq!(seq.value(), 255); + seq.increment(); + assert_eq!(seq.value(), 0); + + let mut seq = SequenceCounter::new(126); + seq.increment(); + assert_eq!(seq.value(), 127); + seq.increment(); + assert_eq!(seq.value(), 0); + } + + #[test] + fn sequence_counter_comparison() { + use core::cmp::Ordering; + + assert!(SequenceCounter::new(240) != SequenceCounter::new(1)); + assert!(SequenceCounter::new(1) != SequenceCounter::new(240)); + assert!(SequenceCounter::new(1) != SequenceCounter::new(240)); + assert!(SequenceCounter::new(240) == SequenceCounter::new(240)); + assert!(SequenceCounter::new(240 - 17) != SequenceCounter::new(240)); + + assert_eq!( + SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(5)), + Some(Ordering::Greater) + ); + assert_eq!( + SequenceCounter::new(250).partial_cmp(&SequenceCounter::new(5)), + Some(Ordering::Less) + ); + assert_eq!( + SequenceCounter::new(5).partial_cmp(&SequenceCounter::new(250)), + Some(Ordering::Greater) + ); + assert_eq!( + SequenceCounter::new(127).partial_cmp(&SequenceCounter::new(129)), + Some(Ordering::Less) + ); + assert_eq!( + SequenceCounter::new(120).partial_cmp(&SequenceCounter::new(121)), + Some(Ordering::Less) + ); + assert_eq!( + SequenceCounter::new(121).partial_cmp(&SequenceCounter::new(120)), + Some(Ordering::Greater) + ); + assert_eq!( + SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(241)), + Some(Ordering::Less) + ); + assert_eq!( + SequenceCounter::new(241).partial_cmp(&SequenceCounter::new(240)), + Some(Ordering::Greater) + ); + assert_eq!( + SequenceCounter::new(120).partial_cmp(&SequenceCounter::new(120)), + Some(Ordering::Equal) + ); + assert_eq!( + SequenceCounter::new(240).partial_cmp(&SequenceCounter::new(240)), + Some(Ordering::Equal) + ); + assert_eq!( + SequenceCounter::new(130).partial_cmp(&SequenceCounter::new(241)), + None + ); + } +} diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs new file mode 100644 index 000000000..41c47550d --- /dev/null +++ b/src/iface/rpl/mod.rs @@ -0,0 +1,4 @@ +#![allow(unused)] + +mod consts; +mod lollipop; From 8681d66fbe7cc3665c9f34041937a1aa7d231cdd Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 14 Apr 2023 12:34:35 +0200 Subject: [PATCH 537/566] Implement the Trickle algorithm defined in RFC6206 Signed-off-by: Thibaut Vandervelden --- src/iface/rpl/consts.rs | 5 + src/iface/rpl/mod.rs | 1 + src/iface/rpl/trickle.rs | 266 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 src/iface/rpl/trickle.rs diff --git a/src/iface/rpl/consts.rs b/src/iface/rpl/consts.rs index ec14e452e..70cc2acb8 100644 --- a/src/iface/rpl/consts.rs +++ b/src/iface/rpl/consts.rs @@ -1 +1,6 @@ pub const SEQUENCE_WINDOW: u8 = 16; + +pub const DEFAULT_DIO_INTERVAL_MIN: u32 = 12; +pub const DEFAULT_DIO_REDUNDANCY_CONSTANT: usize = 10; +/// This is 20 in the standard, but in Contiki they use: +pub const DEFAULT_DIO_INTERVAL_DOUBLINGS: u32 = 8; diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs index 41c47550d..a9f02a30b 100644 --- a/src/iface/rpl/mod.rs +++ b/src/iface/rpl/mod.rs @@ -2,3 +2,4 @@ mod consts; mod lollipop; +mod trickle; diff --git a/src/iface/rpl/trickle.rs b/src/iface/rpl/trickle.rs new file mode 100644 index 000000000..e60cad109 --- /dev/null +++ b/src/iface/rpl/trickle.rs @@ -0,0 +1,266 @@ +//! Implementation of the Trickle timer defined in [RFC 6206]. The algorithm allows node in a lossy +//! shared medium to exchange information in a highly robust, energy efficient, simple, and +//! scalable manner. Dynamicaly adjusting transmission windows allows Trickle to spread new +//! information fast while sending only a few messages per hour when information does not change. +//! +//! **NOTE**: the constants used for the default Trickle timer are the ones from the [Enhanced +//! Trickle]. +//! +//! [RFC 6206]: https://datatracker.ietf.org/doc/html/rfc6206 +//! [Enhanced Trickle]: https://d1wqtxts1xzle7.cloudfront.net/71402623/E-Trickle_Enhanced_Trickle_Algorithm_for20211005-2078-1ckh34a.pdf?1633439582=&response-content-disposition=inline%3B+filename%3DE_Trickle_Enhanced_Trickle_Algorithm_for.pdf&Expires=1681472005&Signature=cC7l-Pyr5r64XBNCDeSJ2ha6oqWUtO6A-KlDOyC0UVaHxDV3h3FuVHRtcNp3O9BUfRK8jeuWCYGBkCZgQT4Zgb6XwgVB-3z4TF9o3qBRMteRyYO5vjVkpPBeN7mz4Tl746SsSCHDm2NMtr7UVtLYamriU3D0rryoqLqJXmnkNoJpn~~wJe2H5PmPgIwixTwSvDkfFLSVoESaYS9ZWHZwbW-7G7OxIw8oSYhx9xMBnzkpdmT7sJNmvDzTUhoOjYrHTRM23cLVS9~oOSpT7hKtKD4h5CSmrNK4st07KnT9~tUqEcvGO3aXdd4quRZeKUcCkCbTLvhOEYg9~QqgD8xwhA__&Key-Pair-Id=APKAJLOHF5GGSLRBV4ZA + +use crate::{ + rand::Rand, + time::{Duration, Instant}, +}; + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) struct TrickleTimer { + i_min: u32, + i_max: u32, + k: usize, + + i: Duration, + t: Duration, + t_exp: Instant, + i_exp: Instant, + counter: usize, +} + +impl TrickleTimer { + /// Creat a new Trickle timer using the default values. + /// + /// **NOTE**: the standard defines I as a random value between [Imin, Imax]. However, this + /// could result in a t value that is very close to Imax. Therefore, sending DIO messages will + /// be sporadic, which is not ideal when a network is started. It might take a long time before + /// the network is actually stable. Therefore, we don't draw a random numberm but just use Imin + /// for I. This only affects the start of the RPL tree and speeds up building it. Also, we + /// don't use the default values from the standard, but the values from the _Enhanced Trickle + /// Algorithm for Low-Power and Lossy Networks_ from Baraq Ghaleb et al. This is also what the + /// Contiki Trickle timer does. + pub(crate) fn default(now: Instant, rand: &mut Rand) -> Self { + use super::consts::{ + DEFAULT_DIO_INTERVAL_DOUBLINGS, DEFAULT_DIO_INTERVAL_MIN, + DEFAULT_DIO_REDUNDANCY_CONSTANT, + }; + + Self::new( + DEFAULT_DIO_INTERVAL_MIN, + DEFAULT_DIO_INTERVAL_MIN + DEFAULT_DIO_INTERVAL_DOUBLINGS, + DEFAULT_DIO_REDUNDANCY_CONSTANT, + now, + rand, + ) + } + + /// Create a new Trickle timer. + pub(crate) fn new(i_min: u32, i_max: u32, k: usize, now: Instant, rand: &mut Rand) -> Self { + let mut timer = Self { + i_min, + i_max, + k, + i: Duration::ZERO, + t: Duration::ZERO, + t_exp: Instant::ZERO, + i_exp: Instant::ZERO, + counter: 0, + }; + + timer.i = Duration::from_millis(2u32.pow(timer.i_min) as u64); + timer.i_exp = now + timer.i; + timer.counter = 0; + + timer.set_t(now, rand); + + timer + } + + /// Poll the Trickle timer. Returns `true` when the Trickle timer singals that a message can be + /// transmitted. This happens when the Trickle timer expires. + pub(crate) fn poll(&mut self, now: Instant, rand: &mut Rand) -> bool { + let can_transmit = self.can_transmit() && self.t_expired(now); + + if can_transmit { + self.set_t(now, rand); + } + + if self.i_expired(now) { + self.expire(now, rand); + } + + can_transmit + } + + /// Returns the Instant at which the Trickle timer should be polled again. Polling the Trickle + /// timer before this Instant is not harmfull, however, polling after it is not correct. + pub(crate) fn poll_at(&self) -> Instant { + self.t_exp.min(self.i_exp) + } + + /// Signal the Trickle timer that a consistency has been heard, and thus increasing it's + /// counter. + pub(crate) fn hear_consistent(&mut self) { + self.counter += 1; + } + + /// Signal the Trickle timer that an inconsistency has been heard. This resets the Trickle + /// timer when the current interval is not the smallest possible. + pub(crate) fn hear_inconsistency(&mut self, now: Instant, rand: &mut Rand) { + let i = Duration::from_millis(2u32.pow(self.i_min) as u64); + if self.i > i { + self.reset(i, now, rand); + } + } + + /// Check if the Trickle timer can transmit or not. Returns `false` when the consistency + /// counter is bigger or equal to the default consistency constant. + pub(crate) fn can_transmit(&self) -> bool { + self.k != 0 && self.counter < self.k + } + + /// Reset the Trickle timer when the interval has expired. + fn expire(&mut self, now: Instant, rand: &mut Rand) { + let max_interval = Duration::from_millis(2u32.pow(self.i_max) as u64); + let i = if self.i >= max_interval { + max_interval + } else { + self.i + self.i + }; + + self.reset(i, now, rand); + } + + pub(crate) fn reset(&mut self, i: Duration, now: Instant, rand: &mut Rand) { + self.i = i; + self.i_exp = now + self.i; + self.counter = 0; + self.set_t(now, rand); + } + + pub(crate) const fn max_expiration(&self) -> Duration { + Duration::from_millis(2u32.pow(self.i_max) as u64) + } + + pub(crate) const fn min_expiration(&self) -> Duration { + Duration::from_millis(2u32.pow(self.i_min) as u64) + } + + fn set_t(&mut self, now: Instant, rand: &mut Rand) { + let t = Duration::from_micros( + self.i.total_micros() / 2 + + (rand.rand_u32() as u64 + % (self.i.total_micros() - self.i.total_micros() / 2 + 1)), + ); + + self.t = t; + self.t_exp = now + t; + } + + fn t_expired(&self, now: Instant) -> bool { + now >= self.t_exp + } + + fn i_expired(&self, now: Instant) -> bool { + now >= self.i_exp + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trickle_timer_intervals() { + let mut rand = Rand::new(1234); + let mut now = Instant::ZERO; + let mut trickle = TrickleTimer::default(now, &mut rand); + + let mut previous_i = trickle.i; + + while now <= Instant::from_secs(100_000) { + trickle.poll(now, &mut rand); + + if now < Instant::ZERO + trickle.max_expiration() { + // t should always be inbetween I/2 and I. + assert!(trickle.i / 2 < trickle.t); + assert!(trickle.i > trickle.t); + } + + if previous_i != trickle.i { + // When a new Interval is selected, this should be double the previous one. + assert_eq!(previous_i * 2, trickle.i); + assert_eq!(trickle.counter, 0); + previous_i = trickle.i; + } + + now += Duration::from_millis(100); + } + } + + #[test] + fn trickle_timer_hear_inconsistency() { + let mut rand = Rand::new(1234); + let mut now = Instant::ZERO; + let mut trickle = TrickleTimer::default(now, &mut rand); + + trickle.counter = 1; + + while now <= Instant::from_secs(10_000) { + trickle.poll(now, &mut rand); + + if now < trickle.i_exp && now < Instant::ZERO + trickle.min_expiration() { + assert_eq!(trickle.counter, 1); + } else { + // The first interval expired, so the conter is reset. + assert_eq!(trickle.counter, 0); + } + + if now == Instant::from_secs(10) { + // We set the counter to 1 such that we can test the `hear_inconsistency`. + trickle.counter = 1; + + assert_eq!(trickle.counter, 1); + + trickle.hear_inconsistency(now, &mut rand); + + assert_eq!(trickle.counter, 0); + assert_eq!(trickle.i, trickle.min_expiration()); + } + + now += Duration::from_millis(100); + } + } + + #[test] + fn trickle_timer_hear_consistency() { + let mut rand = Rand::new(1234); + let mut now = Instant::ZERO; + let mut trickle = TrickleTimer::default(now, &mut rand); + + trickle.counter = 1; + + let mut transmit_counter = 0; + + while now <= Instant::from_secs(10_000) { + trickle.hear_consistent(); + + if trickle.poll(now, &mut rand) { + transmit_counter += 1; + } + + if now == Instant::from_secs(10_000) { + use super::super::consts::{ + DEFAULT_DIO_INTERVAL_DOUBLINGS, DEFAULT_DIO_REDUNDANCY_CONSTANT, + }; + assert!(!trickle.poll(now, &mut rand)); + assert!(trickle.counter > DEFAULT_DIO_REDUNDANCY_CONSTANT); + // We should never have transmitted since the counter was higher than the default + // redundancy constant. + assert_eq!(transmit_counter, 0); + } + + now += Duration::from_millis(100); + } + } +} From 5f34813f09432c7a596c56133d418b27de376f64 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 17 Apr 2023 15:24:03 +0200 Subject: [PATCH 538/566] Add codecov.io badge on the README Signed-off-by: Thibaut Vandervelden --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b3d1f4282..65f7a29eb 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![crates.io](https://img.shields.io/crates/v/smoltcp.svg)](https://crates.io/crates/smoltcp) [![crates.io](https://img.shields.io/crates/d/smoltcp.svg)](https://crates.io/crates/smoltcp) [![crates.io](https://img.shields.io/matrix/smoltcp:matrix.org)](https://matrix.to/#/#smoltcp:matrix.org) +[![codecov](https://codecov.io/github/smoltcp-rs/smoltcp/branch/master/graph/badge.svg?token=3KbAR9xH1t)](https://codecov.io/github/smoltcp-rs/smoltcp) _smoltcp_ is a standalone, event-driven TCP/IP stack that is designed for bare-metal, real-time systems. Its design goals are simplicity and robustness. Its design anti-goals From d1d1d2368f90871f8c6cf2508bf5cc9af4f4d5d6 Mon Sep 17 00:00:00 2001 From: "B. Blechschmidt" Date: Mon, 17 Apr 2023 22:17:43 +0200 Subject: [PATCH 539/566] Add file descriptor support for tuntap devices --- src/phy/sys/tuntap_interface.rs | 39 +++++++++++++++++++++------------ src/phy/tuntap_interface.rs | 16 ++++++++++++-- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/phy/sys/tuntap_interface.rs b/src/phy/sys/tuntap_interface.rs index 494313e2d..3019cadea 100644 --- a/src/phy/sys/tuntap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -6,8 +6,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; #[derive(Debug)] pub struct TunTapInterfaceDesc { lower: libc::c_int, - ifreq: ifreq, - medium: Medium, + mtu: usize, } impl AsRawFd for TunTapInterfaceDesc { @@ -29,15 +28,23 @@ impl TunTapInterfaceDesc { lower }; - Ok(TunTapInterfaceDesc { - lower, - ifreq: ifreq_for(name), - medium, - }) + let mut ifreq = ifreq_for(name); + Self::attach_interface_ifreq(lower, medium, &mut ifreq)?; + let mtu = Self::mtu_ifreq(medium, &mut ifreq)?; + + Ok(TunTapInterfaceDesc { lower, mtu }) + } + + pub fn from_fd(fd: RawFd, mtu: usize) -> io::Result { + Ok(TunTapInterfaceDesc { lower: fd, mtu }) } - pub fn attach_interface(&mut self) -> io::Result<()> { - let mode = match self.medium { + fn attach_interface_ifreq( + lower: libc::c_int, + medium: Medium, + ifr: &mut ifreq, + ) -> io::Result<()> { + let mode = match medium { #[cfg(feature = "medium-ip")] Medium::Ip => imp::IFF_TUN, #[cfg(feature = "medium-ethernet")] @@ -45,11 +52,11 @@ impl TunTapInterfaceDesc { #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => todo!(), }; - self.ifreq.ifr_data = mode | imp::IFF_NO_PI; - ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ()) + ifr.ifr_data = mode | imp::IFF_NO_PI; + ifreq_ioctl(lower, ifr, imp::TUNSETIFF).map(|_| ()) } - pub fn interface_mtu(&mut self) -> io::Result { + fn mtu_ifreq(medium: Medium, ifr: &mut ifreq) -> io::Result { let lower = unsafe { let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); if lower == -1 { @@ -58,7 +65,7 @@ impl TunTapInterfaceDesc { lower }; - let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize); + let ip_mtu = ifreq_ioctl(lower, ifr, imp::SIOCGIFMTU).map(|mtu| mtu as usize); unsafe { libc::close(lower); @@ -69,7 +76,7 @@ impl TunTapInterfaceDesc { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. - let mtu = match self.medium { + let mtu = match medium { #[cfg(feature = "medium-ip")] Medium::Ip => ip_mtu, #[cfg(feature = "medium-ethernet")] @@ -81,6 +88,10 @@ impl TunTapInterfaceDesc { Ok(mtu) } + pub fn interface_mtu(&self) -> io::Result { + Ok(self.mtu) + } + pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result { unsafe { let len = libc::read( diff --git a/src/phy/tuntap_interface.rs b/src/phy/tuntap_interface.rs index c81d8ac3e..32a28dbb4 100644 --- a/src/phy/tuntap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -28,8 +28,7 @@ impl TunTapInterface { /// no special privileges are needed. Otherwise, this requires superuser privileges /// or a corresponding capability set on the executable. pub fn new(name: &str, medium: Medium) -> io::Result { - let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?; - lower.attach_interface()?; + let lower = sys::TunTapInterfaceDesc::new(name, medium)?; let mtu = lower.interface_mtu()?; Ok(TunTapInterface { lower: Rc::new(RefCell::new(lower)), @@ -37,6 +36,19 @@ impl TunTapInterface { medium, }) } + + /// Attaches to a TUN/TAP interface specified by file descriptor `fd`. + /// + /// On platforms like Android, a file descriptor to a tun interface is exposed. + /// On these platforms, a TunTapInterface cannot be instantiated with a name. + pub fn from_fd(fd: RawFd, medium: Medium, mtu: usize) -> io::Result { + let lower = sys::TunTapInterfaceDesc::from_fd(fd, mtu)?; + Ok(TunTapInterface { + lower: Rc::new(RefCell::new(lower)), + mtu, + medium, + }) + } } impl Device for TunTapInterface { From 35b6fecdff1afa23cd08bab419db0f862eb59707 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 22:34:12 +0200 Subject: [PATCH 540/566] disable codecov diff annotations --- .github/codecov.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 000000000..4cda2ce1e --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,2 @@ +github_checks: + annotations: false \ No newline at end of file From 29abfa35e9982c1619690f4ff6c990a661a428d9 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 10 May 2023 14:35:32 +0200 Subject: [PATCH 541/566] fix: check length field of NDISC redirected head If the length field indicates a lenght bigger than the actual data that it is carying, then the packet is just wrong. Emitting such a packet is also not allowed. We now also parse the IPv6 header in an NDISC redirected packet and check that the length is correct. Signed-off-by: Thibaut Vandervelden --- fuzz/Cargo.toml | 2 +- fuzz/fuzz_targets/sixlowpan_packet.rs | 1 + src/wire/ndiscoption.rs | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index b3357eba8..f526d7551 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,7 +12,7 @@ cargo-fuzz = true libfuzzer-sys = "0.4" arbitrary = { version = "1", features = ["derive"] } getopts = "0.2" -smoltcp = { path = "..", features = [ "medium-ethernet" ] } +smoltcp = { path = ".." } # Prevent this from interfering with workspaces [workspace] diff --git a/fuzz/fuzz_targets/sixlowpan_packet.rs b/fuzz/fuzz_targets/sixlowpan_packet.rs index 37038e456..1cb287029 100644 --- a/fuzz/fuzz_targets/sixlowpan_packet.rs +++ b/fuzz/fuzz_targets/sixlowpan_packet.rs @@ -77,6 +77,7 @@ fuzz_target!(|fuzz: SixlowpanPacketFuzzer| { &frame, &iphc_repr.src_addr, &iphc_repr.dst_addr, + &Default::default(), ) { let mut buffer = vec![ 0; diff --git a/src/wire/ndiscoption.rs b/src/wire/ndiscoption.rs index 4e3f4ee67..eff7a93c1 100644 --- a/src/wire/ndiscoption.rs +++ b/src/wire/ndiscoption.rs @@ -466,13 +466,14 @@ impl<'a> Repr<'a> { if opt.data_len() < 6 { Err(Error) } else { - let ip_packet = - Ipv6Packet::new_unchecked(&opt.data()[field::REDIRECTED_RESERVED.len()..]); + let redirected_packet = &opt.data()[field::REDIRECTED_RESERVED.len()..]; + + let ip_packet = Ipv6Packet::new_checked(redirected_packet)?; let ip_repr = Ipv6Repr::parse(&ip_packet)?; + Ok(Repr::RedirectedHeader(RedirectedHeader { header: ip_repr, - data: &opt.data() - [field::REDIRECTED_RESERVED.len() + ip_repr.buffer_len()..], + data: &redirected_packet[ip_repr.buffer_len()..][..ip_repr.payload_len], })) } } From 6513c941de0b6b37c1d76d4b728444aae4ea81d6 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 18 Apr 2023 15:00:43 +0200 Subject: [PATCH 542/566] Add RPL Rank logic Signed-off-by: Thibaut Vandervelden --- src/iface/rpl/consts.rs | 2 + src/iface/rpl/mod.rs | 1 + src/iface/rpl/rank.rs | 102 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 src/iface/rpl/rank.rs diff --git a/src/iface/rpl/consts.rs b/src/iface/rpl/consts.rs index 70cc2acb8..70a66138f 100644 --- a/src/iface/rpl/consts.rs +++ b/src/iface/rpl/consts.rs @@ -1,5 +1,7 @@ pub const SEQUENCE_WINDOW: u8 = 16; +pub const DEFAULT_MIN_HOP_RANK_INCREASE: u16 = 256; + pub const DEFAULT_DIO_INTERVAL_MIN: u32 = 12; pub const DEFAULT_DIO_REDUNDANCY_CONSTANT: usize = 10; /// This is 20 in the standard, but in Contiki they use: diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs index a9f02a30b..2decec054 100644 --- a/src/iface/rpl/mod.rs +++ b/src/iface/rpl/mod.rs @@ -2,4 +2,5 @@ mod consts; mod lollipop; +mod rank; mod trickle; diff --git a/src/iface/rpl/rank.rs b/src/iface/rpl/rank.rs new file mode 100644 index 000000000..daac198e5 --- /dev/null +++ b/src/iface/rpl/rank.rs @@ -0,0 +1,102 @@ +//! Implementation of the Rank comparison in RPL. +//! +//! A Rank can be thought of as a fixed-point number, where the position of the radix point between +//! the integer part and the fractional part is determined by `MinHopRankIncrease`. +//! `MinHopRankIncrease` is the minimum increase in Rank between a node and any of its DODAG +//! parents. +//! This value is provisined by the DODAG root. +//! +//! When Rank is compared, the integer portion of the Rank is to be used. +//! +//! Meaning of the comparison: +//! - **Rank M is less than Rank N**: the position of M is closer to the DODAG root than the position +//! of N. Node M may safely be a DODAG parent for node N. +//! - **Ranks are equal**: the positions of both nodes within the DODAG and with respect to the DODAG +//! are similar or identical. Routing through a node with equal Rank may cause a routing loop. +//! - **Rank M is greater than Rank N**: the position of node M is farther from the DODAG root +//! than the position of N. Node M may in fact be in the sub-DODAG of node N. If node N selects +//! node M as a DODAG parent, there is a risk of creating a loop. + +use super::consts::DEFAULT_MIN_HOP_RANK_INCREASE; + +/// The Rank is the expression of the relative position within a DODAG Version with regard to +/// neighbors, and it is not necessarily a good indication or a proper expression of a distance or +/// a path cost to the root. +#[derive(Debug, Clone, Copy, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Rank { + value: u16, + min_hop_rank_increase: u16, +} + +impl core::fmt::Display for Rank { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Rank({})", self.dag_rank()) + } +} + +impl Rank { + pub const INFINITE: Self = Rank::new(0xffff, DEFAULT_MIN_HOP_RANK_INCREASE); + + /// The ROOT_RANK is the smallest rank possible. + /// DAG_RANK(ROOT_RANK) should be 1. See RFC6550 § 17. + pub const ROOT: Self = Rank::new(DEFAULT_MIN_HOP_RANK_INCREASE, DEFAULT_MIN_HOP_RANK_INCREASE); + + /// Create a new Rank from some value and a `MinHopRankIncrease`. + /// The `MinHopRankIncrease` is used for calculating the integer part for comparing to other + /// Ranks. + pub const fn new(value: u16, min_hop_rank_increase: u16) -> Self { + Self { + value, + min_hop_rank_increase, + } + } + + /// Return the integer part of the Rank. + pub fn dag_rank(&self) -> u16 { + self.value / self.min_hop_rank_increase + } + + /// Return the raw Rank value. + pub fn raw_value(&self) -> u16 { + self.value + } +} + +impl PartialEq for Rank { + fn eq(&self, other: &Self) -> bool { + self.dag_rank() == other.dag_rank() + } +} + +impl PartialOrd for Rank { + fn partial_cmp(&self, other: &Self) -> Option { + self.dag_rank().partial_cmp(&other.dag_rank()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn calculate_rank() { + let r = Rank::new(27, 16); + assert_eq!(r.dag_rank(), 1) + } + + #[test] + fn comparison() { + let r1 = Rank::ROOT; + let r2 = Rank::new(16, 16); + assert!(r1 == r2); + + let r1 = Rank::new(16, 16); + let r2 = Rank::new(32, 16); + assert!(r1 < r2); + + let r1 = Rank::ROOT; + let r2 = Rank::INFINITE; + assert!(r1 < r2); + } +} From 391e762991dc10aa1280d175dfc90fb058592f00 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 18 Apr 2023 15:05:35 +0200 Subject: [PATCH 543/566] Add `proto-rpl` to CI tests Signed-off-by: Thibaut Vandervelden --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index 6b60a2cf1..3c45f5d7c 100755 --- a/ci.sh +++ b/ci.sh @@ -27,6 +27,7 @@ FEATURES_TEST=( "std,medium-ip,proto-ipv6,socket-icmp,socket-tcp" "std,medium-ieee802154,proto-sixlowpan,socket-udp" "std,medium-ieee802154,proto-sixlowpan,proto-sixlowpan-fragmentation,socket-udp" + "std,medium-ieee802154,proto-rpl,proto-sixlowpan,proto-sixlowpan-fragmentation,socket-udp" "std,medium-ip,proto-ipv4,proto-ipv6,socket-tcp,socket-udp" "std,medium-ethernet,medium-ip,medium-ieee802154,proto-ipv4,proto-ipv6,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" ) From 838c13893cbf181755795645789e36842f864287 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 25 May 2023 13:11:10 +0200 Subject: [PATCH 544/566] fix: make codecov informational Hopefully this does not fail CI. Signed-off-by: Thibaut Vandervelden --- .github/codecov.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 4cda2ce1e..c4e6b3f3b 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,2 +1,11 @@ github_checks: - annotations: false \ No newline at end of file + annotations: false + +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true From 4ea83546df80c5d86536a2514fe5e0b13bdbb3a7 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 24 Apr 2023 14:38:57 +0200 Subject: [PATCH 545/566] Rewrite IPv6 extension headers --- src/iface/interface/ipv6.rs | 10 +- src/iface/interface/tests.rs | 4 +- .../{ipv6hopbyhop.rs => ipv6ext_header.rs} | 190 +++++---------- src/wire/ipv6fragment.rs | 69 ++---- src/wire/ipv6option.rs | 20 +- src/wire/ipv6routing.rs | 216 ++++++------------ src/wire/mod.rs | 15 +- 7 files changed, 179 insertions(+), 345 deletions(-) rename src/wire/{ipv6hopbyhop.rs => ipv6ext_header.rs} (54%) diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index 7b9a723ca..c5e9b3a38 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -244,9 +244,11 @@ impl InterfaceInner { handled_by_raw_socket: bool, ip_payload: &'frame [u8], ) -> Option> { - let hbh_pkt = check!(Ipv6HopByHopHeader::new_checked(ip_payload)); - let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_pkt)); - for opt_repr in hbh_repr.options() { + let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ip_payload)); + let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr)); + + let hbh_options = Ipv6OptionsIterator::new(hbh_repr.data); + for opt_repr in hbh_options { let opt_repr = check!(opt_repr); match opt_repr { Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), @@ -273,7 +275,7 @@ impl InterfaceInner { ipv6_repr, hbh_repr.next_header, handled_by_raw_socket, - &ip_payload[hbh_repr.buffer_len()..], + &ip_payload[hbh_repr.header_len() + hbh_repr.data.len()..], ) } diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 3345dae73..7459054fa 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1063,11 +1063,11 @@ fn test_icmpv6_nxthdr_unknown() { hbh_pkt.set_header_len(0); offset += 8; { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut *hbh_pkt.options_mut()); + let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.payload_mut()[..]); Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt); } { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.options_mut()[5..]); + let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.payload_mut()[5..]); Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); } } diff --git a/src/wire/ipv6hopbyhop.rs b/src/wire/ipv6ext_header.rs similarity index 54% rename from src/wire/ipv6hopbyhop.rs rename to src/wire/ipv6ext_header.rs index c604d7791..bc8ef879f 100644 --- a/src/wire/ipv6hopbyhop.rs +++ b/src/wire/ipv6ext_header.rs @@ -1,56 +1,38 @@ -use super::{Error, Result}; -use core::fmt; - -pub use super::IpProtocol as Protocol; -use crate::wire::ipv6option::Ipv6OptionsIterator; +#![allow(unused)] -/// A read/write wrapper around an IPv6 Hop-by-Hop Options Header. -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Header> { - buffer: T, -} +use super::IpProtocol; +use super::{Error, Result}; -// Format of the Hop-by-Hop Options Header -// -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Next Header | Hdr Ext Len | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + -// | | -// . . -// . Options . -// . . -// | | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// -// -// See https://tools.ietf.org/html/rfc8200#section-4.3 for details. mod field { #![allow(non_snake_case)] use crate::wire::field::*; - // Minimum size of the header. pub const MIN_HEADER_SIZE: usize = 8; - // 8-bit identifier of the header immediately following this header. pub const NXT_HDR: usize = 0; - // 8-bit unsigned integer. Length of the OPTIONS field in 8-octet units, - // not including the first 8 octets. pub const LENGTH: usize = 1; - // Variable-length field. Option-Type-specific data. + // Variable-length field. // - // Length of the header is in 8-octet units, not including the first 8 octets. The first two - // octets are the next header type and the header length. - pub const fn OPTIONS(length_field: u8) -> Field { + // Length of the header is in 8-octet units, not including the first 8 octets. + // The first two octets are the next header type and the header length. + pub const fn PAYLOAD(length_field: u8) -> Field { let bytes = length_field as usize * 8 + 8; 2..bytes } } +/// A read/write wrapper around an IPv6 Extension Header buffer. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Header> { + buffer: T, +} + +/// Core getter methods relevant to any IPv6 extension header. impl> Header { - /// Create a raw octet buffer with an IPv6 Hop-by-Hop Options Header structure. - pub const fn new_unchecked(buffer: T) -> Header { + /// Create a raw octet buffer with an IPv6 Extension Header structure. + pub const fn new_unchecked(buffer: T) -> Self { Header { buffer } } @@ -58,7 +40,7 @@ impl> Header { /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len - pub fn new_checked(buffer: T) -> Result> { + pub fn new_checked(buffer: T) -> Result { let header = Self::new_unchecked(buffer); header.check_len()?; Ok(header) @@ -72,14 +54,13 @@ impl> Header { /// [set_header_len]: #method.set_header_len pub fn check_len(&self) -> Result<()> { let data = self.buffer.as_ref(); - let len = data.len(); + let len = data.len(); if len < field::MIN_HEADER_SIZE { return Err(Error); } - let of = field::OPTIONS(data[field::LENGTH]); - + let of = field::PAYLOAD(data[field::LENGTH]); if len < of.end { return Err(Error); } @@ -93,40 +74,36 @@ impl> Header { } /// Return the next header field. - #[inline] - pub fn next_header(&self) -> Protocol { + pub fn next_header(&self) -> IpProtocol { let data = self.buffer.as_ref(); - Protocol::from(data[field::NXT_HDR]) + IpProtocol::from(data[field::NXT_HDR]) } - /// Return length of the Hop-by-Hop Options header in 8-octet units, not including the first - /// 8 octets. - #[inline] + /// Return the header length field. pub fn header_len(&self) -> u8 { let data = self.buffer.as_ref(); data[field::LENGTH] } } -impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> { - /// Return the option data. - #[inline] - pub fn options(&self) -> &'a [u8] { +impl<'h, T: AsRef<[u8]> + ?Sized> Header<&'h T> { + /// Return the payload of the IPv6 extension header. + pub fn payload(&self) -> &'h [u8] { let data = self.buffer.as_ref(); - &data[field::OPTIONS(data[field::LENGTH])] + &data[field::PAYLOAD(data[field::LENGTH])] } } impl + AsMut<[u8]>> Header { /// Set the next header field. #[inline] - pub fn set_next_header(&mut self, value: Protocol) { + pub fn set_next_header(&mut self, value: IpProtocol) { let data = self.buffer.as_mut(); data[field::NXT_HDR] = value.into(); } - /// Set the option data length. Length of the Hop-by-Hop Options header in 8-octet units, - /// not including the first 8 octets. + /// Set the extension header data length. The length of the header is + /// in 8-octet units, not including the first 8 octets. #[inline] pub fn set_header_len(&mut self, value: u8) { let data = self.buffer.as_mut(); @@ -135,78 +112,46 @@ impl + AsMut<[u8]>> Header { } impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> { - /// Return a mutable pointer to the option data. + /// Return a mutable pointer to the payload data. #[inline] - pub fn options_mut(&mut self) -> &mut [u8] { + pub fn payload_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); let len = data[field::LENGTH]; - &mut data[field::OPTIONS(len)] - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match Repr::parse(self) { - Ok(repr) => write!(f, "{repr}"), - Err(err) => { - write!(f, "IPv6 Hop-by-Hop Options ({err})")?; - Ok(()) - } - } + &mut data[field::PAYLOAD(len)] } } -/// A high-level representation of an IPv6 Hop-by-Hop Options header. #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { - /// The type of header immediately following the Hop-by-Hop Options header. - pub next_header: Protocol, - /// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets. + pub next_header: IpProtocol, pub length: u8, - /// The options contained in the Hop-by-Hop Options header. - pub options: &'a [u8], + pub data: &'a [u8], } impl<'a> Repr<'a> { - /// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation. - pub fn parse(header: &Header<&'a T>) -> Result> + /// Parse an IPv6 Extension Header Header and return a high-level representation. + pub fn parse(header: &Header<&'a T>) -> Result where T: AsRef<[u8]> + ?Sized, { - Ok(Repr { + Ok(Self { next_header: header.next_header(), length: header.header_len(), - options: header.options(), + data: header.payload(), }) } /// Return the length, in bytes, of a header that will be emitted from this high-level /// representation. - pub const fn buffer_len(&self) -> usize { - field::OPTIONS(self.length).end + pub const fn header_len(&self) -> usize { + 2 } - /// Emit a high-level representation into an IPv6 Hop-by-Hop Options Header. + /// Emit a high-level representation into an IPv6 Extension Header. pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { header.set_next_header(self.next_header); header.set_header_len(self.length); - header.options_mut().copy_from_slice(self.options); - } - - /// Return an `Iterator` for the contained options. - pub fn options(&self) -> Ipv6OptionsIterator { - Ipv6OptionsIterator::new(self.options, self.buffer_len() - 2) - } -} - -impl<'a> fmt::Display for Repr<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "IPv6 Hop-by-Hop Options next_hdr={} length={} ", - self.next_header, self.length - ) } } @@ -254,14 +199,14 @@ mod test { #[test] fn test_header_deconstruct() { let header = Header::new_unchecked(&REPR_PACKET_PAD4); - assert_eq!(header.next_header(), Protocol::Tcp); + assert_eq!(header.next_header(), IpProtocol::Tcp); assert_eq!(header.header_len(), 0); - assert_eq!(header.options(), &REPR_PACKET_PAD4[2..]); + assert_eq!(header.payload(), &REPR_PACKET_PAD4[2..]); let header = Header::new_unchecked(&REPR_PACKET_PAD12); - assert_eq!(header.next_header(), Protocol::Tcp); + assert_eq!(header.next_header(), IpProtocol::Tcp); assert_eq!(header.header_len(), 1); - assert_eq!(header.options(), &REPR_PACKET_PAD12[2..]); + assert_eq!(header.payload(), &REPR_PACKET_PAD12[2..]); } #[test] @@ -271,11 +216,11 @@ mod test { bytes.push(0); assert_eq!( - Header::new_unchecked(&bytes).options().len(), + Header::new_unchecked(&bytes).payload().len(), REPR_PACKET_PAD4[2..].len() ); assert_eq!( - Header::new_unchecked(&mut bytes).options_mut().len(), + Header::new_unchecked(&mut bytes).payload_mut().len(), REPR_PACKET_PAD4[2..].len() ); @@ -284,11 +229,11 @@ mod test { bytes.push(0); assert_eq!( - Header::new_unchecked(&bytes).options().len(), + Header::new_unchecked(&bytes).payload().len(), REPR_PACKET_PAD12[2..].len() ); assert_eq!( - Header::new_unchecked(&mut bytes).options_mut().len(), + Header::new_unchecked(&mut bytes).payload_mut().len(), REPR_PACKET_PAD12[2..].len() ); } @@ -317,9 +262,9 @@ mod test { assert_eq!( repr, Repr { - next_header: Protocol::Tcp, + next_header: IpProtocol::Tcp, length: 0, - options: &REPR_PACKET_PAD4[2..] + data: &REPR_PACKET_PAD4[2..] } ); @@ -328,9 +273,9 @@ mod test { assert_eq!( repr, Repr { - next_header: Protocol::Tcp, + next_header: IpProtocol::Tcp, length: 1, - options: &REPR_PACKET_PAD12[2..] + data: &REPR_PACKET_PAD12[2..] } ); } @@ -338,34 +283,23 @@ mod test { #[test] fn test_repr_emit() { let repr = Repr { - next_header: Protocol::Tcp, + next_header: IpProtocol::Tcp, length: 0, - options: &REPR_PACKET_PAD4[2..], + data: &REPR_PACKET_PAD4[2..], }; - let mut bytes = [0u8; 8]; + let mut bytes = [0u8; 2]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); - assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]); + assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..2]); let repr = Repr { - next_header: Protocol::Tcp, + next_header: IpProtocol::Tcp, length: 1, - options: &REPR_PACKET_PAD12[2..], + data: &REPR_PACKET_PAD12[2..], }; - let mut bytes = [0u8; 16]; + let mut bytes = [0u8; 2]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); - assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..]); - } - - #[test] - fn test_buffer_len() { - let header = Header::new_unchecked(&REPR_PACKET_PAD4); - let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr.buffer_len(), REPR_PACKET_PAD4.len()); - - let header = Header::new_unchecked(&REPR_PACKET_PAD12); - let repr = Repr::parse(&header).unwrap(); - assert_eq!(repr.buffer_len(), REPR_PACKET_PAD12.len()); + assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..2]); } } diff --git a/src/wire/ipv6fragment.rs b/src/wire/ipv6fragment.rs index d8bfe0337..46246deb1 100644 --- a/src/wire/ipv6fragment.rs +++ b/src/wire/ipv6fragment.rs @@ -21,17 +21,17 @@ pub struct Header> { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // See https://tools.ietf.org/html/rfc8200#section-4.5 for details. +// +// **NOTE**: The fields start counting after the header length field. mod field { use crate::wire::field::*; - // 8-bit identifier of the header immediately following this header. - pub const NXT_HDR: usize = 0; - // 8-bit reserved field. - pub const RESERVED: usize = 1; // 16-bit field containing the fragment offset, reserved and more fragments values. - pub const FR_OF_M: Field = 2..4; + pub const FR_OF_M: Field = 0..2; // 32-bit field identifying the fragmented packet - pub const IDENT: Field = 4..8; + pub const IDENT: Field = 2..6; + /// 1 bit flag indicating if there are more fragments coming. + pub const M: usize = 1; } impl> Header { @@ -68,13 +68,6 @@ impl> Header { self.buffer } - /// Return the next header field. - #[inline] - pub fn next_header(&self) -> Protocol { - let data = self.buffer.as_ref(); - Protocol::from(data[field::NXT_HDR]) - } - /// Return the fragment offset field. #[inline] pub fn frag_offset(&self) -> u16 { @@ -86,7 +79,7 @@ impl> Header { #[inline] pub fn more_frags(&self) -> bool { let data = self.buffer.as_ref(); - (data[3] & 0x1) == 1 + (data[field::M] & 0x1) == 1 } /// Return the fragment identification value field. @@ -98,13 +91,6 @@ impl> Header { } impl + AsMut<[u8]>> Header { - /// Set the next header field. - #[inline] - pub fn set_next_header(&mut self, value: Protocol) { - let data = self.buffer.as_mut(); - data[field::NXT_HDR] = value.into(); - } - /// Set reserved fields. /// /// Set 8-bit reserved field after the next header field. @@ -112,11 +98,8 @@ impl + AsMut<[u8]>> Header { #[inline] pub fn clear_reserved(&mut self) { let data = self.buffer.as_mut(); - - data[field::RESERVED] = 0; - // Retain the higher order 5 bits and lower order 1 bit - data[3] &= 0xf9; + data[field::M] &= 0xf9; } /// Set the fragment offset field. @@ -124,7 +107,7 @@ impl + AsMut<[u8]>> Header { pub fn set_frag_offset(&mut self, value: u16) { let data = self.buffer.as_mut(); // Retain the lower order 3 bits - let raw = ((value & 0x1fff) << 3) | ((data[3] & 0x7) as u16); + let raw = ((value & 0x1fff) << 3) | ((data[field::M] & 0x7) as u16); NetworkEndian::write_u16(&mut data[field::FR_OF_M], raw); } @@ -133,8 +116,8 @@ impl + AsMut<[u8]>> Header { pub fn set_more_frags(&mut self, value: bool) { let data = self.buffer.as_mut(); // Retain the high order 7 bits - let raw = (data[3] & 0xfe) | (value as u8 & 0x1); - data[3] = raw; + let raw = (data[field::M] & 0xfe) | (value as u8 & 0x1); + data[field::M] = raw; } /// Set the fragmentation identification field. @@ -161,8 +144,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr { - /// The type of header immediately following the Fragment header. - pub next_header: Protocol, /// The offset of the data following this header, relative to the start of the Fragmentable /// Part of the original packet. pub frag_offset: u16, @@ -179,7 +160,6 @@ impl Repr { T: AsRef<[u8]> + ?Sized, { Ok(Repr { - next_header: header.next_header(), frag_offset: header.frag_offset(), more_frags: header.more_frags(), ident: header.ident(), @@ -194,7 +174,6 @@ impl Repr { /// Emit a high-level representation into an IPv6 Fragment Header. pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { - header.set_next_header(self.next_header); header.clear_reserved(); header.set_frag_offset(self.frag_offset); header.set_more_frags(self.more_frags); @@ -206,8 +185,8 @@ impl fmt::Display for Repr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "IPv6 Fragment next_hdr={} offset={} more={} ident={}", - self.next_header, self.frag_offset, self.more_frags, self.ident + "IPv6 Fragment offset={} more={} ident={}", + self.frag_offset, self.more_frags, self.ident ) } } @@ -217,17 +196,17 @@ mod test { use super::*; // A Fragment Header with more fragments remaining - static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39]; + static BYTES_HEADER_MORE_FRAG: [u8; 6] = [0x0, 0x1, 0x0, 0x0, 0x30, 0x39]; // A Fragment Header with no more fragments remaining - static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32]; + static BYTES_HEADER_LAST_FRAG: [u8; 6] = [0xa, 0x0, 0x0, 0x1, 0x9, 0x32]; #[test] fn test_check_len() { - // less than 8 bytes + // less than 6 bytes assert_eq!( Err(Error), - Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len() + Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..5]).check_len() ); // valid assert_eq!( @@ -239,13 +218,11 @@ mod test { #[test] fn test_header_deconstruct() { let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG); - assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.frag_offset(), 0); assert!(header.more_frags()); assert_eq!(header.ident(), 12345); let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG); - assert_eq!(header.next_header(), Protocol::Tcp); assert_eq!(header.frag_offset(), 320); assert!(!header.more_frags()); assert_eq!(header.ident(), 67890); @@ -258,7 +235,6 @@ mod test { assert_eq!( repr, Repr { - next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 @@ -270,7 +246,6 @@ mod test { assert_eq!( repr, Repr { - next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 @@ -281,26 +256,24 @@ mod test { #[test] fn test_repr_emit() { let repr = Repr { - next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345, }; - let mut bytes = [0u8; 8]; + let mut bytes = [0u8; 6]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); - assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]); + assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..6]); let repr = Repr { - next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890, }; - let mut bytes = [0u8; 8]; + let mut bytes = [0u8; 6]; let mut header = Header::new_unchecked(&mut bytes); repr.emit(&mut header); - assert_eq!(header.into_inner(), &BYTES_HEADER_LAST_FRAG[0..8]); + assert_eq!(header.into_inner(), &BYTES_HEADER_LAST_FRAG[0..6]); } #[test] diff --git a/src/wire/ipv6option.rs b/src/wire/ipv6option.rs index e05989555..dfbd6acad 100644 --- a/src/wire/ipv6option.rs +++ b/src/wire/ipv6option.rs @@ -322,12 +322,8 @@ impl<'a> Ipv6OptionsIterator<'a> { /// Create a new `Ipv6OptionsIterator`, used to iterate over the /// options contained in a IPv6 Extension Header (e.g. the Hop-by-Hop /// header). - /// - /// # Panics - /// This function panics if the `length` provided is larger than the - /// length of the `data` buffer. - pub fn new(data: &'a [u8], length: usize) -> Ipv6OptionsIterator<'a> { - assert!(length <= data.len()); + pub fn new(data: &'a [u8]) -> Ipv6OptionsIterator<'a> { + let length = data.len(); Ipv6OptionsIterator { pos: 0, hit_error: false, @@ -591,10 +587,7 @@ mod test { 0x08, 0x00, ]; - let mut iterator = Ipv6OptionsIterator::new(&options, 0); - assert_eq!(iterator.next(), None); - - iterator = Ipv6OptionsIterator::new(&options, 16); + let iterator = Ipv6OptionsIterator::new(&options); for (i, opt) in iterator.enumerate() { match (i, opt) { (0, Ok(Repr::Pad1)) => continue, @@ -615,11 +608,4 @@ mod test { } } } - - #[test] - #[should_panic(expected = "length <= data.len()")] - fn test_options_iter_truncated() { - let options = [0x01, 0x02, 0x00, 0x00]; - let _ = Ipv6OptionsIterator::new(&options, 5); - } } diff --git a/src/wire/ipv6routing.rs b/src/wire/ipv6routing.rs index 1626ddb2f..f5f9c4138 100644 --- a/src/wire/ipv6routing.rs +++ b/src/wire/ipv6routing.rs @@ -1,7 +1,6 @@ use super::{Error, Result}; use core::fmt; -use crate::wire::IpProtocol as Protocol; use crate::wire::Ipv6Address as Address; enum_with_unknown! { @@ -70,31 +69,20 @@ pub struct Header> { // // // See https://tools.ietf.org/html/rfc8200#section-4.4 for details. +// +// **NOTE**: The fields start counting after the header length field. mod field { #![allow(non_snake_case)] use crate::wire::field::*; // Minimum size of the header. - pub const MIN_HEADER_SIZE: usize = 4; + pub const MIN_HEADER_SIZE: usize = 2; - // 8-bit identifier of the header immediately following this header. - pub const NXT_HDR: usize = 0; - // 8-bit unsigned integer. Length of the DATA field in 8-octet units, - // not including the first 8 octets. - pub const LENGTH: usize = 1; // 8-bit identifier of a particular Routing header variant. - pub const TYPE: usize = 2; + pub const TYPE: usize = 0; // 8-bit unsigned integer. The number of route segments remaining. - pub const SEG_LEFT: usize = 3; - // Variable-length field. Routing-Type-specific data. - // - // Length of the header is in 8-octet units, not including the first 8 octets. The first four - // octets are the next header type, the header length, routing type and segments left. - pub const fn DATA(length_field: u8) -> Field { - let bytes = length_field as usize * 8 + 8; - 4..bytes - } + pub const SEG_LEFT: usize = 1; // The Type 2 Routing Header has the following format: // @@ -113,7 +101,7 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 16-byte field containing the home address of the destination mobile node. - pub const HOME_ADDRESS: Field = 8..24; + pub const HOME_ADDRESS: Field = 6..22; // The RPL Source Routing Header has the following format: // @@ -130,20 +118,17 @@ mod field { // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 8-bit field containing the CmprI and CmprE values. - pub const CMPR: usize = 4; + pub const CMPR: usize = 2; // 8-bit field containing the Pad value. - pub const PAD: usize = 5; + pub const PAD: usize = 3; // Variable length field containing addresses - pub const fn ADDRESSES(length_field: u8) -> Field { - let data = DATA(length_field); - 8..data.end - } + pub const ADDRESSES: usize = 6; } /// Core getter methods relevant to any routing type. impl> Header { /// Create a raw octet buffer with an IPv6 Routing Header structure. - pub const fn new(buffer: T) -> Header { + pub const fn new_unchecked(buffer: T) -> Header { Header { buffer } } @@ -152,7 +137,7 @@ impl> Header { /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len pub fn new_checked(buffer: T) -> Result> { - let header = Self::new(buffer); + let header = Self::new_unchecked(buffer); header.check_len()?; Ok(header) } @@ -169,15 +154,10 @@ impl> Header { return Err(Error); } - if len < field::DATA(self.header_len()).end { - return Err(Error); - } - - // The header lenght field could be wrong and thus we need to check this as well: - if matches!(self.routing_type(), Type::Type2) - && field::DATA(self.header_len()).end != field::HOME_ADDRESS.end - { - return Err(Error); + match self.routing_type() { + Type::Type2 if len < field::HOME_ADDRESS.end => return Err(Error), + Type::Rpl if len < field::ADDRESSES => return Err(Error), + _ => (), } Ok(()) @@ -188,21 +168,6 @@ impl> Header { self.buffer } - /// Return the next header field. - #[inline] - pub fn next_header(&self) -> Protocol { - let data = self.buffer.as_ref(); - Protocol::from(data[field::NXT_HDR]) - } - - /// Return the header length field. Length of the Routing header in 8-octet units, - /// not including the first 8 octets. - #[inline] - pub fn header_len(&self) -> u8 { - let data = self.buffer.as_ref(); - data[field::LENGTH] - } - /// Return the routing type field. #[inline] pub fn routing_type(&self) -> Type { @@ -265,26 +230,12 @@ impl> Header { /// This function may panic if this header is not the RPL Source Routing Header routing type. pub fn addresses(&self) -> &[u8] { let data = self.buffer.as_ref(); - &data[field::ADDRESSES(data[field::LENGTH])] + &data[field::ADDRESSES..] } } /// Core setter methods relevant to any routing type. impl + AsMut<[u8]>> Header { - /// Set the next header field. - #[inline] - pub fn set_next_header(&mut self, value: Protocol) { - let data = self.buffer.as_mut(); - data[field::NXT_HDR] = value.into(); - } - - /// Set the option data length. Length of the Routing header in 8-octet units. - #[inline] - pub fn set_header_len(&mut self, value: u8) { - let data = self.buffer.as_mut(); - data[field::LENGTH] = value; - } - /// Set the routing type. #[inline] pub fn set_routing_type(&mut self, value: Type) { @@ -376,8 +327,7 @@ impl + AsMut<[u8]>> Header { /// This function may panic if this header is not the RPL Source Routing Header routing type. pub fn set_addresses(&mut self, value: &[u8]) { let data = self.buffer.as_mut(); - let len = data[field::LENGTH]; - let addresses = &mut data[field::ADDRESSES(len)]; + let addresses = &mut data[field::ADDRESSES..]; addresses.copy_from_slice(value); } } @@ -400,20 +350,12 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> { #[non_exhaustive] pub enum Repr<'a> { Type2 { - /// The type of header immediately following the Routing header. - next_header: Protocol, - /// Length of the Routing header in 8-octet units, not including the first 8 octets. - length: u8, /// Number of route segments remaining. segments_left: u8, /// The home address of the destination mobile node. home_address: Address, }, Rpl { - /// The type of header immediately following the Routing header. - next_header: Protocol, - /// Length of the Routing header in 8-octet units, not including the first 8 octets. - length: u8, /// Number of route segments remaining. segments_left: u8, /// Number of prefix octets from each segment, except the last segment, that are elided. @@ -436,14 +378,10 @@ impl<'a> Repr<'a> { { match header.routing_type() { Type::Type2 => Ok(Repr::Type2 { - next_header: header.next_header(), - length: header.header_len(), segments_left: header.segments_left(), home_address: header.home_address(), }), Type::Rpl => Ok(Repr::Rpl { - next_header: header.next_header(), - length: header.header_len(), segments_left: header.segments_left(), cmpr_i: header.cmpr_i(), cmpr_e: header.cmpr_e(), @@ -459,7 +397,9 @@ impl<'a> Repr<'a> { /// representation. pub const fn buffer_len(&self) -> usize { match self { - &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end, + // Routing Type + Segments Left + Reserved + Home Address + Repr::Type2 { home_address, .. } => 2 + 4 + home_address.as_bytes().len(), + Repr::Rpl { addresses, .. } => 2 + 4 + addresses.len(), } } @@ -467,29 +407,21 @@ impl<'a> Repr<'a> { pub fn emit + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) { match *self { Repr::Type2 { - next_header, - length, segments_left, home_address, } => { - header.set_next_header(next_header); - header.set_header_len(length); header.set_routing_type(Type::Type2); header.set_segments_left(segments_left); header.clear_reserved(); header.set_home_address(home_address); } Repr::Rpl { - next_header, - length, segments_left, cmpr_i, cmpr_e, pad, addresses, } => { - header.set_next_header(next_header); - header.set_header_len(length); header.set_routing_type(Type::Rpl); header.set_segments_left(segments_left); header.set_cmpr_i(cmpr_i); @@ -506,32 +438,33 @@ impl<'a> fmt::Display for Repr<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Repr::Type2 { - next_header, - length, segments_left, home_address, } => { write!( f, - "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", - next_header, - length, + "IPv6 Routing type={} seg_left={} home_address={}", Type::Type2, segments_left, home_address ) } Repr::Rpl { - next_header, - length, segments_left, cmpr_i, cmpr_e, pad, .. } => { - write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", - next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad) + write!( + f, + "IPv6 Routing type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", + Type::Rpl, + segments_left, + cmpr_i, + cmpr_e, + pad + ) } } } @@ -542,30 +475,26 @@ mod test { use super::*; // A Type 2 Routing Header - static BYTES_TYPE2: [u8; 24] = [ - 0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + static BYTES_TYPE2: [u8; 22] = [ + 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, ]; // A representation of a Type 2 Routing header static REPR_TYPE2: Repr = Repr::Type2 { - next_header: Protocol::Tcp, - length: 2, segments_left: 1, home_address: Address::LOOPBACK, }; // A Source Routing Header with full IPv6 addresses in bytes - static BYTES_SRH_FULL: [u8; 40] = [ - 0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x3, 0x1, + static BYTES_SRH_FULL: [u8; 38] = [ + 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3, 0x1, ]; // A representation of a Source Routing Header with full IPv6 addresses static REPR_SRH_FULL: Repr = Repr::Rpl { - next_header: Protocol::Tcp, - length: 4, segments_left: 2, cmpr_i: 0, cmpr_e: 0, @@ -577,14 +506,12 @@ mod test { }; // A Source Routing Header with elided IPv6 addresses in bytes - static BYTES_SRH_ELIDED: [u8; 16] = [ - 0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, + static BYTES_SRH_ELIDED: [u8; 14] = [ + 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, ]; // A representation of a Source Routing Header with elided IPv6 addresses static REPR_SRH_ELIDED: Repr = Repr::Rpl { - next_header: Protocol::Tcp, - length: 1, segments_left: 2, cmpr_i: 15, cmpr_e: 14, @@ -595,41 +522,46 @@ mod test { #[test] fn test_check_len() { // less than min header size - assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..3]).check_len()); - assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..3]).check_len()); - assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..3]).check_len()); - // less than specified length field - assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..23]).check_len()); - assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..39]).check_len()); - assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..11]).check_len()); + assert_eq!( + Err(Error), + Header::new_unchecked(&BYTES_TYPE2[..3]).check_len() + ); + assert_eq!( + Err(Error), + Header::new_unchecked(&BYTES_SRH_FULL[..3]).check_len() + ); + assert_eq!( + Err(Error), + Header::new_unchecked(&BYTES_SRH_ELIDED[..3]).check_len() + ); // valid - assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len()); - assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len()); - assert_eq!(Ok(()), Header::new(&BYTES_SRH_ELIDED[..]).check_len()); + assert_eq!(Ok(()), Header::new_unchecked(&BYTES_TYPE2[..]).check_len()); + assert_eq!( + Ok(()), + Header::new_unchecked(&BYTES_SRH_FULL[..]).check_len() + ); + assert_eq!( + Ok(()), + Header::new_unchecked(&BYTES_SRH_ELIDED[..]).check_len() + ); } #[test] fn test_header_deconstruct() { - let header = Header::new(&BYTES_TYPE2[..]); - assert_eq!(header.next_header(), Protocol::Tcp); - assert_eq!(header.header_len(), 2); + let header = Header::new_unchecked(&BYTES_TYPE2[..]); assert_eq!(header.routing_type(), Type::Type2); assert_eq!(header.segments_left(), 1); assert_eq!(header.home_address(), Address::LOOPBACK); - let header = Header::new(&BYTES_SRH_FULL[..]); - assert_eq!(header.next_header(), Protocol::Tcp); - assert_eq!(header.header_len(), 4); + let header = Header::new_unchecked(&BYTES_SRH_FULL[..]); assert_eq!(header.routing_type(), Type::Rpl); assert_eq!(header.segments_left(), 2); - assert_eq!(header.addresses(), &BYTES_SRH_FULL[8..]); + assert_eq!(header.addresses(), &BYTES_SRH_FULL[6..]); - let header = Header::new(&BYTES_SRH_ELIDED[..]); - assert_eq!(header.next_header(), Protocol::Tcp); - assert_eq!(header.header_len(), 1); + let header = Header::new_unchecked(&BYTES_SRH_ELIDED[..]); assert_eq!(header.routing_type(), Type::Rpl); assert_eq!(header.segments_left(), 2); - assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[8..]); + assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[6..]); } #[test] @@ -649,26 +581,26 @@ mod test { #[test] fn test_repr_emit() { - let mut bytes = [0u8; 24]; - let mut header = Header::new(&mut bytes[..]); + let mut bytes = [0u8; 22]; + let mut header = Header::new_unchecked(&mut bytes[..]); REPR_TYPE2.emit(&mut header); assert_eq!(header.into_inner(), &BYTES_TYPE2[..]); - let mut bytes = [0u8; 40]; - let mut header = Header::new(&mut bytes[..]); + let mut bytes = [0u8; 38]; + let mut header = Header::new_unchecked(&mut bytes[..]); REPR_SRH_FULL.emit(&mut header); assert_eq!(header.into_inner(), &BYTES_SRH_FULL[..]); - let mut bytes = [0u8; 16]; - let mut header = Header::new(&mut bytes[..]); + let mut bytes = [0u8; 14]; + let mut header = Header::new_unchecked(&mut bytes[..]); REPR_SRH_ELIDED.emit(&mut header); assert_eq!(header.into_inner(), &BYTES_SRH_ELIDED[..]); } #[test] fn test_buffer_len() { - assert_eq!(REPR_TYPE2.buffer_len(), 24); - assert_eq!(REPR_SRH_FULL.buffer_len(), 40); - assert_eq!(REPR_SRH_ELIDED.buffer_len(), 16); + assert_eq!(REPR_TYPE2.buffer_len(), 22); + assert_eq!(REPR_SRH_FULL.buffer_len(), 38); + assert_eq!(REPR_SRH_ELIDED.buffer_len(), 14); } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index cb835b55b..3b2c5ffc6 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -101,9 +101,9 @@ mod ipv4; #[cfg(feature = "proto-ipv6")] mod ipv6; #[cfg(feature = "proto-ipv6")] -mod ipv6fragment; +mod ipv6ext_header; #[cfg(feature = "proto-ipv6")] -mod ipv6hopbyhop; +mod ipv6fragment; #[cfg(feature = "proto-ipv6")] mod ipv6option; #[cfg(feature = "proto-ipv6")] @@ -190,12 +190,19 @@ pub use self::ipv6::{ #[cfg(feature = "proto-ipv6")] pub use self::ipv6option::{ - FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr, + FailureType as Ipv6OptionFailureType, Ipv6Option, Ipv6OptionsIterator, Repr as Ipv6OptionRepr, Type as Ipv6OptionType, }; #[cfg(feature = "proto-ipv6")] -pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr}; +pub use self::ipv6ext_header::{Header as Ipv6ExtHeader, Repr as Ipv6ExtHeaderRepr}; + +#[cfg(feature = "proto-ipv6")] +/// A read/write wrapper around an IPv6 Hop-By-Hop header. +pub type Ipv6HopByHopHeader = Ipv6ExtHeader; +#[cfg(feature = "proto-ipv6")] +/// A high-level representation of an IPv6 Hop-By-Hop heade. +pub type Ipv6HopByHopRepr<'a> = Ipv6ExtHeaderRepr<'a>; #[cfg(feature = "proto-ipv6")] pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr}; From 23d0754125a2d5b7c6e409c18bc944d7db39ab29 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 24 Apr 2023 15:36:51 +0200 Subject: [PATCH 546/566] Add tests for 6LoWPAN extension headers. Signed-off-by: Thibaut Vandervelden --- src/wire/sixlowpan.rs | 139 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 2 deletions(-) diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 80b25c408..222bb3c10 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -1668,7 +1668,7 @@ pub mod nhc { impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> { /// Return a pointer to the payload. pub fn payload(&self) -> &'a [u8] { - let start = 1 + self.next_header_size(); + let start = 2 + self.next_header_size(); &self.buffer.as_ref()[start..] } } @@ -1775,6 +1775,141 @@ pub mod nhc { } } + #[cfg(test)] + mod tests { + use super::*; + + use crate::wire::{Ipv6RoutingHeader, Ipv6RoutingRepr}; + + #[cfg(feature = "proto-rpl")] + use crate::wire::{ + Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, RplHopByHopRepr, RplInstanceId, + }; + + #[cfg(feature = "proto-rpl")] + const RPL_HOP_BY_HOP_PACKET: [u8; 9] = + [0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00, 0x1e, 0x03, 0x00]; + + const ROUTING_SR_PACKET: [u8; 32] = [ + 0xe3, 0x1e, 0x03, 0x03, 0x99, 0x30, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + ]; + + #[test] + #[cfg(feature = "proto-rpl")] + fn test_rpl_hop_by_hop_option_deconstruct() { + let header = ExtHeaderPacket::new_checked(&RPL_HOP_BY_HOP_PACKET).unwrap(); + assert_eq!( + header.next_header(), + NextHeader::Uncompressed(IpProtocol::Icmpv6) + ); + assert_eq!(header.extension_header_id(), ExtHeaderId::HopByHopHeader); + + let options = header.payload(); + let mut options = Ipv6OptionsIterator::new(options); + let rpl_repr = options.next().unwrap(); + let rpl_repr = rpl_repr.unwrap(); + + match rpl_repr { + Ipv6OptionRepr::Rpl(rpl) => { + assert_eq!( + rpl, + RplHopByHopRepr { + down: false, + rank_error: false, + forwarding_error: false, + instance_id: RplInstanceId::from(0x1e), + sender_rank: 0x0300, + } + ); + } + _ => unreachable!(), + } + } + + #[test] + #[cfg(feature = "proto-rpl")] + fn test_rpl_hop_by_hop_option_emit() { + let repr = Ipv6OptionRepr::Rpl(RplHopByHopRepr { + down: false, + rank_error: false, + forwarding_error: false, + instance_id: RplInstanceId::from(0x1e), + sender_rank: 0x0300, + }); + + let ext_hdr = ExtHeaderRepr { + ext_header_id: ExtHeaderId::HopByHopHeader, + next_header: NextHeader::Uncompressed(IpProtocol::Icmpv6), + length: repr.buffer_len() as u8, + }; + + let mut buffer = vec![0u8; ext_hdr.buffer_len() + repr.buffer_len()]; + ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked( + &mut buffer[..ext_hdr.buffer_len()], + )); + repr.emit(&mut Ipv6Option::new_unchecked( + &mut buffer[ext_hdr.buffer_len()..], + )); + + assert_eq!(&buffer[..], RPL_HOP_BY_HOP_PACKET); + } + + #[test] + fn test_source_routing_deconstruct() { + let header = ExtHeaderPacket::new_checked(&ROUTING_SR_PACKET).unwrap(); + assert_eq!(header.next_header(), NextHeader::Compressed); + assert_eq!(header.extension_header_id(), ExtHeaderId::RoutingHeader); + + let routing_hdr = Ipv6RoutingHeader::new_checked(header.payload()).unwrap(); + let repr = Ipv6RoutingRepr::parse(&routing_hdr).unwrap(); + assert_eq!( + repr, + Ipv6RoutingRepr::Rpl { + segments_left: 3, + cmpr_i: 9, + cmpr_e: 9, + pad: 3, + addresses: &[ + 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, + 0x00, 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00 + ], + } + ); + } + + #[test] + fn test_source_routing_emit() { + let routing_hdr = Ipv6RoutingRepr::Rpl { + segments_left: 3, + cmpr_i: 9, + cmpr_e: 9, + pad: 3, + addresses: &[ + 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + ], + }; + + let ext_hdr = ExtHeaderRepr { + ext_header_id: ExtHeaderId::RoutingHeader, + next_header: NextHeader::Compressed, + length: routing_hdr.buffer_len() as u8, + }; + + let mut buffer = vec![0u8; ext_hdr.buffer_len() + routing_hdr.buffer_len()]; + ext_hdr.emit(&mut ExtHeaderPacket::new_unchecked( + &mut buffer[..ext_hdr.buffer_len()], + )); + routing_hdr.emit(&mut Ipv6RoutingHeader::new_unchecked( + &mut buffer[ext_hdr.buffer_len()..], + )); + + assert_eq!(&buffer[..], ROUTING_SR_PACKET); + } + } + /// A read/write wrapper around a 6LoWPAN_NHC UDP frame. /// [RFC 6282 § 4.3] specifies the format of the header. /// @@ -2128,7 +2263,7 @@ pub mod nhc { assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER); assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader); - assert_eq!(packet.payload(), [0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); + assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]); } #[test] From 74fd2aeeb64fae8d3bc39755d2669a368846f50b Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Fri, 12 May 2023 08:52:43 +0200 Subject: [PATCH 547/566] IPv4: Don't discard from unspecified src addresses --- src/iface/interface/ipv4.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index 50aff2838..e1cbbc60e 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -18,9 +18,9 @@ impl InterfaceInner { frag: &'a mut FragmentsBuffer, ) -> Option> { let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); - if !self.is_unicast_v4(ipv4_repr.src_addr) { - // Discard packets with non-unicast source addresses. - net_debug!("non-unicast source address"); + if !self.is_unicast_v4(ipv4_repr.src_addr) && !ipv4_repr.src_addr.is_unspecified() { + // Discard packets with non-unicast source addresses but allow unspecified + net_debug!("non-unicast or unspecified source address"); return None; } From 7e12265f0014b75839cac8339336cb5536448a88 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2023 22:07:29 +0800 Subject: [PATCH 548/566] tcp: Wake tx waker after abort() It is useful to wake once the RST packet has been emitted, so async callers can wait before dropping the socket. --- src/socket/tcp.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index bcec24d84..228972f5f 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2231,6 +2231,11 @@ impl<'a> Socket<'a> { if self.state == State::Closed { // When aborting a connection, forget about it after sending a single RST packet. self.tuple = None; + #[cfg(feature = "async")] + { + // Wake tx now so that async users can wait for the RST to be sent + self.tx_waker.wake(); + } } Ok(()) From d02d002c0b21107489537fe9808230317f8251ef Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 30 May 2023 10:17:30 +0200 Subject: [PATCH 549/566] add(rpl): relations table The relation table will containt the next hop for each node in the RPL DODAG. For MOP1, this is the parent of each node. For MOP2, this is a neighbor of a sender. Signed-off-by: Thibaut Vandervelden --- Cargo.toml | 9 +++ build.rs | 1 + gen_config.py | 1 + src/iface/rpl/mod.rs | 1 + src/iface/rpl/relations.rs | 162 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 6 files changed, 175 insertions(+) create mode 100644 src/iface/rpl/relations.rs diff --git a/Cargo.toml b/Cargo.toml index 9ee30c2ea..280ba7d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -214,6 +214,15 @@ dns-max-name-size-64 = [] dns-max-name-size-128 = [] dns-max-name-size-255 = [] # Default +rpl-relations-buffer-count-1 = [] +rpl-relations-buffer-count-2 = [] +rpl-relations-buffer-count-4 = [] +rpl-relations-buffer-count-8 = [] +rpl-relations-buffer-count-16 = [] # Default +rpl-relations-buffer-count-32 = [] +rpl-relations-buffer-count-64 = [] +rpl-relations-buffer-count-128 = [] + # END AUTOGENERATED CONFIG FEATURES [[example]] diff --git a/build.rs b/build.rs index 760ee189e..19dd86364 100644 --- a/build.rs +++ b/build.rs @@ -18,6 +18,7 @@ static CONFIGS: &[(&str, usize)] = &[ ("DNS_MAX_RESULT_COUNT", 1), ("DNS_MAX_SERVER_COUNT", 1), ("DNS_MAX_NAME_SIZE", 255), + ("RPL_RELATIONS_BUFFER_COUNT", 16), // END AUTOGENERATED CONFIG FEATURES ]; diff --git a/gen_config.py b/gen_config.py index 83d587380..46549b702 100644 --- a/gen_config.py +++ b/gen_config.py @@ -39,6 +39,7 @@ def feature(name, default, min, max, pow2=None): feature("dns_max_result_count", default=1, min=1, max=32, pow2=4) feature("dns_max_server_count", default=1, min=1, max=32, pow2=4) feature("dns_max_name_size", default=255, min=64, max=255, pow2=True) +feature("rpl_relations_buffer_count", default=16, min=1, max=128, pow2=True) # ========= Update Cargo.toml diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs index 2decec054..d7da7aa15 100644 --- a/src/iface/rpl/mod.rs +++ b/src/iface/rpl/mod.rs @@ -3,4 +3,5 @@ mod consts; mod lollipop; mod rank; +mod relations; mod trickle; diff --git a/src/iface/rpl/relations.rs b/src/iface/rpl/relations.rs new file mode 100644 index 000000000..da02a3cf9 --- /dev/null +++ b/src/iface/rpl/relations.rs @@ -0,0 +1,162 @@ +use crate::time::Instant; +use crate::wire::Ipv6Address; + +use crate::config::RPL_RELATIONS_BUFFER_COUNT; + +#[derive(Debug)] +pub struct Relation { + destination: Ipv6Address, + next_hop: Ipv6Address, + expiration: Instant, +} + +#[derive(Default, Debug)] +pub struct Relations { + relations: heapless::Vec, +} + +impl Relations { + /// Add a new relation to the buffer. If there was already a relation in the buffer, then + /// update it. + pub fn add_relation( + &mut self, + destination: Ipv6Address, + next_hop: Ipv6Address, + expiration: Instant, + ) { + if let Some(r) = self + .relations + .iter_mut() + .find(|r| r.destination == destination) + { + r.next_hop = next_hop; + r.expiration = expiration; + } else { + let relation = Relation { + destination, + next_hop, + expiration, + }; + + if let Err(e) = self.relations.push(relation) { + net_debug!("Unable to add relation, buffer is full"); + } + } + } + + /// Remove all relation entries for a specific destination. + pub fn remove_relation(&mut self, destination: Ipv6Address) { + self.relations.retain(|r| r.destination != destination) + } + + /// Return the next hop for a specific IPv6 address, if there is one. + pub fn find_next_hop(&mut self, destination: Ipv6Address) -> Option { + self.relations.iter().find_map(|r| { + if r.destination == destination { + Some(r.next_hop) + } else { + None + } + }) + } + + /// Purge expired relations. + pub fn purge(&mut self, now: Instant) { + self.relations.retain(|r| r.expiration > now) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::time::Duration; + + fn addresses(count: usize) -> Vec { + (0..count) + .map(|i| { + let mut ip = Ipv6Address::default(); + ip.0[0] = i as u8; + ip + }) + .collect() + } + + #[test] + fn add_relation() { + let addrs = addresses(2); + + let mut relations = Relations::default(); + relations.add_relation(addrs[0], addrs[1], Instant::now()); + assert_eq!(relations.relations.len(), 1); + } + + #[test] + fn add_relations_full_buffer() { + let addrs = addresses(crate::config::RPL_RELATIONS_BUFFER_COUNT + 1); + + // Try to add RPL_RELATIONS_BUFFER_COUNT + 1 to the buffer. + // The size of the buffer should still be RPL_RELATIONS_BUFFER_COUNT. + let mut relations = Relations::default(); + for a in addrs { + relations.add_relation(a, a, Instant::now()); + } + + assert_eq!(relations.relations.len(), RPL_RELATIONS_BUFFER_COUNT); + } + + #[test] + fn update_relation() { + let addrs = addresses(3); + + let mut relations = Relations::default(); + relations.add_relation(addrs[0], addrs[1], Instant::now()); + assert_eq!(relations.relations.len(), 1); + + relations.add_relation(addrs[0], addrs[2], Instant::now()); + assert_eq!(relations.relations.len(), 1); + + assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[2])); + } + + #[test] + fn find_next_hop() { + let addrs = addresses(3); + + let mut relations = Relations::default(); + relations.add_relation(addrs[0], addrs[1], Instant::now()); + assert_eq!(relations.relations.len(), 1); + assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[1])); + + relations.add_relation(addrs[0], addrs[2], Instant::now()); + assert_eq!(relations.relations.len(), 1); + assert_eq!(relations.find_next_hop(addrs[0]), Some(addrs[2])); + + // Find the next hop of a destination not in the buffer. + assert_eq!(relations.find_next_hop(addrs[1]), None); + } + + #[test] + fn remove_relation() { + let addrs = addresses(2); + + let mut relations = Relations::default(); + relations.add_relation(addrs[0], addrs[1], Instant::now()); + assert_eq!(relations.relations.len(), 1); + + relations.remove_relation(addrs[0]); + assert!(relations.relations.is_empty()); + } + + #[test] + fn purge_relation() { + let addrs = addresses(2); + + let mut relations = Relations::default(); + relations.add_relation(addrs[0], addrs[1], Instant::now() - Duration::from_secs(1)); + + assert_eq!(relations.relations.len(), 1); + + relations.purge(Instant::now()); + assert!(relations.relations.is_empty()); + } +} diff --git a/src/lib.rs b/src/lib.rs index ee8abdd5d..1256292b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,7 @@ mod config { pub const IFACE_NEIGHBOR_CACHE_COUNT: usize = 3; pub const REASSEMBLY_BUFFER_COUNT: usize = 4; pub const REASSEMBLY_BUFFER_SIZE: usize = 1500; + pub const RPL_RELATIONS_BUFFER_COUNT: usize = 16; } #[cfg(not(test))] From 5c6475aaa55a6fa6cff4e8cb01f6d9be97487fbc Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 5 Jun 2023 10:56:40 +0200 Subject: [PATCH 550/566] fix(791): wrong payload length of first IPv4 frag The payload length of the first IPv4 fragment packet contained the length of the unfragmented packet. This was because the `repr` was a clone and not a mutable ref to `ip_repr`. This is now fixed. We also didn't check that the full IP packet fits in the fragmentation buffer, which should contain the unfragmented emitted packet. Fixes #791. Signed-off-by: Thibaut Vandervelden --- src/iface/interface/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 34ad4bd24..3a684e458 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1643,7 +1643,7 @@ impl InterfaceInner { packet: IpPacket, frag: &mut Fragmenter, ) -> Result<(), DispatchError> { - let ip_repr = packet.ip_repr(); + let mut ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); // Dispatch IEEE802.15.4: @@ -1724,9 +1724,9 @@ impl InterfaceInner { let total_ip_len = ip_repr.buffer_len(); - match ip_repr { + match &mut ip_repr { #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(mut repr) => { + IpRepr::Ipv4(repr) => { // If we have an IPv4 packet, then we need to check if we need to fragment it. if total_ip_len > self.caps.max_transmission_unit { #[cfg(feature = "proto-ipv4-fragmentation")] @@ -1739,10 +1739,10 @@ impl InterfaceInner { let ip_header_len = repr.buffer_len(); let first_frag_ip_len = self.caps.ip_mtu(); - if frag.buffer.len() < first_frag_ip_len { + if frag.buffer.len() < total_ip_len { net_debug!( "Fragmentation buffer is too small, at least {} needed. Dropping", - first_frag_ip_len + total_ip_len ); return Ok(()); } @@ -1757,7 +1757,7 @@ impl InterfaceInner { frag.packet_len = total_ip_len; // Save the IP header for other fragments. - frag.ipv4.repr = repr; + frag.ipv4.repr = *repr; // Save how much bytes we will send now. frag.sent_bytes = first_frag_ip_len; From 8065502596a371f738fda70d3041f8bc9afe807f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Jun 2023 19:23:49 +0200 Subject: [PATCH 551/566] Switch from bors to github merge queue. --- .github/bors.toml | 6 ------ .github/workflows/coverage.yml | 3 +-- .github/workflows/fuzz.yml | 3 +-- .github/workflows/rustfmt.yaml | 3 +-- .github/workflows/test.yml | 3 +-- 5 files changed, 4 insertions(+), 14 deletions(-) delete mode 100644 .github/bors.toml diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 24c019188..000000000 --- a/.github/bors.toml +++ /dev/null @@ -1,6 +0,0 @@ -status = [ - "tests", - "fuzz", - "clippy", - "fmt", -] diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 79056061a..1d03357b7 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,7 +1,6 @@ on: - push: - branches: [staging, trying] pull_request: + merge_group: name: Coverage diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 60113b48a..e7a787afa 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -1,7 +1,6 @@ on: - push: - branches: [ staging, trying ] pull_request: + merge_group: name: Fuzz diff --git a/.github/workflows/rustfmt.yaml b/.github/workflows/rustfmt.yaml index edf3ae96c..107164af5 100644 --- a/.github/workflows/rustfmt.yaml +++ b/.github/workflows/rustfmt.yaml @@ -1,7 +1,6 @@ on: - push: - branches: [ staging, trying ] pull_request: + merge_group: name: Rustfmt check jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba342127e..ee6774c98 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,6 @@ on: - push: - branches: [staging, trying] pull_request: + merge_group: name: Test From f68603d8b8915a1fb26a34f9236cf34d5d6fbef9 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 5 Jun 2023 13:03:44 +0200 Subject: [PATCH 552/566] Add objective function and parent set --- Cargo.toml | 6 ++ build.rs | 1 + gen_config.py | 1 + src/iface/rpl/mod.rs | 2 + src/iface/rpl/of0.rs | 131 +++++++++++++++++++++++++++ src/iface/rpl/parents.rs | 189 +++++++++++++++++++++++++++++++++++++++ src/iface/rpl/rank.rs | 6 +- src/lib.rs | 1 + 8 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 src/iface/rpl/of0.rs create mode 100644 src/iface/rpl/parents.rs diff --git a/Cargo.toml b/Cargo.toml index 280ba7d0d..f9a4b8a73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -223,6 +223,12 @@ rpl-relations-buffer-count-32 = [] rpl-relations-buffer-count-64 = [] rpl-relations-buffer-count-128 = [] +rpl-parents-buffer-count-2 = [] +rpl-parents-buffer-count-4 = [] +rpl-parents-buffer-count-8 = [] # Default +rpl-parents-buffer-count-16 = [] +rpl-parents-buffer-count-32 = [] + # END AUTOGENERATED CONFIG FEATURES [[example]] diff --git a/build.rs b/build.rs index 19dd86364..568713925 100644 --- a/build.rs +++ b/build.rs @@ -19,6 +19,7 @@ static CONFIGS: &[(&str, usize)] = &[ ("DNS_MAX_SERVER_COUNT", 1), ("DNS_MAX_NAME_SIZE", 255), ("RPL_RELATIONS_BUFFER_COUNT", 16), + ("RPL_PARENTS_BUFFER_COUNT", 8), // END AUTOGENERATED CONFIG FEATURES ]; diff --git a/gen_config.py b/gen_config.py index 46549b702..5d327a6db 100644 --- a/gen_config.py +++ b/gen_config.py @@ -40,6 +40,7 @@ def feature(name, default, min, max, pow2=None): feature("dns_max_server_count", default=1, min=1, max=32, pow2=4) feature("dns_max_name_size", default=255, min=64, max=255, pow2=True) feature("rpl_relations_buffer_count", default=16, min=1, max=128, pow2=True) +feature("rpl_parents_buffer_count", default=8, min=2, max=32, pow2=True) # ========= Update Cargo.toml diff --git a/src/iface/rpl/mod.rs b/src/iface/rpl/mod.rs index d7da7aa15..69aa9ae77 100644 --- a/src/iface/rpl/mod.rs +++ b/src/iface/rpl/mod.rs @@ -2,6 +2,8 @@ mod consts; mod lollipop; +mod of0; +mod parents; mod rank; mod relations; mod trickle; diff --git a/src/iface/rpl/of0.rs b/src/iface/rpl/of0.rs new file mode 100644 index 000000000..4a7714310 --- /dev/null +++ b/src/iface/rpl/of0.rs @@ -0,0 +1,131 @@ +use super::parents::*; +use super::rank::Rank; + +pub struct ObjectiveFunction0; + +pub(crate) trait ObjectiveFunction { + const OCP: u16; + + /// Return the new calculated Rank, based on information from the parent. + fn rank(current_rank: Rank, parent_rank: Rank) -> Rank; + + /// Return the preferred parent from a given parent set. + fn preferred_parent(parent_set: &ParentSet) -> Option<&Parent>; +} + +impl ObjectiveFunction0 { + const OCP: u16 = 0; + + const RANK_STRETCH: u16 = 0; + const RANK_FACTOR: u16 = 1; + const RANK_STEP: u16 = 3; + + fn rank_increase(parent_rank: Rank) -> u16 { + (Self::RANK_FACTOR * Self::RANK_STEP + Self::RANK_STRETCH) + * parent_rank.min_hop_rank_increase + } +} + +impl ObjectiveFunction for ObjectiveFunction0 { + const OCP: u16 = 0; + + fn rank(_: Rank, parent_rank: Rank) -> Rank { + assert_ne!(parent_rank, Rank::INFINITE); + + Rank::new( + parent_rank.value + Self::rank_increase(parent_rank), + parent_rank.min_hop_rank_increase, + ) + } + + fn preferred_parent(parent_set: &ParentSet) -> Option<&Parent> { + let mut pref_parent: Option<&Parent> = None; + + for parent in parent_set.parents() { + if pref_parent.is_none() || parent.rank() < pref_parent.unwrap().rank() { + pref_parent = Some(parent); + } + } + + pref_parent + } +} + +#[cfg(test)] +mod tests { + use crate::iface::rpl::consts::DEFAULT_MIN_HOP_RANK_INCREASE; + + use super::*; + + #[test] + fn rank_increase() { + // 256 (root) + 3 * 256 + assert_eq!( + ObjectiveFunction0::rank(Rank::INFINITE, Rank::ROOT), + Rank::new(256 + 3 * 256, DEFAULT_MIN_HOP_RANK_INCREASE) + ); + + // 1024 + 3 * 256 + assert_eq!( + ObjectiveFunction0::rank( + Rank::INFINITE, + Rank::new(1024, DEFAULT_MIN_HOP_RANK_INCREASE) + ), + Rank::new(1024 + 3 * 256, DEFAULT_MIN_HOP_RANK_INCREASE) + ); + } + + #[test] + #[should_panic] + fn rank_increase_infinite() { + assert_eq!( + ObjectiveFunction0::rank(Rank::INFINITE, Rank::INFINITE), + Rank::INFINITE + ); + } + + #[test] + fn empty_set() { + assert_eq!( + ObjectiveFunction0::preferred_parent(&ParentSet::default()), + None + ); + } + + #[test] + fn non_empty_set() { + use crate::wire::Ipv6Address; + + let mut parents = ParentSet::default(); + + parents.add(Parent::new( + Ipv6Address::default(), + 0, + Rank::ROOT, + Default::default(), + Ipv6Address::default(), + )); + + let mut address = Ipv6Address::default(); + address.0[15] = 1; + + parents.add(Parent::new( + address, + 0, + Rank::new(1024, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + Ipv6Address::default(), + )); + + assert_eq!( + ObjectiveFunction0::preferred_parent(&parents), + Some(&Parent::new( + Ipv6Address::default(), + 0, + Rank::ROOT, + Default::default(), + Ipv6Address::default(), + )) + ); + } +} diff --git a/src/iface/rpl/parents.rs b/src/iface/rpl/parents.rs new file mode 100644 index 000000000..724008d5b --- /dev/null +++ b/src/iface/rpl/parents.rs @@ -0,0 +1,189 @@ +use crate::wire::Ipv6Address; + +use super::{lollipop::SequenceCounter, rank::Rank}; +use crate::config::RPL_PARENTS_BUFFER_COUNT; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub(crate) struct Parent { + rank: Rank, + address: Ipv6Address, + preference: u8, + version_number: SequenceCounter, + dodag_id: Ipv6Address, +} + +impl Parent { + /// Create a new parent. + pub(crate) fn new( + address: Ipv6Address, + preference: u8, + rank: Rank, + version_number: SequenceCounter, + dodag_id: Ipv6Address, + ) -> Self { + Self { + rank, + address, + preference, + version_number, + dodag_id, + } + } + + /// Return the Rank of the parent. + pub(crate) fn rank(&self) -> &Rank { + &self.rank + } +} + +#[derive(Debug, Default)] +pub(crate) struct ParentSet { + parents: heapless::Vec, +} + +impl ParentSet { + /// Add a new parent to the parent set. The Rank of the new parent should be lower than the + /// Rank of the node that holds this parent set. + pub(crate) fn add(&mut self, parent: Parent) { + if let Some(p) = self.find_mut(parent.address) { + // Update information + *p = parent; + } else if let Err(p) = self.parents.push(parent) { + // Look for the worst parent + if let Some(worst) = self.worst_parent() { + if worst.rank().dag_rank() > parent.rank().dag_rank() { + *worst = parent; + } else { + net_debug!("could not add parent"); + } + } else { + // WARNING: there should be a worst parent, since the list of parents is not empty + unreachable!(); + } + } + } + + /// Find a parent based on its address. + pub(crate) fn find(&self, address: Ipv6Address) -> Option<&Parent> { + self.parents.iter().find(|p| p.address == address) + } + + /// Find a mutable parent based on its address. + pub(crate) fn find_mut(&mut self, address: Ipv6Address) -> Option<&mut Parent> { + self.parents.iter_mut().find(|p| p.address == address) + } + + /// Return a slice to the parent set. + pub(crate) fn parents(&self) -> &[Parent] { + &self.parents + } + + /// Find the worst parent that is currently in the parent set. + fn worst_parent(&mut self) -> Option<&mut Parent> { + let mut worst: Option<&mut Parent> = None; + + for p in self.parents.iter_mut() { + if worst.is_none() || worst.as_mut().unwrap().rank.dag_rank() < p.rank.dag_rank() { + worst = Some(p); + } + } + + worst + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add_parent() { + let mut set = ParentSet::default(); + set.add(Parent::new( + Default::default(), + 0, + Rank::ROOT, + Default::default(), + Default::default(), + )); + + assert_eq!( + set.find(Default::default()), + Some(&Parent::new( + Default::default(), + 0, + Rank::ROOT, + Default::default(), + Default::default() + )) + ); + } + + #[test] + fn add_more_parents() { + use super::super::consts::DEFAULT_MIN_HOP_RANK_INCREASE; + let mut set = ParentSet::default(); + + let mut last_address = Default::default(); + for i in 0..RPL_PARENTS_BUFFER_COUNT { + let i = i as u16; + let mut address = Ipv6Address::default(); + address.0[15] = i as u8; + last_address = address; + + set.add(Parent::new( + address, + 0, + Rank::new(256 * i, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + )); + + assert_eq!( + set.find(address), + Some(&Parent::new( + address, + 0, + Rank::new(256 * i, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + )) + ); + } + + // This one is not added to the set, because its Rank is worse than any other parent in the + // set. + let mut address = Ipv6Address::default(); + address.0[15] = 8; + set.add(Parent::new( + address, + 0, + Rank::new(256 * 8, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + )); + assert_eq!(set.find(address), None); + + /// This Parent has a better rank than the last one in the set. + let mut address = Ipv6Address::default(); + address.0[15] = 9; + set.add(Parent::new( + address, + 0, + Rank::new(0, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + )); + assert_eq!( + set.find(address), + Some(&Parent::new( + address, + 0, + Rank::new(0, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address + )) + ); + assert_eq!(set.find(last_address), None); + } +} diff --git a/src/iface/rpl/rank.rs b/src/iface/rpl/rank.rs index daac198e5..02a5ecf59 100644 --- a/src/iface/rpl/rank.rs +++ b/src/iface/rpl/rank.rs @@ -25,8 +25,8 @@ use super::consts::DEFAULT_MIN_HOP_RANK_INCREASE; #[derive(Debug, Clone, Copy, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Rank { - value: u16, - min_hop_rank_increase: u16, + pub(super) value: u16, + pub(super) min_hop_rank_increase: u16, } impl core::fmt::Display for Rank { @@ -46,6 +46,8 @@ impl Rank { /// The `MinHopRankIncrease` is used for calculating the integer part for comparing to other /// Ranks. pub const fn new(value: u16, min_hop_rank_increase: u16) -> Self { + assert!(min_hop_rank_increase > 0); + Self { value, min_hop_rank_increase, diff --git a/src/lib.rs b/src/lib.rs index 1256292b0..6128c2314 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,6 +145,7 @@ mod config { pub const REASSEMBLY_BUFFER_COUNT: usize = 4; pub const REASSEMBLY_BUFFER_SIZE: usize = 1500; pub const RPL_RELATIONS_BUFFER_COUNT: usize = 16; + pub const RPL_PARENTS_BUFFER_COUNT: usize = 8; } #[cfg(not(test))] From 533f103a9544fa0de7d75383b13fc021f7b0642b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 12 Jun 2023 16:31:38 +0200 Subject: [PATCH 553/566] pass the now time when creating the iface Signed-off-by: Thibaut Vandervelden --- examples/benchmark.rs | 2 +- examples/client.rs | 2 +- examples/dhcp_client.rs | 2 +- examples/dns.rs | 2 +- examples/httpclient.rs | 2 +- examples/loopback.rs | 2 +- examples/multicast.rs | 2 +- examples/ping.rs | 2 +- examples/server.rs | 2 +- examples/sixlowpan.rs | 2 +- examples/sixlowpan_benchmark.rs | 2 +- src/iface/interface/mod.rs | 4 ++-- src/iface/interface/tests.rs | 8 ++++---- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index b0cedcfbc..ad2c6e142 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -97,7 +97,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/client.rs b/examples/client.rs index 5422aded9..c18c08ff7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -38,7 +38,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index d118219d9..9ef46c27b 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -36,7 +36,7 @@ fn main() { Medium::Ieee802154 => todo!(), }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); // Create sockets let mut dhcp_socket = dhcpv4::Socket::new(); diff --git a/examples/dns.rs b/examples/dns.rs index da066aaf3..977f40546 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -33,7 +33,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 751916da4..8f3a53aa7 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -38,7 +38,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/loopback.rs b/examples/loopback.rs index 9bca85e55..7ca95b188 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -91,7 +91,7 @@ fn main() { Medium::Ieee802154 => todo!(), }; - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) diff --git a/examples/multicast.rs b/examples/multicast.rs index ec7ff4354..ea89a2e93 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -37,7 +37,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/ping.rs b/examples/ping.rs index 7e6cbdb01..341413a3a 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -114,7 +114,7 @@ fn main() { }; config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/server.rs b/examples/server.rs index 67df63927..33d95c5d5 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -34,7 +34,7 @@ fn main() { config.random_seed = rand::random(); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)) diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 5b41116cf..9d474e3fe 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -79,7 +79,7 @@ fn main() { config.random_seed = rand::random(); config.pan_id = Some(Ieee802154Pan(0xbeef)); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new( diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index a389ca94d..b459d8d84 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -159,7 +159,7 @@ fn main() { config.random_seed = rand::random(); config.pan_id = Some(Ieee802154Pan(0xbeef)); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::now()); iface.update_ip_addrs(|ip_addrs| { ip_addrs .push(IpCidr::new( diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 3a684e458..32e056be9 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -461,7 +461,7 @@ impl Interface { /// /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache - pub fn new(config: Config, device: &mut D) -> Self + pub fn new(config: Config, device: &mut D, now: Instant) -> Self where D: Device + ?Sized, { @@ -519,7 +519,7 @@ impl Interface { }, fragmenter: Fragmenter::new(), inner: InterfaceInner { - now: Instant::from_secs(0), + now, caps, hardware_addr: config.hardware_addr, ip_addrs: Vec::new(), diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 7459054fa..879c42489 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -40,7 +40,7 @@ fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ip); let config = Config::new(HardwareAddress::Ip); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::ZERO); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv4")] ip_addrs @@ -65,7 +65,7 @@ fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ethernet); let config = Config::new(HardwareAddress::Ethernet(EthernetAddress::default())); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::ZERO); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv4")] ip_addrs @@ -90,7 +90,7 @@ fn create_ieee802154<'a>() -> (Interface, SocketSet<'a>, Loopback) { let mut device = Loopback::new(Medium::Ieee802154); let config = Config::new(HardwareAddress::Ieee802154(Ieee802154Address::default())); - let mut iface = Interface::new(config, &mut device); + let mut iface = Interface::new(config, &mut device, Instant::ZERO); iface.update_ip_addrs(|ip_addrs| { #[cfg(feature = "proto-ipv6")] ip_addrs @@ -136,7 +136,7 @@ impl TxToken for MockTxToken { fn test_new_panic() { let mut device = Loopback::new(Medium::Ethernet); let config = Config::new(HardwareAddress::Ip); - Interface::new(config, &mut device); + Interface::new(config, &mut device, Instant::ZERO); } #[test] From 7d78370ded341b05eb3a60691c9e3efb876ae026 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 14 Jun 2023 23:15:02 +0200 Subject: [PATCH 554/566] iface: add support for sending to subnet-local broadcast addrs (like 192.168.1.255). --- src/iface/interface/mod.rs | 33 ++++++++++++++++++++++----------- src/iface/interface/tests.rs | 32 +++++++++++++++++++++----------- src/socket/udp.rs | 4 ++-- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 3a684e458..8361787ad 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1312,10 +1312,25 @@ impl InterfaceInner { handled_by_raw_socket } - /// Checks if an incoming packet has a broadcast address for the interfaces - /// associated ipv4 addresses. + /// Checks if an address is broadcast, taking into account ipv4 subnet-local + /// broadcast addresses. + pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool { + match address { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(address) => self.is_broadcast_v4(*address), + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(_) => false, + } + } + + /// Checks if an address is broadcast, taking into account ipv4 subnet-local + /// broadcast addresses. #[cfg(feature = "proto-ipv4")] - fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { + pub(crate) fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { + if address.is_broadcast() { + return true; + } + self.ip_addrs .iter() .filter_map(|own_cidr| match own_cidr { @@ -1326,16 +1341,10 @@ impl InterfaceInner { .any(|broadcast_address| address == broadcast_address) } - /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses - #[cfg(feature = "proto-ipv4")] - fn is_broadcast_v4(&self, address: Ipv4Address) -> bool { - address.is_broadcast() || self.is_subnet_broadcast(address) - } - /// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses #[cfg(feature = "proto-ipv4")] fn is_unicast_v4(&self, address: Ipv4Address) -> bool { - address.is_unicast() && !self.is_subnet_broadcast(address) + address.is_unicast() && !self.is_broadcast_v4(address) } #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] @@ -1475,6 +1484,8 @@ impl InterfaceInner { fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option { // Send directly. + // note: no need to use `self.is_broadcast()` to check for subnet-local broadcast addrs + // here because `in_same_network` will already return true. if self.in_same_network(addr) || addr.is_broadcast() { return Some(*addr); } @@ -1508,7 +1519,7 @@ impl InterfaceInner { where Tx: TxToken, { - if dst_addr.is_broadcast() { + if self.is_broadcast(dst_addr) { let hardware_addr = match self.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST), diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 7459054fa..cd84c1811 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -268,46 +268,56 @@ fn test_local_subnet_broadcasts() { assert!(iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])),); + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])),); + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); + assert!(iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 255]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 254]))); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); }); }); + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])),); + .is_broadcast_v4(Ipv4Address([192, 168, 23, 255]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])),); + .is_broadcast_v4(Ipv4Address([192, 168, 23, 254]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])),); + .is_broadcast_v4(Ipv4Address([192, 168, 255, 254]))); assert!(iface .inner - .is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])),); + .is_broadcast_v4(Ipv4Address([192, 168, 255, 255]))); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); }); }); - assert!(!iface + assert!(iface .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])),); + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])),); + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 255]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 254]))); assert!(!iface .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])),); + .is_broadcast_v4(Ipv4Address([192, 255, 255, 254]))); assert!(iface .inner - .is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])),); + .is_broadcast_v4(Ipv4Address([192, 255, 255, 255]))); } #[test] diff --git a/src/socket/udp.rs b/src/socket/udp.rs index bbf2e37bd..cdf6e7f33 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -405,13 +405,13 @@ impl<'a> Socket<'a> { Ok((length, endpoint)) } - pub(crate) fn accepts(&self, _cx: &mut Context, ip_repr: &IpRepr, repr: &UdpRepr) -> bool { + pub(crate) fn accepts(&self, cx: &mut Context, ip_repr: &IpRepr, repr: &UdpRepr) -> bool { if self.endpoint.port != repr.dst_port { return false; } if self.endpoint.addr.is_some() && self.endpoint.addr != Some(ip_repr.dst_addr()) - && !ip_repr.dst_addr().is_broadcast() + && !cx.is_broadcast(&ip_repr.dst_addr()) && !ip_repr.dst_addr().is_multicast() { return false; From 2741503a556e128e38ee1305d98f43fe21941ee0 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Thu, 22 Jun 2023 18:14:23 +0200 Subject: [PATCH 555/566] iface: fix outdated docs on Interface::new --- src/iface/interface/mod.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 04c8d96a8..81e723f2c 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -451,16 +451,6 @@ enum IgmpReportState { impl Interface { /// Create a network interface using the previously provided configuration. - /// - /// # Panics - /// If a required option is not provided, this function will panic. Required - /// options are: - /// - /// - [ethernet_addr] - /// - [neighbor_cache] - /// - /// [ethernet_addr]: #method.ethernet_addr - /// [neighbor_cache]: #method.neighbor_cache pub fn new(config: Config, device: &mut D, now: Instant) -> Self where D: Device + ?Sized, From fd6f71f61410ed767d325c844d0581e7a5cea5de Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 23 Jun 2023 10:29:57 +0200 Subject: [PATCH 556/566] rpl: use LinearMap for the ParentSet Signed-off-by: Thibaut Vandervelden --- src/iface/rpl/of0.rs | 26 +++++---- src/iface/rpl/parents.rs | 113 +++++++++++++++++---------------------- 2 files changed, 62 insertions(+), 77 deletions(-) diff --git a/src/iface/rpl/of0.rs b/src/iface/rpl/of0.rs index 4a7714310..99e4d1f36 100644 --- a/src/iface/rpl/of0.rs +++ b/src/iface/rpl/of0.rs @@ -41,7 +41,7 @@ impl ObjectiveFunction for ObjectiveFunction0 { fn preferred_parent(parent_set: &ParentSet) -> Option<&Parent> { let mut pref_parent: Option<&Parent> = None; - for parent in parent_set.parents() { + for (_, parent) in parent_set.parents() { if pref_parent.is_none() || parent.rank() < pref_parent.unwrap().rank() { pref_parent = Some(parent); } @@ -98,29 +98,27 @@ mod tests { let mut parents = ParentSet::default(); - parents.add(Parent::new( + parents.add( Ipv6Address::default(), - 0, - Rank::ROOT, - Default::default(), - Ipv6Address::default(), - )); + Parent::new(0, Rank::ROOT, Default::default(), Ipv6Address::default()), + ); let mut address = Ipv6Address::default(); address.0[15] = 1; - parents.add(Parent::new( + parents.add( address, - 0, - Rank::new(1024, DEFAULT_MIN_HOP_RANK_INCREASE), - Default::default(), - Ipv6Address::default(), - )); + Parent::new( + 0, + Rank::new(1024, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + Ipv6Address::default(), + ), + ); assert_eq!( ObjectiveFunction0::preferred_parent(&parents), Some(&Parent::new( - Ipv6Address::default(), 0, Rank::ROOT, Default::default(), diff --git a/src/iface/rpl/parents.rs b/src/iface/rpl/parents.rs index 724008d5b..70d5a5e88 100644 --- a/src/iface/rpl/parents.rs +++ b/src/iface/rpl/parents.rs @@ -6,7 +6,6 @@ use crate::config::RPL_PARENTS_BUFFER_COUNT; #[derive(Debug, Clone, Copy, PartialEq)] pub(crate) struct Parent { rank: Rank, - address: Ipv6Address, preference: u8, version_number: SequenceCounter, dodag_id: Ipv6Address, @@ -15,7 +14,6 @@ pub(crate) struct Parent { impl Parent { /// Create a new parent. pub(crate) fn new( - address: Ipv6Address, preference: u8, rank: Rank, version_number: SequenceCounter, @@ -23,7 +21,6 @@ impl Parent { ) -> Self { Self { rank, - address, preference, version_number, dodag_id, @@ -38,57 +35,47 @@ impl Parent { #[derive(Debug, Default)] pub(crate) struct ParentSet { - parents: heapless::Vec, + parents: heapless::LinearMap, } impl ParentSet { /// Add a new parent to the parent set. The Rank of the new parent should be lower than the /// Rank of the node that holds this parent set. - pub(crate) fn add(&mut self, parent: Parent) { - if let Some(p) = self.find_mut(parent.address) { - // Update information + pub(crate) fn add(&mut self, address: Ipv6Address, parent: Parent) { + if let Some(p) = self.parents.get_mut(&address) { *p = parent; - } else if let Err(p) = self.parents.push(parent) { - // Look for the worst parent - if let Some(worst) = self.worst_parent() { - if worst.rank().dag_rank() > parent.rank().dag_rank() { - *worst = parent; + } else if let Err(p) = self.parents.insert(address, parent) { + if let Some((w_a, w_p)) = self.worst_parent() { + if w_p.rank.dag_rank() > parent.rank.dag_rank() { + self.parents.remove(&w_a.clone()).unwrap(); + self.parents.insert(address, parent).unwrap(); } else { - net_debug!("could not add parent"); + net_debug!("could not add {} to parent set, buffer is full", address); } } else { - // WARNING: there should be a worst parent, since the list of parents is not empty - unreachable!(); + unreachable!() } } } /// Find a parent based on its address. - pub(crate) fn find(&self, address: Ipv6Address) -> Option<&Parent> { - self.parents.iter().find(|p| p.address == address) + pub(crate) fn find(&self, address: &Ipv6Address) -> Option<&Parent> { + self.parents.get(address) } /// Find a mutable parent based on its address. - pub(crate) fn find_mut(&mut self, address: Ipv6Address) -> Option<&mut Parent> { - self.parents.iter_mut().find(|p| p.address == address) + pub(crate) fn find_mut(&mut self, address: &Ipv6Address) -> Option<&mut Parent> { + self.parents.get_mut(address) } /// Return a slice to the parent set. - pub(crate) fn parents(&self) -> &[Parent] { - &self.parents + pub(crate) fn parents(&self) -> impl Iterator { + self.parents.iter() } /// Find the worst parent that is currently in the parent set. - fn worst_parent(&mut self) -> Option<&mut Parent> { - let mut worst: Option<&mut Parent> = None; - - for p in self.parents.iter_mut() { - if worst.is_none() || worst.as_mut().unwrap().rank.dag_rank() < p.rank.dag_rank() { - worst = Some(p); - } - } - - worst + fn worst_parent(&self) -> Option<(&Ipv6Address, &Parent)> { + self.parents.iter().max_by_key(|(k, v)| v.rank.dag_rank()) } } @@ -99,18 +86,14 @@ mod tests { #[test] fn add_parent() { let mut set = ParentSet::default(); - set.add(Parent::new( - Default::default(), - 0, - Rank::ROOT, + set.add( Default::default(), - Default::default(), - )); + Parent::new(0, Rank::ROOT, Default::default(), Default::default()), + ); assert_eq!( - set.find(Default::default()), + set.find(&Default::default()), Some(&Parent::new( - Default::default(), 0, Rank::ROOT, Default::default(), @@ -131,18 +114,19 @@ mod tests { address.0[15] = i as u8; last_address = address; - set.add(Parent::new( - address, - 0, - Rank::new(256 * i, DEFAULT_MIN_HOP_RANK_INCREASE), - Default::default(), + set.add( address, - )); + Parent::new( + 0, + Rank::new(256 * i, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + ), + ); assert_eq!( - set.find(address), + set.find(&address), Some(&Parent::new( - address, 0, Rank::new(256 * i, DEFAULT_MIN_HOP_RANK_INCREASE), Default::default(), @@ -155,35 +139,38 @@ mod tests { // set. let mut address = Ipv6Address::default(); address.0[15] = 8; - set.add(Parent::new( - address, - 0, - Rank::new(256 * 8, DEFAULT_MIN_HOP_RANK_INCREASE), - Default::default(), + set.add( address, - )); - assert_eq!(set.find(address), None); + Parent::new( + 0, + Rank::new(256 * 8, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + ), + ); + assert_eq!(set.find(&address), None); /// This Parent has a better rank than the last one in the set. let mut address = Ipv6Address::default(); address.0[15] = 9; - set.add(Parent::new( + set.add( address, - 0, - Rank::new(0, DEFAULT_MIN_HOP_RANK_INCREASE), - Default::default(), - address, - )); + Parent::new( + 0, + Rank::new(0, DEFAULT_MIN_HOP_RANK_INCREASE), + Default::default(), + address, + ), + ); assert_eq!( - set.find(address), + set.find(&address), Some(&Parent::new( - address, 0, Rank::new(0, DEFAULT_MIN_HOP_RANK_INCREASE), Default::default(), address )) ); - assert_eq!(set.find(last_address), None); + assert_eq!(set.find(&last_address), None); } } From 97afe68e386a3c63168a958cddbc15d3663826a9 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Fri, 23 Jun 2023 12:37:24 +0200 Subject: [PATCH 557/566] iface: doc panic if hardware addr doesn't match --- src/iface/interface/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 81e723f2c..17cde6f26 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -451,12 +451,15 @@ enum IgmpReportState { impl Interface { /// Create a network interface using the previously provided configuration. + /// + /// # Panics + /// This function panics if the [`Config::hardware_address`] does not match + /// the medium of the device. pub fn new(config: Config, device: &mut D, now: Instant) -> Self where D: Device + ?Sized, { let caps = device.capabilities(); - assert_eq!( config.hardware_addr.medium(), caps.medium, From 78b4f39a10d63a21d83ba5dd7365d17eb3308be3 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Sat, 24 Jun 2023 20:47:43 +0200 Subject: [PATCH 558/566] Device-level packet metadata identifiers --- Cargo.toml | 4 +- examples/server.rs | 2 +- examples/sixlowpan.rs | 4 +- src/iface/interface/ethernet.rs | 5 +- src/iface/interface/ieee802154.rs | 5 +- src/iface/interface/ipv4.rs | 2 + src/iface/interface/ipv6.rs | 17 ++- src/iface/interface/mod.rs | 91 ++++++++---- src/iface/interface/sixlowpan.rs | 17 ++- src/iface/interface/tests.rs | 133 ++++++++++++----- src/phy/fault_injector.rs | 17 ++- src/phy/fuzz_injector.rs | 8 + src/phy/mod.rs | 46 ++++++ src/phy/pcap_writer.rs | 8 + src/phy/tracer.rs | 8 + src/socket/udp.rs | 234 +++++++++++++++++++++++++----- 16 files changed, 480 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9a4b8a73..ac46d43ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,8 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "socket-dns" = ["socket", "proto-dns"] "socket-mdns" = ["socket-dns"] +"packet-id" = [] + "async" = [] default = [ @@ -72,7 +74,7 @@ default = [ "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns", "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns", - "async" + "packet-id", "async" ] # Private features diff --git a/examples/server.rs b/examples/server.rs index 33d95c5d5..35b2e93a6 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -111,7 +111,7 @@ fn main() { }; if let Some((endpoint, data)) = client { debug!("udp:6969 send data: {:?} to {}", data, endpoint,); - socket.send_slice(&data, endpoint).unwrap(); + socket.send_slice(&data, endpoint.endpoint()).unwrap(); } // tcp:6969: respond "hello" diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 9d474e3fe..d76cc24a0 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -135,7 +135,9 @@ fn main() { "udp:6969 send data: {:?}", str::from_utf8(&buffer[..len]).unwrap() ); - socket.send_slice(&buffer[..len], endpoint).unwrap(); + socket + .send_slice(&buffer[..len], endpoint.endpoint()) + .unwrap(); } let socket = sockets.get_mut::(tcp_handle); diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index bf8496fe5..570630069 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -14,6 +14,7 @@ impl InterfaceInner { pub(super) fn process_ethernet<'frame, T: AsRef<[u8]>>( &mut self, sockets: &mut SocketSet, + packet_id: crate::phy::PacketId, frame: &'frame T, fragments: &'frame mut FragmentsBuffer, ) -> Option> { @@ -34,13 +35,13 @@ impl InterfaceInner { EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - self.process_ipv4(sockets, &ipv4_packet, fragments) + self.process_ipv4(sockets, packet_id, &ipv4_packet, fragments) .map(EthernetPacket::Ip) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload())); - self.process_ipv6(sockets, &ipv6_packet) + self.process_ipv6(sockets, packet_id, &ipv6_packet) .map(EthernetPacket::Ip) } // Drop all other traffic. diff --git a/src/iface/interface/ieee802154.rs b/src/iface/interface/ieee802154.rs index 78023001b..925ce12f4 100644 --- a/src/iface/interface/ieee802154.rs +++ b/src/iface/interface/ieee802154.rs @@ -7,6 +7,7 @@ impl InterfaceInner { pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, sixlowpan_payload: &'payload T, _fragments: &'output mut FragmentsBuffer, ) -> Option> { @@ -32,7 +33,9 @@ impl InterfaceInner { } match ieee802154_frame.payload() { - Some(payload) => self.process_sixlowpan(sockets, &ieee802154_repr, payload, _fragments), + Some(payload) => { + self.process_sixlowpan(sockets, packet_id, &ieee802154_repr, payload, _fragments) + } None => None, } } diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index e1cbbc60e..aae999425 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -14,6 +14,7 @@ impl InterfaceInner { pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ipv4_packet: &Ipv4Packet<&'a T>, frag: &'a mut FragmentsBuffer, ) -> Option> { @@ -138,6 +139,7 @@ impl InterfaceInner { self.process_udp( sockets, + packet_id, ip_repr, udp_repr, handled_by_raw_socket, diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index c5e9b3a38..38f5d7a40 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -8,6 +8,7 @@ use super::SocketSet; use crate::socket::icmp; use crate::socket::AnySocket; +use crate::phy::PacketId; use crate::wire::*; impl InterfaceInner { @@ -15,6 +16,7 @@ impl InterfaceInner { pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ipv6_packet: &Ipv6Packet<&'frame T>, ) -> Option> { let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet)); @@ -34,6 +36,7 @@ impl InterfaceInner { self.process_nxt_hdr( sockets, + packet_id, ipv6_repr, ipv6_repr.next_header, handled_by_raw_socket, @@ -47,6 +50,7 @@ impl InterfaceInner { pub(super) fn process_nxt_hdr<'frame>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, handled_by_raw_socket: bool, @@ -67,6 +71,7 @@ impl InterfaceInner { self.process_udp( sockets, + packet_id, ipv6_repr.into(), udp_repr, handled_by_raw_socket, @@ -78,9 +83,13 @@ impl InterfaceInner { #[cfg(feature = "socket-tcp")] IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), - IpProtocol::HopByHop => { - self.process_hopbyhop(sockets, ipv6_repr, handled_by_raw_socket, ip_payload) - } + IpProtocol::HopByHop => self.process_hopbyhop( + sockets, + packet_id, + ipv6_repr, + handled_by_raw_socket, + ip_payload, + ), #[cfg(feature = "socket-raw")] _ if handled_by_raw_socket => None, @@ -240,6 +249,7 @@ impl InterfaceInner { pub(super) fn process_hopbyhop<'frame>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, ip_payload: &'frame [u8], @@ -272,6 +282,7 @@ impl InterfaceInner { } self.process_nxt_hdr( sockets, + packet_id, ipv6_repr, hbh_repr.next_header, handled_by_raw_socket, diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 17cde6f26..e4da374ab 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -37,6 +37,7 @@ use crate::config::{ IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT, }; use crate::iface::Routes; +use crate::phy::PacketId; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::rand::Rand; #[cfg(feature = "socket-dns")] @@ -320,7 +321,7 @@ pub(crate) enum IpPacket<'a> { #[cfg(feature = "socket-raw")] Raw((IpRepr, &'a [u8])), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - Udp((IpRepr, UdpRepr, &'a [u8])), + Udp((IpRepr, UdpRepr, &'a [u8], PacketId)), #[cfg(feature = "socket-tcp")] Tcp((IpRepr, TcpRepr<'a>)), #[cfg(feature = "socket-dhcpv4")] @@ -339,7 +340,7 @@ impl<'a> IpPacket<'a> { #[cfg(feature = "socket-raw")] IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), + IpPacket::Udp((ip_repr, _, _, _)) => ip_repr.clone(), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), #[cfg(feature = "socket-dhcpv4")] @@ -347,6 +348,14 @@ impl<'a> IpPacket<'a> { } } + pub(crate) fn packet_id(&self) -> PacketId { + match self { + #[cfg(feature = "socket-udp")] + IpPacket::Udp((_, _, _, packet_id)) => *packet_id, + _ => PacketId::empty(), + } + } + pub(crate) fn emit_payload( &self, _ip_repr: &IpRepr, @@ -372,14 +381,16 @@ impl<'a> IpPacket<'a> { #[cfg(feature = "socket-raw")] IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit( - &mut UdpPacket::new_unchecked(payload), - &_ip_repr.src_addr(), - &_ip_repr.dst_addr(), - inner_payload.len(), - |buf| buf.copy_from_slice(inner_payload), - &caps.checksum, - ), + IpPacket::Udp((_, udp_repr, inner_payload, _)) => { + udp_repr.emit( + &mut UdpPacket::new_unchecked(payload), + &_ip_repr.src_addr(), + &_ip_repr.dst_addr(), + inner_payload.len(), + |buf| buf.copy_from_slice(inner_payload), + &caps.checksum, + ); + } #[cfg(feature = "socket-tcp")] IpPacket::Tcp((_, mut tcp_repr)) => { // This is a terrible hack to make TCP performance more acceptable on systems @@ -416,7 +427,7 @@ impl<'a> IpPacket<'a> { |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(), &caps.checksum, ), - } + }; } } @@ -803,14 +814,17 @@ impl Interface { let mut processed_any = false; while let Some((rx_token, tx_token)) = device.receive(self.inner.now) { + let rx_packet_id = rx_token.packet_id(); rx_token.consume(|frame| { match self.inner.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { - if let Some(packet) = - self.inner - .process_ethernet(sockets, &frame, &mut self.fragments) - { + if let Some(packet) = self.inner.process_ethernet( + sockets, + rx_packet_id, + &frame, + &mut self.fragments, + ) { if let Err(err) = self.inner.dispatch(tx_token, packet, &mut self.fragmenter) { @@ -820,9 +834,12 @@ impl Interface { } #[cfg(feature = "medium-ip")] Medium::Ip => { - if let Some(packet) = - self.inner.process_ip(sockets, &frame, &mut self.fragments) - { + if let Some(packet) = self.inner.process_ip( + sockets, + rx_packet_id, + &frame, + &mut self.fragments, + ) { if let Err(err) = self.inner .dispatch_ip(tx_token, packet, &mut self.fragmenter) @@ -833,10 +850,12 @@ impl Interface { } #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => { - if let Some(packet) = - self.inner - .process_ieee802154(sockets, &frame, &mut self.fragments) - { + if let Some(packet) = self.inner.process_ieee802154( + sockets, + rx_packet_id, + &frame, + &mut self.fragments, + ) { if let Err(err) = self.inner .dispatch_ip(tx_token, packet, &mut self.fragmenter) @@ -923,9 +942,11 @@ impl Interface { respond(inner, IpPacket::Dhcpv4(response)) }), #[cfg(feature = "socket-dns")] - Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) - }), + Socket::Dns(socket) => { + socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| { + respond(inner, IpPacket::Udp((ip, udp, payload, PacketId::empty()))) + }) + } }; match result { @@ -1263,6 +1284,7 @@ impl InterfaceInner { fn process_ip<'frame, T: AsRef<[u8]>>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ip_payload: &'frame T, frag: &'frame mut FragmentsBuffer, ) -> Option> { @@ -1271,12 +1293,12 @@ impl InterfaceInner { Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - self.process_ipv4(sockets, &ipv4_packet, frag) + self.process_ipv4(sockets, packet_id, &ipv4_packet, frag) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload)); - self.process_ipv6(sockets, &ipv6_packet) + self.process_ipv6(sockets, packet_id, &ipv6_packet) } // Drop all other traffic. _ => None, @@ -1341,9 +1363,11 @@ impl InterfaceInner { } #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] + #[allow(clippy::too_many_arguments)] fn process_udp<'frame>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ip_repr: IpRepr, udp_repr: UdpRepr, handled_by_raw_socket: bool, @@ -1356,7 +1380,7 @@ impl InterfaceInner { .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) { if udp_socket.accepts(self, &ip_repr, &udp_repr) { - udp_socket.process(self, &ip_repr, &udp_repr, udp_payload); + udp_socket.process(self, packet_id, &ip_repr, &udp_repr, udp_payload); return None; } } @@ -1643,7 +1667,9 @@ impl InterfaceInner { fn dispatch_ip( &mut self, - tx_token: Tx, + // NOTE(unused_mut): tx_token isn't always mutated, depending on + // the feature set that is used. + #[allow(unused_mut)] mut tx_token: Tx, packet: IpPacket, frag: &mut Fragmenter, ) -> Result<(), DispatchError> { @@ -1684,7 +1710,7 @@ impl InterfaceInner { // If the medium is Ethernet, then we need to retrieve the destination hardware address. #[cfg(feature = "medium-ethernet")] - let (dst_hardware_addr, tx_token) = match self.caps.medium { + let (dst_hardware_addr, mut tx_token) = match self.caps.medium { Medium::Ethernet => { match self.lookup_hardware_addr( tx_token, @@ -1723,7 +1749,7 @@ impl InterfaceInner { repr.emit(&mut tx_buffer, &self.caps.checksum); let payload = &mut tx_buffer[repr.header_len()..]; - packet.emit_payload(repr, payload, &caps); + packet.emit_payload(repr, payload, &caps) }; let total_ip_len = ip_repr.buffer_len(); @@ -1771,6 +1797,7 @@ impl InterfaceInner { // Emit the IP header to the buffer. emit_ip(&ip_repr, &mut frag.buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]); frag.ipv4.ident = ipv4_id; ipv4_packet.set_ident(ipv4_id); @@ -1807,6 +1834,8 @@ impl InterfaceInner { Ok(()) } } else { + tx_token.set_packet_id(packet.packet_id()); + // No fragmentation is required. tx_token.consume(total_len, |mut tx_buffer| { #[cfg(feature = "medium-ethernet")] diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index b288b9956..235ae49a5 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -11,6 +11,7 @@ impl InterfaceInner { pub(super) fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, + packet_id: PacketId, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, f: &'output mut FragmentsBuffer, @@ -47,7 +48,11 @@ impl InterfaceInner { } }; - self.process_ipv6(sockets, &check!(Ipv6Packet::new_checked(payload))) + self.process_ipv6( + sockets, + packet_id, + &check!(Ipv6Packet::new_checked(payload)), + ) } #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -227,7 +232,7 @@ impl InterfaceInner { pub(super) fn dispatch_sixlowpan( &mut self, - tx_token: Tx, + mut tx_token: Tx, packet: IpPacket, ieee_repr: Ieee802154Repr, frag: &mut Fragmenter, @@ -275,7 +280,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { + IpPacket::Udp((_, udpv6_repr, payload, _)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); _compressed_headers_len += udp_repr.header_len(); _uncompressed_headers_len += udpv6_repr.header_len(); @@ -328,7 +333,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { + IpPacket::Udp((_, udpv6_repr, payload, _)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( &mut b[..udp_repr.header_len() + payload.len()], @@ -429,6 +434,8 @@ impl InterfaceInner { return; } } else { + tx_token.set_packet_id(packet.packet_id()); + // We don't need fragmentation, so we emit everything to the TX token. tx_token.consume(total_size + ieee_len, |mut tx_buf| { let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]); @@ -442,7 +449,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload)) => { + IpPacket::Udp((_, udpv6_repr, payload, _)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( &mut tx_buf[..udp_repr.header_len() + payload.len()], diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 1133d97cb..de44cdb73 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -166,9 +166,12 @@ fn test_no_icmp_no_unicast_ipv4() { // broadcast address assert_eq!( - iface - .inner - .process_ipv4(&mut sockets, &frame, &mut iface.fragments), + iface.inner.process_ipv4( + &mut sockets, + PacketId::empty(), + &frame, + &mut iface.fragments + ), None ); } @@ -198,7 +201,12 @@ fn test_no_icmp_no_unicast_ipv6() { // 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 sockets, &frame), None); + assert_eq!( + iface + .inner + .process_ipv6(&mut sockets, PacketId::empty(), &frame), + None + ); } #[test] @@ -249,9 +257,12 @@ fn test_icmp_error_no_payload() { // And we correctly handle no payload. assert_eq!( - iface - .inner - .process_ipv4(&mut sockets, &frame, &mut iface.fragments), + iface.inner.process_ipv4( + &mut sockets, + PacketId::empty(), + &frame, + &mut iface.fragments + ), Some(expected_repr) ); } @@ -385,9 +396,15 @@ fn test_icmp_error_port_unreachable() { // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( - iface - .inner - .process_udp(&mut sockets, ip_repr, udp_repr, false, &UDP_PAYLOAD, data), + iface.inner.process_udp( + &mut sockets, + PacketId::empty(), + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + data + ), Some(expected_repr) ); @@ -415,6 +432,7 @@ fn test_icmp_error_port_unreachable() { assert_eq!( iface.inner.process_udp( &mut sockets, + PacketId::empty(), ip_repr, udp_repr, false, @@ -428,7 +446,7 @@ fn test_icmp_error_port_unreachable() { #[test] #[cfg(feature = "socket-udp")] fn test_handle_udp_broadcast() { - use crate::wire::IpEndpoint; + use crate::{socket::udp::UdpMetadata, wire::IpEndpoint}; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; @@ -490,6 +508,7 @@ fn test_handle_udp_broadcast() { assert_eq!( iface.inner.process_udp( &mut sockets, + PacketId::empty(), ip_repr, udp_repr, false, @@ -505,7 +524,10 @@ fn test_handle_udp_broadcast() { assert!(socket.can_recv()); assert_eq!( socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67))) + Ok(( + &UDP_PAYLOAD[..], + UdpMetadata::new(IpEndpoint::new(src_ip.into(), 67), PacketId::empty(),) + )) ); } @@ -566,9 +588,12 @@ fn test_handle_ipv4_broadcast() { let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); assert_eq!( - iface - .inner - .process_ipv4(&mut sockets, &frame, &mut iface.fragments), + iface.inner.process_ipv4( + &mut sockets, + PacketId::empty(), + &frame, + &mut iface.fragments + ), Some(expected_packet) ); } @@ -680,6 +705,7 @@ fn test_icmp_reply_size() { assert_eq!( iface.inner.process_udp( &mut sockets, + PacketId::empty(), ip_repr.into(), udp_repr, false, @@ -692,6 +718,7 @@ fn test_icmp_reply_size() { assert_eq!( iface.inner.process_udp( &mut sockets, + PacketId::empty(), ip_repr.into(), udp_repr, false, @@ -731,9 +758,12 @@ fn test_handle_valid_arp_request() { // Ensure an ARP Request for us triggers an ARP Reply assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + iface.inner.process_ethernet( + &mut sockets, + PacketId::empty(), + frame.into_inner(), + &mut iface.fragments + ), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -807,9 +837,12 @@ fn test_handle_valid_ndisc_request() { // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + iface.inner.process_ethernet( + &mut sockets, + PacketId::empty(), + frame.into_inner(), + &mut iface.fragments + ), Some(EthernetPacket::Ip(IpPacket::Icmpv6(( ipv6_expected, icmpv6_expected @@ -855,9 +888,12 @@ fn test_handle_other_arp_request() { // Ensure an ARP Request for someone else does not trigger an ARP Reply assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + iface.inner.process_ethernet( + &mut sockets, + PacketId::empty(), + frame.into_inner(), + &mut iface.fragments + ), None ); @@ -908,9 +944,12 @@ fn test_arp_flush_after_update_ip() { // Ensure an ARP Request for us triggers an ARP Reply assert_eq!( - iface - .inner - .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments), + iface.inner.process_ethernet( + &mut sockets, + PacketId::empty(), + frame.into_inner(), + &mut iface.fragments + ), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, @@ -1103,7 +1142,9 @@ fn test_icmpv6_nxthdr_unknown() { // 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 sockets, &frame), + iface + .inner + .process_ipv6(&mut sockets, PacketId::empty(), &frame), Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) ); } @@ -1267,9 +1308,12 @@ fn test_raw_socket_no_reply() { }; assert_eq!( - iface - .inner - .process_ipv4(&mut sockets, &frame, &mut iface.fragments), + iface.inner.process_ipv4( + &mut sockets, + PacketId::empty(), + &frame, + &mut iface.fragments + ), None ); } @@ -1277,7 +1321,10 @@ fn test_raw_socket_no_reply() { #[test] #[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] fn test_raw_socket_with_udp_socket() { - use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + use crate::{ + socket::udp::UdpMetadata, + wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}, + }; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; @@ -1353,9 +1400,12 @@ fn test_raw_socket_with_udp_socket() { }; assert_eq!( - iface - .inner - .process_ipv4(&mut sockets, &frame, &mut iface.fragments), + iface.inner.process_ipv4( + &mut sockets, + PacketId::empty(), + &frame, + &mut iface.fragments + ), None ); @@ -1364,7 +1414,10 @@ fn test_raw_socket_with_udp_socket() { assert!(socket.can_recv()); assert_eq!( socket.recv(), - Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67))) + Ok(( + &UDP_PAYLOAD[..], + UdpMetadata::new(IpEndpoint::new(src_addr.into(), 67), PacketId::empty(),) + )) ); } @@ -1458,6 +1511,7 @@ fn test_echo_request_sixlowpan_128_bytes() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, + PacketId::empty(), &ieee802154_repr, &request_first_part_packet.into_inner(), &mut iface.fragments @@ -1482,6 +1536,7 @@ fn test_echo_request_sixlowpan_128_bytes() { let result = iface.inner.process_sixlowpan( &mut sockets, + PacketId::empty(), &ieee802154_repr, &request_second_part, &mut iface.fragments, @@ -1612,6 +1667,7 @@ fn test_sixlowpan_udp_with_fragmentation() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, + PacketId::empty(), &ieee802154_repr, udp_first_part, &mut iface.fragments @@ -1631,6 +1687,7 @@ fn test_sixlowpan_udp_with_fragmentation() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, + PacketId::empty(), &ieee802154_repr, udp_second_part, &mut iface.fragments @@ -1642,8 +1699,9 @@ fn test_sixlowpan_udp_with_fragmentation() { let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec."; + assert_eq!( - socket.recv(), + socket.recv().map(|(data, meta)| (data, meta.endpoint())), Ok(( &udp_data[..], IpEndpoint { @@ -1673,6 +1731,7 @@ fn test_sixlowpan_udp_with_fragmentation() { dst_port: 1234, }, udp_data, + PacketId::empty(), )), &mut iface.fragmenter, ); diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 64d217148..acc084a54 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -1,6 +1,8 @@ use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::{Duration, Instant}; +use super::PacketId; + // We use our own RNG to stay compatible with #![no_std]. // The use of the RNG below has a slight bias, but it doesn't matter. fn xorshift32(state: &mut u32) -> u32 { @@ -211,6 +213,7 @@ impl Device for FaultInjector { fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let (rx_token, tx_token) = self.inner.receive(timestamp)?; + let rx_packet_id = as phy::RxToken>::packet_id(&rx_token); let len = super::RxToken::consume(rx_token, |buffer| { if (self.config.max_size > 0 && buffer.len() > self.config.max_size) @@ -240,7 +243,10 @@ impl Device for FaultInjector { self.state.corrupt(&mut buf[..]); } - let rx = RxToken { buf }; + let rx = RxToken { + buf, + packet_id: rx_packet_id, + }; let tx = TxToken { state: &mut self.state, config: self.config, @@ -265,6 +271,7 @@ impl Device for FaultInjector { #[doc(hidden)] pub struct RxToken<'a> { buf: &'a mut [u8], + packet_id: PacketId, } impl<'a> phy::RxToken for RxToken<'a> { @@ -274,6 +281,10 @@ impl<'a> phy::RxToken for RxToken<'a> { { f(self.buf) } + + fn packet_id(&self) -> phy::PacketId { + self.packet_id + } } #[doc(hidden)] @@ -315,4 +326,8 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { f(buf) }) } + + fn set_packet_id(&mut self, packet_id: PacketId) { + self.token.set_packet_id(packet_id); + } } diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index a2a95405b..624dc3711 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -99,6 +99,10 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { f(buffer) }) } + + fn packet_id(&self) -> phy::PacketId { + self.token.packet_id() + } } #[doc(hidden)] @@ -118,4 +122,8 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { result }) } + + fn set_packet_id(&mut self, packet_id: phy::PacketId) { + self.token.set_packet_id(packet_id) + } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 91cef8b1a..40378321b 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -130,6 +130,43 @@ pub use self::tracer::Tracer; ))] pub use self::tuntap_interface::TunTapInterface; +/// An ID that can be used to uniquely identify a packet to a [`Device`], +/// sent or received by that same [`Device`] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub struct PacketId { + #[cfg(feature = "packet-id")] + id: Option, +} + +impl PacketId { + pub fn empty() -> Self { + #[cfg(feature = "packet-id")] + { + Self { id: None } + } + #[cfg(not(feature = "packet-id"))] + { + Self {} + } + } +} + +#[cfg(feature = "packet-id")] +impl PacketId { + pub fn id(&self) -> Option { + self.id + } + + /// Create a new packet ID. + /// + /// A caller of this function should know the context in which + /// this ID is relevant. + pub fn new(id: u32) -> Self { + Self { id: Some(id) } + } +} + /// A description of checksum behavior for a particular protocol. #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -339,6 +376,11 @@ pub trait RxToken { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R; + + /// The Packet ID associated with the frame received by this [`RxToken`] + fn packet_id(&self) -> PacketId { + PacketId::empty() + } } /// A token to transmit a single network packet. @@ -352,4 +394,8 @@ pub trait TxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R; + + /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`]. + #[allow(unused_variables)] + fn set_packet_id(&mut self, packet_id: PacketId) {} } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index 662ec41bd..e008d4598 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -231,6 +231,10 @@ impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> { f(buffer) }) } + + fn packet_id(&self) -> phy::PacketId { + self.token.packet_id() + } } #[doc(hidden)] @@ -257,4 +261,8 @@ impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> { result }) } + + fn set_packet_id(&mut self, packet_id: phy::PacketId) { + self.token.set_packet_id(packet_id) + } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 9e877753c..d5510251b 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -108,6 +108,10 @@ impl phy::RxToken for RxToken { f(buffer) }) } + + fn packet_id(&self) -> phy::PacketId { + self.token.packet_id() + } } #[doc(hidden)] @@ -136,6 +140,10 @@ impl phy::TxToken for TxToken { result }) } + + fn set_packet_id(&mut self, packet_id: phy::PacketId) { + self.token.set_packet_id(packet_id) + } } pub struct Packet<'a> { diff --git a/src/socket/udp.rs b/src/socket/udp.rs index cdf6e7f33..2a9cd1145 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -3,17 +3,59 @@ use core::cmp::min; use core::task::Waker; use crate::iface::Context; +use crate::phy::PacketId; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; use crate::storage::Empty; use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct UdpMetadata { + endpoint: IpEndpoint, + packet_id: PacketId, +} + +impl UdpMetadata { + /// The endpoint of this metadata + pub fn endpoint(&self) -> IpEndpoint { + self.endpoint + } + + /// The packet ID of this metadata + pub fn packet_id(self) -> PacketId { + self.packet_id + } + + /// Create a new metadata instance. + /// + /// If `packet_id` is `Some`, it can be used to track a datagram + /// as it is handled by the networking stack, or other elements of `smoltcp` + /// that interact with the specific datagram. + pub(crate) fn new(endpoint: IpEndpoint, packet_id: PacketId) -> Self { + Self { + endpoint, + packet_id, + } + } +} + +impl core::fmt::Display for UdpMetadata { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "packet-id")] + return write!(f, "{}, PacketID: {:?}", self.endpoint, self.packet_id); + + #[cfg(not(feature = "packet-id"))] + write!(f, "{}", self.endpoint) + } +} + /// A UDP packet metadata. -pub type PacketMetadata = crate::storage::PacketMetadata; +pub type PacketMetadata = crate::storage::PacketMetadata; /// A UDP packet ring buffer. -pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpEndpoint>; +pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, UdpMetadata>; /// Error returned by [`Socket::bind`] #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -281,7 +323,7 @@ impl<'a> Socket<'a> { let payload_buf = self .tx_buffer - .enqueue(size, remote_endpoint) + .enqueue(size, UdpMetadata::new(remote_endpoint, PacketId::empty())) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -293,6 +335,44 @@ impl<'a> Socket<'a> { Ok(payload_buf) } + /// Send a packet, but marked, so that possible metadata about the packet + /// may be retrieved from the sending device + #[cfg(feature = "packet-id")] + pub fn send_marked( + &mut self, + size: usize, + remote_endpoint: IpEndpoint, + packet_id: u32, + ) -> Result<&mut [u8], SendError> { + if self.endpoint.port == 0 { + return Err(SendError::Unaddressable); + } + if remote_endpoint.addr.is_unspecified() { + return Err(SendError::Unaddressable); + } + if remote_endpoint.port == 0 { + return Err(SendError::Unaddressable); + } + + let payload_buf = self + .tx_buffer + .enqueue( + size, + UdpMetadata::new(remote_endpoint, PacketId::new(packet_id)), + ) + .map_err(|_| SendError::BufferFull)?; + + net_trace!( + "udp:{}:{}: buffer to send {} octets (marked with ID {})", + self.endpoint, + remote_endpoint, + size, + packet_id + ); + + Ok(payload_buf) + } + /// Enqueue a packet to be send to a given remote endpoint and pass the buffer /// to the provided closure. The closure then returns the size of the data written /// into the buffer. @@ -319,7 +399,11 @@ impl<'a> Socket<'a> { let size = self .tx_buffer - .enqueue_with_infallible(max_size, remote_endpoint, f) + .enqueue_with_infallible( + max_size, + UdpMetadata::new(remote_endpoint, PacketId::empty()), + f, + ) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -348,14 +432,14 @@ impl<'a> Socket<'a> { /// as a pointer to the payload. /// /// This function returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint), RecvError> { + pub fn recv(&mut self) -> Result<(&[u8], UdpMetadata), RecvError> { let (remote_endpoint, payload_buf) = self.rx_buffer.dequeue().map_err(|_| RecvError::Exhausted)?; net_trace!( "udp:{}:{}: receive {} buffered octets", self.endpoint, - remote_endpoint, + remote_endpoint.endpoint, payload_buf.len() ); Ok((payload_buf, remote_endpoint)) @@ -365,7 +449,7 @@ impl<'a> Socket<'a> { /// and return the amount of octets copied as well as the endpoint. /// /// See also [recv](#method.recv). - pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpEndpoint), RecvError> { + pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, UdpMetadata), RecvError> { let (buffer, endpoint) = self.recv().map_err(|_| RecvError::Exhausted)?; let length = min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); @@ -377,14 +461,14 @@ impl<'a> Socket<'a> { /// This function otherwise behaves identically to [recv](#method.recv). /// /// It returns `Err(Error::Exhausted)` if the receive buffer is empty. - pub fn peek(&mut self) -> Result<(&[u8], &IpEndpoint), RecvError> { + pub fn peek(&mut self) -> Result<(&[u8], &UdpMetadata), RecvError> { let endpoint = self.endpoint; self.rx_buffer.peek().map_err(|_| RecvError::Exhausted).map( |(remote_endpoint, payload_buf)| { net_trace!( "udp:{}:{}: peek {} buffered octets", endpoint, - remote_endpoint, + remote_endpoint.endpoint, payload_buf.len() ); (payload_buf, remote_endpoint) @@ -398,7 +482,7 @@ impl<'a> Socket<'a> { /// This function otherwise behaves identically to [recv_slice](#method.recv_slice). /// /// See also [peek](#method.peek). - pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &IpEndpoint), RecvError> { + pub fn peek_slice(&mut self, data: &mut [u8]) -> Result<(usize, &UdpMetadata), RecvError> { let (buffer, endpoint) = self.peek()?; let length = min(data.len(), buffer.len()); data[..length].copy_from_slice(&buffer[..length]); @@ -423,6 +507,7 @@ impl<'a> Socket<'a> { pub(crate) fn process( &mut self, cx: &mut Context, + packet_id: PacketId, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8], @@ -443,7 +528,12 @@ impl<'a> Socket<'a> { size ); - match self.rx_buffer.enqueue(size, remote_endpoint) { + let metadata = UdpMetadata { + endpoint: remote_endpoint, + packet_id, + }; + + match self.rx_buffer.enqueue(size, metadata) { Ok(buf) => buf.copy_from_slice(payload), Err(_) => net_trace!( "udp:{}:{}: buffer full, dropped incoming packet", @@ -458,21 +548,21 @@ impl<'a> Socket<'a> { pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketId)) -> Result<(), E>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); - let res = self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| { + let res = self.tx_buffer.dequeue_with(|packet_meta, payload_buf| { let src_addr = match endpoint.addr { Some(addr) => addr, - None => match cx.get_source_address(remote_endpoint.addr) { + None => match cx.get_source_address(packet_meta.endpoint.addr) { Some(addr) => addr, None => { net_trace!( "udp:{}:{}: cannot find suitable source address, dropping.", endpoint, - remote_endpoint + packet_meta.endpoint ); return Ok(()); } @@ -482,22 +572,23 @@ impl<'a> Socket<'a> { net_trace!( "udp:{}:{}: sending {} octets", endpoint, - remote_endpoint, + packet_meta.endpoint, payload_buf.len() ); let repr = UdpRepr { src_port: endpoint.port, - dst_port: remote_endpoint.port, + dst_port: packet_meta.endpoint.port, }; let ip_repr = IpRepr::new( src_addr, - remote_endpoint.addr, + packet_meta.endpoint.addr, IpProtocol::Udp, repr.header_len() + payload_buf.len(), hop_limit, ); - emit(cx, (ip_repr, repr, payload_buf)) + + emit(cx, (ip_repr, repr, payload_buf, packet_meta.packet_id)) }); match res { Err(Empty) => Ok(()), @@ -525,7 +616,12 @@ mod test { use crate::wire::{IpRepr, UdpRepr}; fn buffer(packets: usize) -> PacketBuffer<'static> { - PacketBuffer::new(vec![PacketMetadata::EMPTY; packets], vec![0; 16 * packets]) + PacketBuffer::new( + (0..packets) + .map(|_| PacketMetadata::EMPTY) + .collect::>(), + vec![0; 16 * packets], + ) } fn socket( @@ -682,7 +778,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -693,7 +789,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -715,13 +811,31 @@ mod test { assert_eq!(socket.recv(), Err(RecvError::Exhausted)); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); + socket.process( + &mut cx, + PacketId::empty(), + &REMOTE_IP_REPR, + &REMOTE_UDP_REPR, + PAYLOAD, + ); assert!(socket.can_recv()); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); + socket.process( + &mut cx, + PacketId::empty(), + &REMOTE_IP_REPR, + &REMOTE_UDP_REPR, + PAYLOAD, + ); - assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); + assert_eq!( + socket.recv(), + Ok(( + &b"abcdef"[..], + UdpMetadata::new(REMOTE_END, PacketId::empty()) + )) + ); assert!(!socket.can_recv()); } @@ -734,9 +848,27 @@ mod test { assert_eq!(socket.peek(), Err(RecvError::Exhausted)); - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); - assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END))); - assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END))); + socket.process( + &mut cx, + PacketId::empty(), + &REMOTE_IP_REPR, + &REMOTE_UDP_REPR, + PAYLOAD, + ); + assert_eq!( + socket.peek(), + Ok(( + &b"abcdef"[..], + &UdpMetadata::new(REMOTE_END, PacketId::empty()) + )) + ); + assert_eq!( + socket.recv(), + Ok(( + &b"abcdef"[..], + UdpMetadata::new(REMOTE_END, PacketId::empty()) + )) + ); assert_eq!(socket.peek(), Err(RecvError::Exhausted)); } @@ -748,10 +880,19 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); + socket.process( + &mut cx, + PacketId::empty(), + &REMOTE_IP_REPR, + &REMOTE_UDP_REPR, + PAYLOAD, + ); let mut slice = [0; 4]; - assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END))); + assert_eq!( + socket.recv_slice(&mut slice[..]), + Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty()))) + ); assert_eq!(&slice, b"abcd"); } @@ -762,12 +903,24 @@ mod test { assert_eq!(socket.bind(LOCAL_PORT), Ok(())); - socket.process(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD); + socket.process( + &mut cx, + PacketId::empty(), + &REMOTE_IP_REPR, + &REMOTE_UDP_REPR, + PAYLOAD, + ); let mut slice = [0; 4]; - assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END))); + assert_eq!( + socket.peek_slice(&mut slice[..]), + Ok((4, &UdpMetadata::new(REMOTE_END, PacketId::empty()))) + ); assert_eq!(&slice, b"abcd"); - assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END))); + assert_eq!( + socket.recv_slice(&mut slice[..]), + Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty()))) + ); assert_eq!(&slice, b"abcd"); assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted)); } @@ -782,7 +935,7 @@ mod test { s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(())); assert_eq!( - s.dispatch(&mut cx, |_, (ip_repr, _, _)| { + s.dispatch(&mut cx, |_, (ip_repr, _, _, _)| { assert_eq!( ip_repr, IpReprIpvX(IpvXRepr { @@ -841,7 +994,8 @@ mod test { #[test] fn test_process_empty_payload() { - let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]); + let meta = Box::leak(Box::new([PacketMetadata::EMPTY])); + let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]); let mut socket = socket(recv_buffer, buffer(0)); let mut cx = Context::mock(); @@ -851,13 +1005,17 @@ mod test { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, }; - socket.process(&mut cx, &REMOTE_IP_REPR, &repr, &[]); - assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END))); + socket.process(&mut cx, PacketId::empty(), &REMOTE_IP_REPR, &repr, &[]); + assert_eq!( + socket.recv(), + Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketId::empty()))) + ); } #[test] fn test_closing() { - let recv_buffer = PacketBuffer::new(vec![PacketMetadata::EMPTY; 1], vec![]); + let meta = Box::leak(Box::new([PacketMetadata::EMPTY])); + let recv_buffer = PacketBuffer::new(&mut meta[..], vec![]); let mut socket = socket(recv_buffer, buffer(0)); assert_eq!(socket.bind(LOCAL_PORT), Ok(())); From 409ad14da9663fb67f5b48bf0ee9775123322660 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Jun 2023 21:00:08 +0200 Subject: [PATCH 559/566] Rename PacketId to PacketMeta, make id field public. --- src/iface/interface/ethernet.rs | 6 +-- src/iface/interface/ieee802154.rs | 4 +- src/iface/interface/ipv4.rs | 4 +- src/iface/interface/ipv6.rs | 24 +++++------ src/iface/interface/mod.rs | 43 +++++++++--------- src/iface/interface/sixlowpan.rs | 10 ++--- src/iface/interface/tests.rs | 46 ++++++++++---------- src/phy/fault_injector.rs | 19 ++++---- src/phy/fuzz_injector.rs | 8 ++-- src/phy/mod.rs | 41 +++--------------- src/phy/pcap_writer.rs | 8 ++-- src/phy/tracer.rs | 8 ++-- src/socket/udp.rs | 72 +++++++++++++++---------------- 13 files changed, 128 insertions(+), 165 deletions(-) diff --git a/src/iface/interface/ethernet.rs b/src/iface/interface/ethernet.rs index 570630069..c8ec3566d 100644 --- a/src/iface/interface/ethernet.rs +++ b/src/iface/interface/ethernet.rs @@ -14,7 +14,7 @@ impl InterfaceInner { pub(super) fn process_ethernet<'frame, T: AsRef<[u8]>>( &mut self, sockets: &mut SocketSet, - packet_id: crate::phy::PacketId, + meta: crate::phy::PacketMeta, frame: &'frame T, fragments: &'frame mut FragmentsBuffer, ) -> Option> { @@ -35,13 +35,13 @@ impl InterfaceInner { EthernetProtocol::Ipv4 => { let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); - self.process_ipv4(sockets, packet_id, &ipv4_packet, fragments) + self.process_ipv4(sockets, meta, &ipv4_packet, fragments) .map(EthernetPacket::Ip) } #[cfg(feature = "proto-ipv6")] EthernetProtocol::Ipv6 => { let ipv6_packet = check!(Ipv6Packet::new_checked(eth_frame.payload())); - self.process_ipv6(sockets, packet_id, &ipv6_packet) + self.process_ipv6(sockets, meta, &ipv6_packet) .map(EthernetPacket::Ip) } // Drop all other traffic. diff --git a/src/iface/interface/ieee802154.rs b/src/iface/interface/ieee802154.rs index 925ce12f4..842494fd4 100644 --- a/src/iface/interface/ieee802154.rs +++ b/src/iface/interface/ieee802154.rs @@ -7,7 +7,7 @@ impl InterfaceInner { pub(super) fn process_ieee802154<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, sixlowpan_payload: &'payload T, _fragments: &'output mut FragmentsBuffer, ) -> Option> { @@ -34,7 +34,7 @@ impl InterfaceInner { match ieee802154_frame.payload() { Some(payload) => { - self.process_sixlowpan(sockets, packet_id, &ieee802154_repr, payload, _fragments) + self.process_sixlowpan(sockets, meta, &ieee802154_repr, payload, _fragments) } None => None, } diff --git a/src/iface/interface/ipv4.rs b/src/iface/interface/ipv4.rs index aae999425..dc351c436 100644 --- a/src/iface/interface/ipv4.rs +++ b/src/iface/interface/ipv4.rs @@ -14,7 +14,7 @@ impl InterfaceInner { pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ipv4_packet: &Ipv4Packet<&'a T>, frag: &'a mut FragmentsBuffer, ) -> Option> { @@ -139,7 +139,7 @@ impl InterfaceInner { self.process_udp( sockets, - packet_id, + meta, ip_repr, udp_repr, handled_by_raw_socket, diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index 38f5d7a40..5355316a4 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -8,7 +8,7 @@ use super::SocketSet; use crate::socket::icmp; use crate::socket::AnySocket; -use crate::phy::PacketId; +use crate::phy::PacketMeta; use crate::wire::*; impl InterfaceInner { @@ -16,7 +16,7 @@ impl InterfaceInner { pub(super) fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ipv6_packet: &Ipv6Packet<&'frame T>, ) -> Option> { let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet)); @@ -36,7 +36,7 @@ impl InterfaceInner { self.process_nxt_hdr( sockets, - packet_id, + meta, ipv6_repr, ipv6_repr.next_header, handled_by_raw_socket, @@ -50,7 +50,7 @@ impl InterfaceInner { pub(super) fn process_nxt_hdr<'frame>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ipv6_repr: Ipv6Repr, nxt_hdr: IpProtocol, handled_by_raw_socket: bool, @@ -71,7 +71,7 @@ impl InterfaceInner { self.process_udp( sockets, - packet_id, + meta, ipv6_repr.into(), udp_repr, handled_by_raw_socket, @@ -83,13 +83,9 @@ impl InterfaceInner { #[cfg(feature = "socket-tcp")] IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), - IpProtocol::HopByHop => self.process_hopbyhop( - sockets, - packet_id, - ipv6_repr, - handled_by_raw_socket, - ip_payload, - ), + IpProtocol::HopByHop => { + self.process_hopbyhop(sockets, meta, ipv6_repr, handled_by_raw_socket, ip_payload) + } #[cfg(feature = "socket-raw")] _ if handled_by_raw_socket => None, @@ -249,7 +245,7 @@ impl InterfaceInner { pub(super) fn process_hopbyhop<'frame>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool, ip_payload: &'frame [u8], @@ -282,7 +278,7 @@ impl InterfaceInner { } self.process_nxt_hdr( sockets, - packet_id, + meta, ipv6_repr, hbh_repr.next_header, handled_by_raw_socket, diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index e4da374ab..c65cd67a7 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -37,7 +37,7 @@ use crate::config::{ IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT, }; use crate::iface::Routes; -use crate::phy::PacketId; +use crate::phy::PacketMeta; use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken}; use crate::rand::Rand; #[cfg(feature = "socket-dns")] @@ -321,7 +321,7 @@ pub(crate) enum IpPacket<'a> { #[cfg(feature = "socket-raw")] Raw((IpRepr, &'a [u8])), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - Udp((IpRepr, UdpRepr, &'a [u8], PacketId)), + Udp((IpRepr, UdpRepr, &'a [u8], PacketMeta)), #[cfg(feature = "socket-tcp")] Tcp((IpRepr, TcpRepr<'a>)), #[cfg(feature = "socket-dhcpv4")] @@ -348,11 +348,11 @@ impl<'a> IpPacket<'a> { } } - pub(crate) fn packet_id(&self) -> PacketId { + pub(crate) fn meta(&self) -> PacketMeta { match self { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, _, _, packet_id)) => *packet_id, - _ => PacketId::empty(), + IpPacket::Udp((_, _, _, meta)) => *meta, + _ => PacketMeta::default(), } } @@ -814,14 +814,14 @@ impl Interface { let mut processed_any = false; while let Some((rx_token, tx_token)) = device.receive(self.inner.now) { - let rx_packet_id = rx_token.packet_id(); + let rx_meta = rx_token.meta(); rx_token.consume(|frame| { match self.inner.caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { if let Some(packet) = self.inner.process_ethernet( sockets, - rx_packet_id, + rx_meta, &frame, &mut self.fragments, ) { @@ -834,12 +834,10 @@ impl Interface { } #[cfg(feature = "medium-ip")] Medium::Ip => { - if let Some(packet) = self.inner.process_ip( - sockets, - rx_packet_id, - &frame, - &mut self.fragments, - ) { + if let Some(packet) = + self.inner + .process_ip(sockets, rx_meta, &frame, &mut self.fragments) + { if let Err(err) = self.inner .dispatch_ip(tx_token, packet, &mut self.fragmenter) @@ -852,7 +850,7 @@ impl Interface { Medium::Ieee802154 => { if let Some(packet) = self.inner.process_ieee802154( sockets, - rx_packet_id, + rx_meta, &frame, &mut self.fragments, ) { @@ -944,7 +942,10 @@ impl Interface { #[cfg(feature = "socket-dns")] Socket::Dns(socket) => { socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| { - respond(inner, IpPacket::Udp((ip, udp, payload, PacketId::empty()))) + respond( + inner, + IpPacket::Udp((ip, udp, payload, PacketMeta::default())), + ) }) } }; @@ -1284,7 +1285,7 @@ impl InterfaceInner { fn process_ip<'frame, T: AsRef<[u8]>>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ip_payload: &'frame T, frag: &'frame mut FragmentsBuffer, ) -> Option> { @@ -1293,12 +1294,12 @@ impl InterfaceInner { Ok(IpVersion::Ipv4) => { let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); - self.process_ipv4(sockets, packet_id, &ipv4_packet, frag) + self.process_ipv4(sockets, meta, &ipv4_packet, frag) } #[cfg(feature = "proto-ipv6")] Ok(IpVersion::Ipv6) => { let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload)); - self.process_ipv6(sockets, packet_id, &ipv6_packet) + self.process_ipv6(sockets, meta, &ipv6_packet) } // Drop all other traffic. _ => None, @@ -1367,7 +1368,7 @@ impl InterfaceInner { fn process_udp<'frame>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ip_repr: IpRepr, udp_repr: UdpRepr, handled_by_raw_socket: bool, @@ -1380,7 +1381,7 @@ impl InterfaceInner { .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket)) { if udp_socket.accepts(self, &ip_repr, &udp_repr) { - udp_socket.process(self, packet_id, &ip_repr, &udp_repr, udp_payload); + udp_socket.process(self, meta, &ip_repr, &udp_repr, udp_payload); return None; } } @@ -1834,7 +1835,7 @@ impl InterfaceInner { Ok(()) } } else { - tx_token.set_packet_id(packet.packet_id()); + tx_token.set_meta(packet.meta()); // No fragmentation is required. tx_token.consume(total_len, |mut tx_buffer| { diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 235ae49a5..6f78e148d 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -11,7 +11,7 @@ impl InterfaceInner { pub(super) fn process_sixlowpan<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, - packet_id: PacketId, + meta: PacketMeta, ieee802154_repr: &Ieee802154Repr, payload: &'payload T, f: &'output mut FragmentsBuffer, @@ -48,11 +48,7 @@ impl InterfaceInner { } }; - self.process_ipv6( - sockets, - packet_id, - &check!(Ipv6Packet::new_checked(payload)), - ) + self.process_ipv6(sockets, meta, &check!(Ipv6Packet::new_checked(payload))) } #[cfg(feature = "proto-sixlowpan-fragmentation")] @@ -434,7 +430,7 @@ impl InterfaceInner { return; } } else { - tx_token.set_packet_id(packet.packet_id()); + tx_token.set_meta(packet.meta()); // We don't need fragmentation, so we emit everything to the TX token. tx_token.consume(total_size + ieee_len, |mut tx_buf| { diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index de44cdb73..55e7bc37a 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -168,7 +168,7 @@ fn test_no_icmp_no_unicast_ipv4() { assert_eq!( iface.inner.process_ipv4( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &frame, &mut iface.fragments ), @@ -204,7 +204,7 @@ fn test_no_icmp_no_unicast_ipv6() { assert_eq!( iface .inner - .process_ipv6(&mut sockets, PacketId::empty(), &frame), + .process_ipv6(&mut sockets, PacketMeta::default(), &frame), None ); } @@ -259,7 +259,7 @@ fn test_icmp_error_no_payload() { assert_eq!( iface.inner.process_ipv4( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &frame, &mut iface.fragments ), @@ -398,7 +398,7 @@ fn test_icmp_error_port_unreachable() { assert_eq!( iface.inner.process_udp( &mut sockets, - PacketId::empty(), + PacketMeta::default(), ip_repr, udp_repr, false, @@ -432,7 +432,7 @@ fn test_icmp_error_port_unreachable() { assert_eq!( iface.inner.process_udp( &mut sockets, - PacketId::empty(), + PacketMeta::default(), ip_repr, udp_repr, false, @@ -508,7 +508,7 @@ fn test_handle_udp_broadcast() { assert_eq!( iface.inner.process_udp( &mut sockets, - PacketId::empty(), + PacketMeta::default(), ip_repr, udp_repr, false, @@ -526,7 +526,7 @@ fn test_handle_udp_broadcast() { socket.recv(), Ok(( &UDP_PAYLOAD[..], - UdpMetadata::new(IpEndpoint::new(src_ip.into(), 67), PacketId::empty(),) + UdpMetadata::new(IpEndpoint::new(src_ip.into(), 67), PacketMeta::default(),) )) ); } @@ -590,7 +590,7 @@ fn test_handle_ipv4_broadcast() { assert_eq!( iface.inner.process_ipv4( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &frame, &mut iface.fragments ), @@ -705,7 +705,7 @@ fn test_icmp_reply_size() { assert_eq!( iface.inner.process_udp( &mut sockets, - PacketId::empty(), + PacketMeta::default(), ip_repr.into(), udp_repr, false, @@ -718,7 +718,7 @@ fn test_icmp_reply_size() { assert_eq!( iface.inner.process_udp( &mut sockets, - PacketId::empty(), + PacketMeta::default(), ip_repr.into(), udp_repr, false, @@ -760,7 +760,7 @@ fn test_handle_valid_arp_request() { assert_eq!( iface.inner.process_ethernet( &mut sockets, - PacketId::empty(), + PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), @@ -839,7 +839,7 @@ fn test_handle_valid_ndisc_request() { assert_eq!( iface.inner.process_ethernet( &mut sockets, - PacketId::empty(), + PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), @@ -890,7 +890,7 @@ fn test_handle_other_arp_request() { assert_eq!( iface.inner.process_ethernet( &mut sockets, - PacketId::empty(), + PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), @@ -946,7 +946,7 @@ fn test_arp_flush_after_update_ip() { assert_eq!( iface.inner.process_ethernet( &mut sockets, - PacketId::empty(), + PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), @@ -1144,7 +1144,7 @@ fn test_icmpv6_nxthdr_unknown() { assert_eq!( iface .inner - .process_ipv6(&mut sockets, PacketId::empty(), &frame), + .process_ipv6(&mut sockets, PacketMeta::default(), &frame), Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) ); } @@ -1310,7 +1310,7 @@ fn test_raw_socket_no_reply() { assert_eq!( iface.inner.process_ipv4( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &frame, &mut iface.fragments ), @@ -1402,7 +1402,7 @@ fn test_raw_socket_with_udp_socket() { assert_eq!( iface.inner.process_ipv4( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &frame, &mut iface.fragments ), @@ -1416,7 +1416,7 @@ fn test_raw_socket_with_udp_socket() { socket.recv(), Ok(( &UDP_PAYLOAD[..], - UdpMetadata::new(IpEndpoint::new(src_addr.into(), 67), PacketId::empty(),) + UdpMetadata::new(IpEndpoint::new(src_addr.into(), 67), PacketMeta::default(),) )) ); } @@ -1511,7 +1511,7 @@ fn test_echo_request_sixlowpan_128_bytes() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &ieee802154_repr, &request_first_part_packet.into_inner(), &mut iface.fragments @@ -1536,7 +1536,7 @@ fn test_echo_request_sixlowpan_128_bytes() { let result = iface.inner.process_sixlowpan( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &ieee802154_repr, &request_second_part, &mut iface.fragments, @@ -1667,7 +1667,7 @@ fn test_sixlowpan_udp_with_fragmentation() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &ieee802154_repr, udp_first_part, &mut iface.fragments @@ -1687,7 +1687,7 @@ fn test_sixlowpan_udp_with_fragmentation() { assert_eq!( iface.inner.process_sixlowpan( &mut sockets, - PacketId::empty(), + PacketMeta::default(), &ieee802154_repr, udp_second_part, &mut iface.fragments @@ -1731,7 +1731,7 @@ fn test_sixlowpan_udp_with_fragmentation() { dst_port: 1234, }, udp_data, - PacketId::empty(), + PacketMeta::default(), )), &mut iface.fragmenter, ); diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index acc084a54..fffe11a26 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -1,7 +1,7 @@ use crate::phy::{self, Device, DeviceCapabilities}; use crate::time::{Duration, Instant}; -use super::PacketId; +use super::PacketMeta; // We use our own RNG to stay compatible with #![no_std]. // The use of the RNG below has a slight bias, but it doesn't matter. @@ -213,7 +213,7 @@ impl Device for FaultInjector { fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let (rx_token, tx_token) = self.inner.receive(timestamp)?; - let rx_packet_id = as phy::RxToken>::packet_id(&rx_token); + let rx_meta = as phy::RxToken>::meta(&rx_token); let len = super::RxToken::consume(rx_token, |buffer| { if (self.config.max_size > 0 && buffer.len() > self.config.max_size) @@ -243,10 +243,7 @@ impl Device for FaultInjector { self.state.corrupt(&mut buf[..]); } - let rx = RxToken { - buf, - packet_id: rx_packet_id, - }; + let rx = RxToken { buf, meta: rx_meta }; let tx = TxToken { state: &mut self.state, config: self.config, @@ -271,7 +268,7 @@ impl Device for FaultInjector { #[doc(hidden)] pub struct RxToken<'a> { buf: &'a mut [u8], - packet_id: PacketId, + meta: PacketMeta, } impl<'a> phy::RxToken for RxToken<'a> { @@ -282,8 +279,8 @@ impl<'a> phy::RxToken for RxToken<'a> { f(self.buf) } - fn packet_id(&self) -> phy::PacketId { - self.packet_id + fn meta(&self) -> phy::PacketMeta { + self.meta } } @@ -327,7 +324,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { }) } - fn set_packet_id(&mut self, packet_id: PacketId) { - self.token.set_packet_id(packet_id); + fn set_meta(&mut self, meta: PacketMeta) { + self.token.set_meta(meta); } } diff --git a/src/phy/fuzz_injector.rs b/src/phy/fuzz_injector.rs index 624dc3711..6769d8ec0 100644 --- a/src/phy/fuzz_injector.rs +++ b/src/phy/fuzz_injector.rs @@ -100,8 +100,8 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> { }) } - fn packet_id(&self) -> phy::PacketId { - self.token.packet_id() + fn meta(&self) -> phy::PacketMeta { + self.token.meta() } } @@ -123,7 +123,7 @@ impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> { }) } - fn set_packet_id(&mut self, packet_id: phy::PacketId) { - self.token.set_packet_id(packet_id) + fn set_meta(&mut self, meta: phy::PacketMeta) { + self.token.set_meta(meta) } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 40378321b..b054b2ebb 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -133,38 +133,11 @@ pub use self::tuntap_interface::TunTapInterface; /// An ID that can be used to uniquely identify a packet to a [`Device`], /// sent or received by that same [`Device`] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub struct PacketId { +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] +#[non_exhaustive] +pub struct PacketMeta { #[cfg(feature = "packet-id")] - id: Option, -} - -impl PacketId { - pub fn empty() -> Self { - #[cfg(feature = "packet-id")] - { - Self { id: None } - } - #[cfg(not(feature = "packet-id"))] - { - Self {} - } - } -} - -#[cfg(feature = "packet-id")] -impl PacketId { - pub fn id(&self) -> Option { - self.id - } - - /// Create a new packet ID. - /// - /// A caller of this function should know the context in which - /// this ID is relevant. - pub fn new(id: u32) -> Self { - Self { id: Some(id) } - } + pub id: Option, } /// A description of checksum behavior for a particular protocol. @@ -378,8 +351,8 @@ pub trait RxToken { F: FnOnce(&mut [u8]) -> R; /// The Packet ID associated with the frame received by this [`RxToken`] - fn packet_id(&self) -> PacketId { - PacketId::empty() + fn meta(&self) -> PacketMeta { + PacketMeta::default() } } @@ -397,5 +370,5 @@ pub trait TxToken { /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`]. #[allow(unused_variables)] - fn set_packet_id(&mut self, packet_id: PacketId) {} + fn set_meta(&mut self, meta: PacketMeta) {} } diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index e008d4598..fc6c3b236 100644 --- a/src/phy/pcap_writer.rs +++ b/src/phy/pcap_writer.rs @@ -232,8 +232,8 @@ impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> { }) } - fn packet_id(&self) -> phy::PacketId { - self.token.packet_id() + fn meta(&self) -> phy::PacketMeta { + self.token.meta() } } @@ -262,7 +262,7 @@ impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> { }) } - fn set_packet_id(&mut self, packet_id: phy::PacketId) { - self.token.set_packet_id(packet_id) + fn set_meta(&mut self, meta: phy::PacketMeta) { + self.token.set_meta(meta) } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index d5510251b..48e60ec2b 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -109,8 +109,8 @@ impl phy::RxToken for RxToken { }) } - fn packet_id(&self) -> phy::PacketId { - self.token.packet_id() + fn meta(&self) -> phy::PacketMeta { + self.token.meta() } } @@ -141,8 +141,8 @@ impl phy::TxToken for TxToken { }) } - fn set_packet_id(&mut self, packet_id: phy::PacketId) { - self.token.set_packet_id(packet_id) + fn set_meta(&mut self, meta: phy::PacketMeta) { + self.token.set_meta(meta) } } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 2a9cd1145..644574bcf 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -3,7 +3,7 @@ use core::cmp::min; use core::task::Waker; use crate::iface::Context; -use crate::phy::PacketId; +use crate::phy::PacketMeta; use crate::socket::PollAt; #[cfg(feature = "async")] use crate::socket::WakerRegistration; @@ -14,7 +14,7 @@ use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct UdpMetadata { endpoint: IpEndpoint, - packet_id: PacketId, + meta: PacketMeta, } impl UdpMetadata { @@ -24,27 +24,24 @@ impl UdpMetadata { } /// The packet ID of this metadata - pub fn packet_id(self) -> PacketId { - self.packet_id + pub fn meta(self) -> PacketMeta { + self.meta } /// Create a new metadata instance. /// - /// If `packet_id` is `Some`, it can be used to track a datagram + /// If `meta` is `Some`, it can be used to track a datagram /// as it is handled by the networking stack, or other elements of `smoltcp` /// that interact with the specific datagram. - pub(crate) fn new(endpoint: IpEndpoint, packet_id: PacketId) -> Self { - Self { - endpoint, - packet_id, - } + pub(crate) fn new(endpoint: IpEndpoint, meta: PacketMeta) -> Self { + Self { endpoint, meta } } } impl core::fmt::Display for UdpMetadata { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { #[cfg(feature = "packet-id")] - return write!(f, "{}, PacketID: {:?}", self.endpoint, self.packet_id); + return write!(f, "{}, PacketID: {:?}", self.endpoint, self.meta); #[cfg(not(feature = "packet-id"))] write!(f, "{}", self.endpoint) @@ -323,7 +320,10 @@ impl<'a> Socket<'a> { let payload_buf = self .tx_buffer - .enqueue(size, UdpMetadata::new(remote_endpoint, PacketId::empty())) + .enqueue( + size, + UdpMetadata::new(remote_endpoint, PacketMeta::default()), + ) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -354,12 +354,12 @@ impl<'a> Socket<'a> { return Err(SendError::Unaddressable); } + let mut meta = PacketMeta::default(); + meta.id = Some(packet_id); + let payload_buf = self .tx_buffer - .enqueue( - size, - UdpMetadata::new(remote_endpoint, PacketId::new(packet_id)), - ) + .enqueue(size, UdpMetadata::new(remote_endpoint, meta)) .map_err(|_| SendError::BufferFull)?; net_trace!( @@ -401,7 +401,7 @@ impl<'a> Socket<'a> { .tx_buffer .enqueue_with_infallible( max_size, - UdpMetadata::new(remote_endpoint, PacketId::empty()), + UdpMetadata::new(remote_endpoint, PacketMeta::default()), f, ) .map_err(|_| SendError::BufferFull)?; @@ -507,7 +507,7 @@ impl<'a> Socket<'a> { pub(crate) fn process( &mut self, cx: &mut Context, - packet_id: PacketId, + meta: PacketMeta, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8], @@ -530,7 +530,7 @@ impl<'a> Socket<'a> { let metadata = UdpMetadata { endpoint: remote_endpoint, - packet_id, + meta, }; match self.rx_buffer.enqueue(size, metadata) { @@ -548,7 +548,7 @@ impl<'a> Socket<'a> { pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketId)) -> Result<(), E>, + F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketMeta)) -> Result<(), E>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); @@ -588,7 +588,7 @@ impl<'a> Socket<'a> { hop_limit, ); - emit(cx, (ip_repr, repr, payload_buf, packet_meta.packet_id)) + emit(cx, (ip_repr, repr, payload_buf, packet_meta.meta)) }); match res { Err(Empty) => Ok(()), @@ -778,7 +778,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _meta)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -789,7 +789,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _packet_id)| { + socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _meta)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -813,7 +813,7 @@ mod test { assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); socket.process( &mut cx, - PacketId::empty(), + PacketMeta::default(), &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD, @@ -823,7 +823,7 @@ mod test { assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); socket.process( &mut cx, - PacketId::empty(), + PacketMeta::default(), &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD, @@ -833,7 +833,7 @@ mod test { socket.recv(), Ok(( &b"abcdef"[..], - UdpMetadata::new(REMOTE_END, PacketId::empty()) + UdpMetadata::new(REMOTE_END, PacketMeta::default()) )) ); assert!(!socket.can_recv()); @@ -850,7 +850,7 @@ mod test { socket.process( &mut cx, - PacketId::empty(), + PacketMeta::default(), &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD, @@ -859,14 +859,14 @@ mod test { socket.peek(), Ok(( &b"abcdef"[..], - &UdpMetadata::new(REMOTE_END, PacketId::empty()) + &UdpMetadata::new(REMOTE_END, PacketMeta::default()) )) ); assert_eq!( socket.recv(), Ok(( &b"abcdef"[..], - UdpMetadata::new(REMOTE_END, PacketId::empty()) + UdpMetadata::new(REMOTE_END, PacketMeta::default()) )) ); assert_eq!(socket.peek(), Err(RecvError::Exhausted)); @@ -882,7 +882,7 @@ mod test { assert!(socket.accepts(&mut cx, &REMOTE_IP_REPR, &REMOTE_UDP_REPR)); socket.process( &mut cx, - PacketId::empty(), + PacketMeta::default(), &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD, @@ -891,7 +891,7 @@ mod test { let mut slice = [0; 4]; assert_eq!( socket.recv_slice(&mut slice[..]), - Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty()))) + Ok((4, UdpMetadata::new(REMOTE_END, PacketMeta::default()))) ); assert_eq!(&slice, b"abcd"); } @@ -905,7 +905,7 @@ mod test { socket.process( &mut cx, - PacketId::empty(), + PacketMeta::default(), &REMOTE_IP_REPR, &REMOTE_UDP_REPR, PAYLOAD, @@ -914,12 +914,12 @@ mod test { let mut slice = [0; 4]; assert_eq!( socket.peek_slice(&mut slice[..]), - Ok((4, &UdpMetadata::new(REMOTE_END, PacketId::empty()))) + Ok((4, &UdpMetadata::new(REMOTE_END, PacketMeta::default()))) ); assert_eq!(&slice, b"abcd"); assert_eq!( socket.recv_slice(&mut slice[..]), - Ok((4, UdpMetadata::new(REMOTE_END, PacketId::empty()))) + Ok((4, UdpMetadata::new(REMOTE_END, PacketMeta::default()))) ); assert_eq!(&slice, b"abcd"); assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted)); @@ -1005,10 +1005,10 @@ mod test { src_port: REMOTE_PORT, dst_port: LOCAL_PORT, }; - socket.process(&mut cx, PacketId::empty(), &REMOTE_IP_REPR, &repr, &[]); + socket.process(&mut cx, PacketMeta::default(), &REMOTE_IP_REPR, &repr, &[]); assert_eq!( socket.recv(), - Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketId::empty()))) + Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketMeta::default()))) ); } From 2e40ed0224c7cf97f241098cb17eec917fb8ee66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 24 Jun 2023 21:09:27 +0200 Subject: [PATCH 560/566] udp: use UdpMetadata for send/recv fns, add Into impl so you can still pass an IpEndpoint as before. --- examples/server.rs | 2 +- examples/sixlowpan.rs | 4 +- src/iface/interface/tests.rs | 12 +++- src/socket/udp.rs | 136 ++++++++--------------------------- 4 files changed, 41 insertions(+), 113 deletions(-) diff --git a/examples/server.rs b/examples/server.rs index 35b2e93a6..33d95c5d5 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -111,7 +111,7 @@ fn main() { }; if let Some((endpoint, data)) = client { debug!("udp:6969 send data: {:?} to {}", data, endpoint,); - socket.send_slice(&data, endpoint.endpoint()).unwrap(); + socket.send_slice(&data, endpoint).unwrap(); } // tcp:6969: respond "hello" diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index d76cc24a0..9d474e3fe 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -135,9 +135,7 @@ fn main() { "udp:6969 send data: {:?}", str::from_utf8(&buffer[..len]).unwrap() ); - socket - .send_slice(&buffer[..len], endpoint.endpoint()) - .unwrap(); + socket.send_slice(&buffer[..len], endpoint).unwrap(); } let socket = sockets.get_mut::(tcp_handle); diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 55e7bc37a..003411cde 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -526,7 +526,10 @@ fn test_handle_udp_broadcast() { socket.recv(), Ok(( &UDP_PAYLOAD[..], - UdpMetadata::new(IpEndpoint::new(src_ip.into(), 67), PacketMeta::default(),) + UdpMetadata { + endpoint: IpEndpoint::new(src_ip.into(), 67), + meta: PacketMeta::default() + } )) ); } @@ -1416,7 +1419,10 @@ fn test_raw_socket_with_udp_socket() { socket.recv(), Ok(( &UDP_PAYLOAD[..], - UdpMetadata::new(IpEndpoint::new(src_addr.into(), 67), PacketMeta::default(),) + UdpMetadata { + endpoint: IpEndpoint::new(src_addr.into(), 67), + meta: PacketMeta::default() + } )) ); } @@ -1701,7 +1707,7 @@ fn test_sixlowpan_udp_with_fragmentation() { In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec."; assert_eq!( - socket.recv().map(|(data, meta)| (data, meta.endpoint())), + socket.recv().map(|(data, meta)| (data, meta.endpoint)), Ok(( &udp_data[..], IpEndpoint { diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 644574bcf..fb15fff43 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -10,31 +10,20 @@ use crate::socket::WakerRegistration; use crate::storage::Empty; use crate::wire::{IpEndpoint, IpListenEndpoint, IpProtocol, IpRepr, UdpRepr}; +/// Metadata for a sent or received UDP packet. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct UdpMetadata { - endpoint: IpEndpoint, - meta: PacketMeta, + pub endpoint: IpEndpoint, + pub meta: PacketMeta, } -impl UdpMetadata { - /// The endpoint of this metadata - pub fn endpoint(&self) -> IpEndpoint { - self.endpoint - } - - /// The packet ID of this metadata - pub fn meta(self) -> PacketMeta { - self.meta - } - - /// Create a new metadata instance. - /// - /// If `meta` is `Some`, it can be used to track a datagram - /// as it is handled by the networking stack, or other elements of `smoltcp` - /// that interact with the specific datagram. - pub(crate) fn new(endpoint: IpEndpoint, meta: PacketMeta) -> Self { - Self { endpoint, meta } +impl> From for UdpMetadata { + fn from(value: T) -> Self { + Self { + endpoint: value.into(), + meta: PacketMeta::default(), + } } } @@ -306,73 +295,33 @@ impl<'a> Socket<'a> { pub fn send( &mut self, size: usize, - remote_endpoint: IpEndpoint, + meta: impl Into, ) -> Result<&mut [u8], SendError> { + let meta = meta.into(); if self.endpoint.port == 0 { return Err(SendError::Unaddressable); } - if remote_endpoint.addr.is_unspecified() { + if meta.endpoint.addr.is_unspecified() { return Err(SendError::Unaddressable); } - if remote_endpoint.port == 0 { + if meta.endpoint.port == 0 { return Err(SendError::Unaddressable); } let payload_buf = self .tx_buffer - .enqueue( - size, - UdpMetadata::new(remote_endpoint, PacketMeta::default()), - ) + .enqueue(size, meta) .map_err(|_| SendError::BufferFull)?; net_trace!( "udp:{}:{}: buffer to send {} octets", self.endpoint, - remote_endpoint, + meta.endpoint, size ); Ok(payload_buf) } - /// Send a packet, but marked, so that possible metadata about the packet - /// may be retrieved from the sending device - #[cfg(feature = "packet-id")] - pub fn send_marked( - &mut self, - size: usize, - remote_endpoint: IpEndpoint, - packet_id: u32, - ) -> Result<&mut [u8], SendError> { - if self.endpoint.port == 0 { - return Err(SendError::Unaddressable); - } - if remote_endpoint.addr.is_unspecified() { - return Err(SendError::Unaddressable); - } - if remote_endpoint.port == 0 { - return Err(SendError::Unaddressable); - } - - let mut meta = PacketMeta::default(); - meta.id = Some(packet_id); - - let payload_buf = self - .tx_buffer - .enqueue(size, UdpMetadata::new(remote_endpoint, meta)) - .map_err(|_| SendError::BufferFull)?; - - net_trace!( - "udp:{}:{}: buffer to send {} octets (marked with ID {})", - self.endpoint, - remote_endpoint, - size, - packet_id - ); - - Ok(payload_buf) - } - /// Enqueue a packet to be send to a given remote endpoint and pass the buffer /// to the provided closure. The closure then returns the size of the data written /// into the buffer. @@ -381,35 +330,32 @@ impl<'a> Socket<'a> { pub fn send_with( &mut self, max_size: usize, - remote_endpoint: IpEndpoint, + meta: impl Into, f: F, ) -> Result where F: FnOnce(&mut [u8]) -> usize, { + let meta = meta.into(); if self.endpoint.port == 0 { return Err(SendError::Unaddressable); } - if remote_endpoint.addr.is_unspecified() { + if meta.endpoint.addr.is_unspecified() { return Err(SendError::Unaddressable); } - if remote_endpoint.port == 0 { + if meta.endpoint.port == 0 { return Err(SendError::Unaddressable); } let size = self .tx_buffer - .enqueue_with_infallible( - max_size, - UdpMetadata::new(remote_endpoint, PacketMeta::default()), - f, - ) + .enqueue_with_infallible(max_size, meta, f) .map_err(|_| SendError::BufferFull)?; net_trace!( "udp:{}:{}: buffer to send {} octets", self.endpoint, - remote_endpoint, + meta.endpoint, size ); Ok(size) @@ -421,10 +367,9 @@ impl<'a> Socket<'a> { pub fn send_slice( &mut self, data: &[u8], - remote_endpoint: IpEndpoint, + meta: impl Into, ) -> Result<(), SendError> { - self.send(data.len(), remote_endpoint)? - .copy_from_slice(data); + self.send(data.len(), meta)?.copy_from_slice(data); Ok(()) } @@ -829,13 +774,7 @@ mod test { PAYLOAD, ); - assert_eq!( - socket.recv(), - Ok(( - &b"abcdef"[..], - UdpMetadata::new(REMOTE_END, PacketMeta::default()) - )) - ); + assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END.into()))); assert!(!socket.can_recv()); } @@ -855,20 +794,8 @@ mod test { &REMOTE_UDP_REPR, PAYLOAD, ); - assert_eq!( - socket.peek(), - Ok(( - &b"abcdef"[..], - &UdpMetadata::new(REMOTE_END, PacketMeta::default()) - )) - ); - assert_eq!( - socket.recv(), - Ok(( - &b"abcdef"[..], - UdpMetadata::new(REMOTE_END, PacketMeta::default()) - )) - ); + assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END.into(),))); + assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END.into(),))); assert_eq!(socket.peek(), Err(RecvError::Exhausted)); } @@ -891,7 +818,7 @@ mod test { let mut slice = [0; 4]; assert_eq!( socket.recv_slice(&mut slice[..]), - Ok((4, UdpMetadata::new(REMOTE_END, PacketMeta::default()))) + Ok((4, REMOTE_END.into())) ); assert_eq!(&slice, b"abcd"); } @@ -914,12 +841,12 @@ mod test { let mut slice = [0; 4]; assert_eq!( socket.peek_slice(&mut slice[..]), - Ok((4, &UdpMetadata::new(REMOTE_END, PacketMeta::default()))) + Ok((4, &REMOTE_END.into())) ); assert_eq!(&slice, b"abcd"); assert_eq!( socket.recv_slice(&mut slice[..]), - Ok((4, UdpMetadata::new(REMOTE_END, PacketMeta::default()))) + Ok((4, REMOTE_END.into())) ); assert_eq!(&slice, b"abcd"); assert_eq!(socket.peek_slice(&mut slice[..]), Err(RecvError::Exhausted)); @@ -1006,10 +933,7 @@ mod test { dst_port: LOCAL_PORT, }; socket.process(&mut cx, PacketMeta::default(), &REMOTE_IP_REPR, &repr, &[]); - assert_eq!( - socket.recv(), - Ok((&[][..], UdpMetadata::new(REMOTE_END, PacketMeta::default()))) - ); + assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END.into()))); } #[test] From efa7897987857968ee300e0069b372de6c8ee039 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 25 Jun 2023 23:52:16 +0200 Subject: [PATCH 561/566] Rename feature `packet-id` -> `packetmeta-id`. --- Cargo.toml | 4 ++-- src/phy/mod.rs | 2 +- src/socket/udp.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac46d43ce..51116eb67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ defmt = [ "dep:defmt", "heapless/defmt", "heapless/defmt-impl" ] "socket-dns" = ["socket", "proto-dns"] "socket-mdns" = ["socket-dns"] -"packet-id" = [] +"packetmeta-id" = [] "async" = [] @@ -74,7 +74,7 @@ default = [ "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-dns", "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns", - "packet-id", "async" + "packetmeta-id", "async" ] # Private features diff --git a/src/phy/mod.rs b/src/phy/mod.rs index b054b2ebb..a4ee59d2c 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -136,7 +136,7 @@ pub use self::tuntap_interface::TunTapInterface; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] #[non_exhaustive] pub struct PacketMeta { - #[cfg(feature = "packet-id")] + #[cfg(feature = "packetmeta-id")] pub id: Option, } diff --git a/src/socket/udp.rs b/src/socket/udp.rs index fb15fff43..12b9435eb 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -29,10 +29,10 @@ impl> From for UdpMetadata { impl core::fmt::Display for UdpMetadata { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - #[cfg(feature = "packet-id")] + #[cfg(feature = "packetmeta-id")] return write!(f, "{}, PacketID: {:?}", self.endpoint, self.meta); - #[cfg(not(feature = "packet-id"))] + #[cfg(not(feature = "packetmeta-id"))] write!(f, "{}", self.endpoint) } } From 942ec6d2cce7c9540f84031f0f100d2016fe438d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 00:02:56 +0200 Subject: [PATCH 562/566] Add PacketMeta docs. --- src/phy/mod.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index a4ee59d2c..ee438b54a 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -130,8 +130,29 @@ pub use self::tracer::Tracer; ))] pub use self::tuntap_interface::TunTapInterface; -/// An ID that can be used to uniquely identify a packet to a [`Device`], -/// sent or received by that same [`Device`] +/// Metadata associated to a packet. +/// +/// The packet metadata is a set of attributes associated to network packets +/// as they travel up or down the stack. The metadata is get/set by the +/// [`Device`] implementations or by the user when sending/receiving packets from a +/// socket. +/// +/// Metadata fields are enabled via Cargo features. If no field is enabled, this +/// struct becomes zero-sized, which allows the compiler to optimize it out as if +/// the packet metadata mechanism didn't exist at all. +/// +/// Currently only UDP sockets allow setting/retrieving packet metadata. The metadata +/// for packets emitted with other sockets will be all default values. +/// +/// This struct is marked as `#[non_exhaustive]`. This means it is not possible to +/// create it directly by specifying all fields. You have to instead create it with +/// default values and then set the fields you want. This makes adding metadata +/// fields a non-breaking change. +/// +/// ```rust,ignore +/// let mut meta = PacketMeta::new(); +/// meta.id = 15; +/// ``` #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] #[non_exhaustive] From 2b0ad1a2781aafd57d59e64cad27f62cf302e172 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 00:03:46 +0200 Subject: [PATCH 563/566] Don't use option in PacketMeta.id. --- src/phy/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phy/mod.rs b/src/phy/mod.rs index ee438b54a..677e78b47 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -158,7 +158,7 @@ pub use self::tuntap_interface::TunTapInterface; #[non_exhaustive] pub struct PacketMeta { #[cfg(feature = "packetmeta-id")] - pub id: Option, + pub id: u32, } /// A description of checksum behavior for a particular protocol. From 3a8f133a2102bdbc352eb9b0967ae29ea8c4eca7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 00:37:59 +0200 Subject: [PATCH 564/566] Pass PacketMeta separately, not within IpPacket. --- src/iface/interface/ieee802154.rs | 3 +- src/iface/interface/igmp.rs | 15 ++++-- src/iface/interface/mod.rs | 80 +++++++++++++++++-------------- src/iface/interface/sixlowpan.rs | 9 ++-- src/iface/interface/tests.rs | 3 +- src/socket/udp.rs | 12 ++--- 6 files changed, 68 insertions(+), 54 deletions(-) diff --git a/src/iface/interface/ieee802154.rs b/src/iface/interface/ieee802154.rs index 842494fd4..3e3fdf076 100644 --- a/src/iface/interface/ieee802154.rs +++ b/src/iface/interface/ieee802154.rs @@ -44,6 +44,7 @@ impl InterfaceInner { &mut self, ll_dst_a: Ieee802154Address, tx_token: Tx, + meta: PacketMeta, packet: IpPacket, frag: &mut Fragmenter, ) { @@ -64,7 +65,7 @@ impl InterfaceInner { src_addr: Some(ll_src_a), }; - self.dispatch_sixlowpan(tx_token, packet, ieee_repr, frag); + self.dispatch_sixlowpan(tx_token, meta, packet, ieee_repr, frag); } #[cfg(feature = "proto-sixlowpan-fragmentation")] diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index a2ce2c1ec..9bf6ad946 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -1,5 +1,5 @@ use super::{check, IgmpReportState, Interface, InterfaceInner, IpPacket}; -use crate::phy::Device; +use crate::phy::{Device, PacketMeta}; use crate::time::{Duration, Instant}; use crate::wire::*; @@ -65,7 +65,7 @@ impl Interface { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. self.inner - .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .dispatch_ip(tx_token, PacketMeta::default(), pkt, &mut self.fragmenter) .unwrap(); Ok(true) @@ -107,7 +107,7 @@ impl Interface { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. self.inner - .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .dispatch_ip(tx_token, PacketMeta::default(), pkt, &mut self.fragmenter) .unwrap(); Ok(true) @@ -143,7 +143,7 @@ impl Interface { if let Some(tx_token) = device.transmit(self.inner.now) { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. self.inner - .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .dispatch_ip(tx_token, PacketMeta::default(), pkt, &mut self.fragmenter) .unwrap(); } else { return false; @@ -173,7 +173,12 @@ impl Interface { if let Some(tx_token) = device.transmit(self.inner.now) { // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery. self.inner - .dispatch_ip(tx_token, pkt, &mut self.fragmenter) + .dispatch_ip( + tx_token, + PacketMeta::default(), + pkt, + &mut self.fragmenter, + ) .unwrap(); } else { return false; diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index c65cd67a7..6f0600875 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -321,7 +321,7 @@ pub(crate) enum IpPacket<'a> { #[cfg(feature = "socket-raw")] Raw((IpRepr, &'a [u8])), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - Udp((IpRepr, UdpRepr, &'a [u8], PacketMeta)), + Udp((IpRepr, UdpRepr, &'a [u8])), #[cfg(feature = "socket-tcp")] Tcp((IpRepr, TcpRepr<'a>)), #[cfg(feature = "socket-dhcpv4")] @@ -340,7 +340,7 @@ impl<'a> IpPacket<'a> { #[cfg(feature = "socket-raw")] IpPacket::Raw((ip_repr, _)) => ip_repr.clone(), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((ip_repr, _, _, _)) => ip_repr.clone(), + IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(), #[cfg(feature = "socket-tcp")] IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(), #[cfg(feature = "socket-dhcpv4")] @@ -348,14 +348,6 @@ impl<'a> IpPacket<'a> { } } - pub(crate) fn meta(&self) -> PacketMeta { - match self { - #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, _, _, meta)) => *meta, - _ => PacketMeta::default(), - } - } - pub(crate) fn emit_payload( &self, _ip_repr: &IpRepr, @@ -381,7 +373,7 @@ impl<'a> IpPacket<'a> { #[cfg(feature = "socket-raw")] IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), #[cfg(any(feature = "socket-udp", feature = "socket-dns"))] - IpPacket::Udp((_, udp_repr, inner_payload, _)) => { + IpPacket::Udp((_, udp_repr, inner_payload)) => { udp_repr.emit( &mut UdpPacket::new_unchecked(payload), &_ip_repr.src_addr(), @@ -838,10 +830,12 @@ impl Interface { self.inner .process_ip(sockets, rx_meta, &frame, &mut self.fragments) { - if let Err(err) = - self.inner - .dispatch_ip(tx_token, packet, &mut self.fragmenter) - { + if let Err(err) = self.inner.dispatch_ip( + tx_token, + PacketMeta::default(), + packet, + &mut self.fragmenter, + ) { net_debug!("Failed to send response: {:?}", err); } } @@ -854,10 +848,12 @@ impl Interface { &frame, &mut self.fragments, ) { - if let Err(err) = - self.inner - .dispatch_ip(tx_token, packet, &mut self.fragmenter) - { + if let Err(err) = self.inner.dispatch_ip( + tx_token, + PacketMeta::default(), + packet, + &mut self.fragmenter, + ) { net_debug!("Failed to send response: {:?}", err); } } @@ -891,7 +887,7 @@ impl Interface { } let mut neighbor_addr = None; - let mut respond = |inner: &mut InterfaceInner, response: IpPacket| { + let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: IpPacket| { neighbor_addr = Some(response.ip_repr().dst_addr()); let t = device.transmit(inner.now).ok_or_else(|| { net_debug!("failed to transmit IP: device exhausted"); @@ -899,7 +895,7 @@ impl Interface { })?; inner - .dispatch_ip(t, response, &mut self.fragmenter) + .dispatch_ip(t, meta, response, &mut self.fragmenter) .map_err(EgressError::Dispatch)?; emitted_any = true; @@ -910,41 +906,46 @@ impl Interface { let result = match &mut item.socket { #[cfg(feature = "socket-raw")] Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Raw(response)) + respond(inner, PacketMeta::default(), IpPacket::Raw(response)) }), #[cfg(feature = "socket-icmp")] Socket::Icmp(socket) => { socket.dispatch(&mut self.inner, |inner, response| match response { #[cfg(feature = "proto-ipv4")] - (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => { - respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr))) - } + (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond( + inner, + PacketMeta::default(), + IpPacket::Icmpv4((ipv4_repr, icmpv4_repr)), + ), #[cfg(feature = "proto-ipv6")] - (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => { - respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr))) - } + (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond( + inner, + PacketMeta::default(), + IpPacket::Icmpv6((ipv6_repr, icmpv6_repr)), + ), #[allow(unreachable_patterns)] _ => unreachable!(), }) } #[cfg(feature = "socket-udp")] - Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Udp(response)) + Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, meta, response| { + respond(inner, meta, IpPacket::Udp(response)) }), #[cfg(feature = "socket-tcp")] Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Tcp(response)) + respond(inner, PacketMeta::default(), IpPacket::Tcp(response)) }), #[cfg(feature = "socket-dhcpv4")] Socket::Dhcpv4(socket) => socket.dispatch(&mut self.inner, |inner, response| { - respond(inner, IpPacket::Dhcpv4(response)) + respond(inner, PacketMeta::default(), IpPacket::Dhcpv4(response)) }), #[cfg(feature = "socket-dns")] Socket::Dns(socket) => { socket.dispatch(&mut self.inner, |inner, (ip, udp, payload)| { respond( inner, - IpPacket::Udp((ip, udp, payload, PacketMeta::default())), + PacketMeta::default(), + IpPacket::Udp((ip, udp, payload)), ) }) } @@ -1492,7 +1493,9 @@ impl InterfaceInner { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, frag), + EthernetPacket::Ip(packet) => { + self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag) + } } } @@ -1646,7 +1649,9 @@ impl InterfaceInner { solicit, )); - if let Err(e) = self.dispatch_ip(tx_token, packet, fragmenter) { + if let Err(e) = + self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter) + { net_debug!("Failed to dispatch NDISC solicit: {:?}", e); return Err(DispatchError::NeighborPending); } @@ -1671,6 +1676,7 @@ impl InterfaceInner { // NOTE(unused_mut): tx_token isn't always mutated, depending on // the feature set that is used. #[allow(unused_mut)] mut tx_token: Tx, + meta: PacketMeta, packet: IpPacket, frag: &mut Fragmenter, ) -> Result<(), DispatchError> { @@ -1689,7 +1695,7 @@ impl InterfaceInner { )?; let addr = addr.ieee802154_or_panic(); - self.dispatch_ieee802154(addr, tx_token, packet, frag); + self.dispatch_ieee802154(addr, tx_token, meta, packet, frag); return Ok(()); } @@ -1835,7 +1841,7 @@ impl InterfaceInner { Ok(()) } } else { - tx_token.set_meta(packet.meta()); + tx_token.set_meta(meta); // No fragmentation is required. tx_token.consume(total_len, |mut tx_buffer| { diff --git a/src/iface/interface/sixlowpan.rs b/src/iface/interface/sixlowpan.rs index 6f78e148d..838866911 100644 --- a/src/iface/interface/sixlowpan.rs +++ b/src/iface/interface/sixlowpan.rs @@ -229,6 +229,7 @@ impl InterfaceInner { pub(super) fn dispatch_sixlowpan( &mut self, mut tx_token: Tx, + meta: PacketMeta, packet: IpPacket, ieee_repr: Ieee802154Repr, frag: &mut Fragmenter, @@ -276,7 +277,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload, _)) => { + IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); _compressed_headers_len += udp_repr.header_len(); _uncompressed_headers_len += udpv6_repr.header_len(); @@ -329,7 +330,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload, _)) => { + IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( &mut b[..udp_repr.header_len() + payload.len()], @@ -430,7 +431,7 @@ impl InterfaceInner { return; } } else { - tx_token.set_meta(packet.meta()); + tx_token.set_meta(meta); // We don't need fragmentation, so we emit everything to the TX token. tx_token.consume(total_size + ieee_len, |mut tx_buf| { @@ -445,7 +446,7 @@ impl InterfaceInner { match packet { #[cfg(feature = "socket-udp")] - IpPacket::Udp((_, udpv6_repr, payload, _)) => { + IpPacket::Udp((_, udpv6_repr, payload)) => { let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); let mut udp_packet = SixlowpanUdpNhcPacket::new_unchecked( &mut tx_buf[..udp_repr.header_len() + payload.len()], diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs index 003411cde..88431f4f3 100644 --- a/src/iface/interface/tests.rs +++ b/src/iface/interface/tests.rs @@ -1584,6 +1584,7 @@ fn test_echo_request_sixlowpan_128_bytes() { iface.inner.dispatch_ieee802154( Ieee802154Address::default(), tx_token, + PacketMeta::default(), result.unwrap(), &mut iface.fragmenter, ); @@ -1724,6 +1725,7 @@ fn test_sixlowpan_udp_with_fragmentation() { iface.inner.dispatch_ieee802154( Ieee802154Address::default(), tx_token, + PacketMeta::default(), IpPacket::Udp(( IpRepr::Ipv6(Ipv6Repr { src_addr: Ipv6Address::default(), @@ -1737,7 +1739,6 @@ fn test_sixlowpan_udp_with_fragmentation() { dst_port: 1234, }, udp_data, - PacketMeta::default(), )), &mut iface.fragmenter, ); diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 12b9435eb..39172dc8e 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -493,7 +493,7 @@ impl<'a> Socket<'a> { pub(crate) fn dispatch(&mut self, cx: &mut Context, emit: F) -> Result<(), E> where - F: FnOnce(&mut Context, (IpRepr, UdpRepr, &[u8], PacketMeta)) -> Result<(), E>, + F: FnOnce(&mut Context, PacketMeta, (IpRepr, UdpRepr, &[u8])) -> Result<(), E>, { let endpoint = self.endpoint; let hop_limit = self.hop_limit.unwrap_or(64); @@ -533,7 +533,7 @@ impl<'a> Socket<'a> { hop_limit, ); - emit(cx, (ip_repr, repr, payload_buf, packet_meta.meta)) + emit(cx, packet_meta.meta, (ip_repr, repr, payload_buf)) }); match res { Err(Empty) => Ok(()), @@ -711,7 +711,7 @@ mod test { assert!(socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, _| unreachable!()), + socket.dispatch(&mut cx, |_, _, _| unreachable!()), Ok::<_, ()>(()) ); @@ -723,7 +723,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _meta)| { + socket.dispatch(&mut cx, |_, _, (ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -734,7 +734,7 @@ mod test { assert!(!socket.can_send()); assert_eq!( - socket.dispatch(&mut cx, |_, (ip_repr, udp_repr, payload, _meta)| { + socket.dispatch(&mut cx, |_, _, (ip_repr, udp_repr, payload)| { assert_eq!(ip_repr, LOCAL_IP_REPR); assert_eq!(udp_repr, LOCAL_UDP_REPR); assert_eq!(payload, PAYLOAD); @@ -862,7 +862,7 @@ mod test { s.set_hop_limit(Some(0x2a)); assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(())); assert_eq!( - s.dispatch(&mut cx, |_, (ip_repr, _, _, _)| { + s.dispatch(&mut cx, |_, _, (ip_repr, _, _)| { assert_eq!( ip_repr, IpReprIpvX(IpvXRepr { From 62396092b62cdcb14f785b46af736c747a6ae431 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 26 Jun 2023 00:20:36 +0200 Subject: [PATCH 565/566] Improve tests using rstest, move into own files, add more tests --- Cargo.toml | 1 + src/iface/interface/tests.rs | 1775 ------------------------ src/iface/interface/tests/ipv4.rs | 961 +++++++++++++ src/iface/interface/tests/ipv6.rs | 732 ++++++++++ src/iface/interface/tests/mod.rs | 183 +++ src/iface/interface/tests/sixlowpan.rs | 411 ++++++ 6 files changed, 2288 insertions(+), 1775 deletions(-) delete mode 100644 src/iface/interface/tests.rs create mode 100644 src/iface/interface/tests/ipv4.rs create mode 100644 src/iface/interface/tests/ipv6.rs create mode 100644 src/iface/interface/tests/mod.rs create mode 100644 src/iface/interface/tests/sixlowpan.rs diff --git a/Cargo.toml b/Cargo.toml index 51116eb67..9167f7f05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ env_logger = "0.10" getopts = "0.2" rand = "0.8" url = "2.0" +rstest = "0.17" [features] std = ["managed/std", "alloc"] diff --git a/src/iface/interface/tests.rs b/src/iface/interface/tests.rs deleted file mode 100644 index 88431f4f3..000000000 --- a/src/iface/interface/tests.rs +++ /dev/null @@ -1,1775 +0,0 @@ -#[cfg(feature = "proto-igmp")] -use std::vec::Vec; - -use super::*; - -use crate::iface::Interface; -use crate::phy::{ChecksumCapabilities, Loopback}; -#[cfg(feature = "proto-igmp")] -use crate::time::Instant; - -#[allow(unused)] -fn fill_slice(s: &mut [u8], val: u8) { - for x in s.iter_mut() { - *x = val - } -} - -#[cfg(feature = "medium-ethernet")] -const MEDIUM: Medium = Medium::Ethernet; -#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))] -const MEDIUM: Medium = Medium::Ip; -#[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))] -const MEDIUM: Medium = Medium::Ieee802154; - -fn create<'a>(medium: Medium) -> (Interface, SocketSet<'a>, Loopback) { - match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => create_ethernet(), - #[cfg(feature = "medium-ip")] - Medium::Ip => create_ip(), - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => create_ieee802154(), - } -} - -#[cfg(feature = "medium-ip")] -#[allow(unused)] -fn create_ip<'a>() -> (Interface, SocketSet<'a>, Loopback) { - // Create a basic device - let mut device = Loopback::new(Medium::Ip); - - let config = Config::new(HardwareAddress::Ip); - let mut iface = Interface::new(config, &mut device, Instant::ZERO); - iface.update_ip_addrs(|ip_addrs| { - #[cfg(feature = "proto-ipv4")] - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - }); - - (iface, SocketSet::new(vec![]), device) -} - -#[cfg(feature = "medium-ethernet")] -fn create_ethernet<'a>() -> (Interface, SocketSet<'a>, Loopback) { - // Create a basic device - let mut device = Loopback::new(Medium::Ethernet); - - let config = Config::new(HardwareAddress::Ethernet(EthernetAddress::default())); - let mut iface = Interface::new(config, &mut device, Instant::ZERO); - iface.update_ip_addrs(|ip_addrs| { - #[cfg(feature = "proto-ipv4")] - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - }); - - (iface, SocketSet::new(vec![]), device) -} - -#[cfg(feature = "medium-ieee802154")] -fn create_ieee802154<'a>() -> (Interface, SocketSet<'a>, Loopback) { - // Create a basic device - let mut device = Loopback::new(Medium::Ieee802154); - - let config = Config::new(HardwareAddress::Ieee802154(Ieee802154Address::default())); - let mut iface = Interface::new(config, &mut device, Instant::ZERO); - iface.update_ip_addrs(|ip_addrs| { - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) - .unwrap(); - #[cfg(feature = "proto-ipv6")] - ip_addrs - .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) - .unwrap(); - }); - - (iface, SocketSet::new(vec![]), device) -} - -#[cfg(feature = "proto-igmp")] -fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { - let mut pkts = Vec::new(); - while let Some((rx, _tx)) = device.receive(timestamp) { - rx.consume(|pkt| { - pkts.push(pkt.to_vec()); - }); - } - pkts -} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct MockTxToken; - -impl TxToken for MockTxToken { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - let mut junk = [0; 1536]; - f(&mut junk[..len]) - } -} - -#[test] -#[should_panic(expected = "The hardware address does not match the medium of the interface.")] -#[cfg(all(feature = "medium-ip", feature = "medium-ethernet"))] -fn test_new_panic() { - let mut device = Loopback::new(Medium::Ethernet); - let config = Config::new(HardwareAddress::Ip); - Interface::new(config, &mut device, Instant::ZERO); -} - -#[test] -#[cfg(feature = "proto-ipv4")] -fn test_no_icmp_no_unicast_ipv4() { - let (mut iface, mut sockets, _device) = create(MEDIUM); - - // Unknown Ipv4 Protocol - // - // Because the destination is the broadcast address - // this should not trigger and Destination Unreachable - // response. See RFC 1122 § 3.2.2. - let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 54]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv4Packet::new_unchecked(&bytes); - - // 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 sockets, - PacketMeta::default(), - &frame, - &mut iface.fragments - ), - None - ); -} - -#[test] -#[cfg(feature = "proto-ipv6")] -fn test_no_icmp_no_unicast_ipv6() { - let (mut iface, mut sockets, _device) = create(MEDIUM); - - // Unknown Ipv6 Protocol - // - // Because the destination is the broadcast address - // this should not trigger and Destination Unreachable - // response. See RFC 1122 § 3.2.2. - let repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 54]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv6Packet::new_unchecked(&bytes); - - // 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 sockets, PacketMeta::default(), &frame), - None - ); -} - -#[test] -#[cfg(feature = "proto-ipv4")] -fn test_icmp_error_no_payload() { - static NO_BYTES: [u8; 0] = []; - let (mut iface, mut sockets, _device) = create(MEDIUM); - - // Unknown Ipv4 Protocol with no payload - let repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Unknown(0x0c), - payload_len: 0, - hop_limit: 0x40, - }); - - let mut bytes = vec![0u8; 34]; - repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let frame = Ipv4Packet::new_unchecked(&bytes); - - // The expected Destination Unreachable response due to the - // unknown protocol - let icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::ProtoUnreachable, - header: Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Unknown(12), - payload_len: 0, - hop_limit: 64, - }, - data: &NO_BYTES, - }; - - let expected_repr = IpPacket::Icmpv4(( - Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }, - icmp_repr, - )); - - // Ensure that the unknown protocol triggers an error response. - // And we correctly handle no payload. - - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - PacketMeta::default(), - &frame, - &mut iface.fragments - ), - Some(expected_repr) - ); -} - -#[test] -#[cfg(feature = "proto-ipv4")] -fn test_local_subnet_broadcasts() { - let (mut iface, _, _device) = create(MEDIUM); - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); - }); - }); - - assert!(iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); - assert!(iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 255]))); - assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 254]))); - - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); - }); - }); - assert!(iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([192, 168, 23, 255]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([192, 168, 23, 254]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([192, 168, 255, 254]))); - assert!(iface - .inner - .is_broadcast_v4(Ipv4Address([192, 168, 255, 255]))); - - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); - }); - }); - assert!(iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); - assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 255]))); - assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 254]))); - assert!(!iface - .inner - .is_broadcast_v4(Ipv4Address([192, 255, 255, 254]))); - assert!(iface - .inner - .is_broadcast_v4(Ipv4Address([192, 255, 255, 255]))); -} - -#[test] -#[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] -fn test_icmp_error_port_unreachable() { - static UDP_PAYLOAD: [u8; 12] = [ - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, - ]; - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let mut udp_bytes_unicast = vec![0u8; 20]; - let mut udp_bytes_broadcast = vec![0u8; 20]; - let mut packet_unicast = UdpPacket::new_unchecked(&mut udp_bytes_unicast); - let mut packet_broadcast = UdpPacket::new_unchecked(&mut udp_bytes_broadcast); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }); - - // Emit the representations to a packet - udp_repr.emit( - &mut packet_unicast, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - let data = packet_unicast.into_inner(); - - // The expected Destination Unreachable ICMPv4 error response due - // to no sockets listening on the destination port. - let icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::PortUnreachable, - header: Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }, - data, - }; - let expected_repr = IpPacket::Icmpv4(( - Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - next_header: IpProtocol::Icmp, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }, - icmp_repr, - )); - - // Ensure that the unknown protocol triggers an error response. - // And we correctly handle no payload. - assert_eq!( - iface.inner.process_udp( - &mut sockets, - PacketMeta::default(), - ip_repr, - udp_repr, - false, - &UDP_PAYLOAD, - data - ), - Some(expected_repr) - ); - - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 64, - }); - - // Emit the representations to a packet - udp_repr.emit( - &mut packet_broadcast, - &ip_repr.src_addr(), - &IpAddress::Ipv4(Ipv4Address::BROADCAST), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - // 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 sockets, - PacketMeta::default(), - ip_repr, - udp_repr, - false, - &UDP_PAYLOAD, - packet_broadcast.into_inner(), - ), - None - ); -} - -#[test] -#[cfg(feature = "socket-udp")] -fn test_handle_udp_broadcast() { - use crate::{socket::udp::UdpMetadata, wire::IpEndpoint}; - - static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - - let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); - - let mut udp_bytes = vec![0u8; 13]; - let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); - - let socket_handle = sockets.add(udp_socket); - - #[cfg(feature = "proto-ipv6")] - let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] - let src_ip = Ipv4Address::new(0x7f, 0x00, 0x00, 0x02); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - - #[cfg(feature = "proto-ipv6")] - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: src_ip, - dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40, - }); - #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] - let ip_repr = IpRepr::Ipv4(Ipv4Repr { - src_addr: src_ip, - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Udp, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - hop_limit: 0x40, - }); - - // Bind the socket to port 68 - let socket = sockets.get_mut::(socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - - udp_repr.emit( - &mut packet, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - - // Packet should be handled by bound UDP socket - assert_eq!( - iface.inner.process_udp( - &mut sockets, - PacketMeta::default(), - ip_repr, - udp_repr, - false, - &UDP_PAYLOAD, - packet.into_inner(), - ), - None - ); - - // Make sure the payload to the UDP packet processed by process_udp is - // appended to the bound sockets rx_buffer - let socket = sockets.get_mut::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok(( - &UDP_PAYLOAD[..], - UdpMetadata { - endpoint: IpEndpoint::new(src_ip.into(), 67), - meta: PacketMeta::default() - } - )) - ); -} - -#[test] -#[cfg(feature = "proto-ipv4")] -fn test_handle_ipv4_broadcast() { - use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let our_ipv4_addr = iface.ipv4_addr().unwrap(); - let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); - - // ICMPv4 echo request - let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; - let icmpv4_repr = Icmpv4Repr::EchoRequest { - ident: 0x1234, - seq_no: 0xabcd, - data: &icmpv4_data, - }; - - // Send to IPv4 broadcast address - let ipv4_repr = Ipv4Repr { - src_addr: src_ipv4_addr, - dst_addr: Ipv4Address::BROADCAST, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: icmpv4_repr.buffer_len(), - }; - - // Emit to ip frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - icmpv4_repr.emit( - &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - // Expected ICMPv4 echo reply - let expected_icmpv4_repr = Icmpv4Repr::EchoReply { - ident: 0x1234, - seq_no: 0xabcd, - data: &icmpv4_data, - }; - let expected_ipv4_repr = Ipv4Repr { - src_addr: our_ipv4_addr, - dst_addr: src_ipv4_addr, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: expected_icmpv4_repr.buffer_len(), - }; - let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); - - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - PacketMeta::default(), - &frame, - &mut iface.fragments - ), - Some(expected_packet) - ); -} - -#[test] -#[cfg(feature = "socket-udp")] -fn test_icmp_reply_size() { - #[cfg(feature = "proto-ipv6")] - use crate::wire::Icmpv6DstUnreachable; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - use crate::wire::IPV4_MIN_MTU as MIN_MTU; - #[cfg(feature = "proto-ipv6")] - use crate::wire::IPV6_MIN_MTU as MIN_MTU; - - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - const MAX_PAYLOAD_LEN: usize = 528; - #[cfg(feature = "proto-ipv6")] - const MAX_PAYLOAD_LEN: usize = 1192; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let src_addr = Ipv4Address([192, 168, 1, 1]); - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let dst_addr = Ipv4Address([192, 168, 1, 2]); - #[cfg(feature = "proto-ipv6")] - let src_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - #[cfg(feature = "proto-ipv6")] - let dst_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2); - - // UDP packet that if not tructated will cause a icmp port unreachable reply - // to exeed the minimum mtu bytes in length. - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - MAX_PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let ip_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, - }; - #[cfg(feature = "proto-ipv6")] - let ip_repr = Ipv6Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, - }; - let payload = packet.into_inner(); - - // Expected packets - #[cfg(feature = "proto-ipv6")] - let expected_icmp_repr = Icmpv6Repr::DstUnreachable { - reason: Icmpv6DstUnreachable::PortUnreachable, - header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN], - }; - #[cfg(feature = "proto-ipv6")] - let expected_ip_repr = Ipv6Repr { - src_addr: dst_addr, - dst_addr: src_addr, - next_header: IpProtocol::Icmpv6, - hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(), - }; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let expected_icmp_repr = Icmpv4Repr::DstUnreachable { - reason: Icmpv4DstUnreachable::PortUnreachable, - header: ip_repr, - data: &payload[..MAX_PAYLOAD_LEN], - }; - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - let expected_ip_repr = Ipv4Repr { - src_addr: dst_addr, - dst_addr: src_addr, - next_header: IpProtocol::Icmp, - hop_limit: 64, - payload_len: expected_icmp_repr.buffer_len(), - }; - - // The expected packet does not exceed the IPV4_MIN_MTU - #[cfg(feature = "proto-ipv6")] - assert_eq!( - expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), - MIN_MTU - ); - // The expected packet does not exceed the IPV4_MIN_MTU - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - 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 sockets, - PacketMeta::default(), - ip_repr.into(), - udp_repr, - false, - &vec![0x2a; MAX_PAYLOAD_LEN], - payload, - ), - Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) - ); - #[cfg(feature = "proto-ipv6")] - assert_eq!( - iface.inner.process_udp( - &mut sockets, - PacketMeta::default(), - ip_repr.into(), - udp_repr, - false, - &vec![0x2a; MAX_PAYLOAD_LEN], - payload, - ), - Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) - ); -} - -#[test] -#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] -fn test_handle_valid_arp_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: local_ip_addr, - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - - // Ensure an ARP Request for us triggers an ARP Reply - assert_eq!( - iface.inner.process_ethernet( - &mut sockets, - PacketMeta::default(), - frame.into_inner(), - &mut iface.fragments - ), - Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { - operation: ArpOperation::Reply, - source_hardware_addr: local_hw_addr, - source_protocol_addr: local_ip_addr, - target_hardware_addr: remote_hw_addr, - target_protocol_addr: remote_ip_addr - })) - ); - - // Ensure the address of the requestor was entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr), - &mut iface.fragmenter, - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); -} - -#[test] -#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv6"))] -fn test_handle_valid_ndisc_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 86]; - - let local_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1); - let remote_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { - target_addr: local_ip_addr, - lladdr: Some(remote_hw_addr.into()), - }); - let ip_repr = IpRepr::Ipv6(Ipv6Repr { - src_addr: remote_ip_addr, - dst_addr: local_ip_addr.solicited_node(), - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: solicit.buffer_len(), - }); - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Ipv6); - ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - solicit.emit( - &remote_ip_addr.into(), - &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), - &ChecksumCapabilities::default(), - ); - - let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { - flags: NdiscNeighborFlags::SOLICITED, - target_addr: local_ip_addr, - lladdr: Some(local_hw_addr.into()), - }); - - let ipv6_expected = Ipv6Repr { - src_addr: local_ip_addr, - dst_addr: remote_ip_addr, - next_header: IpProtocol::Icmpv6, - hop_limit: 0xff, - payload_len: icmpv6_expected.buffer_len(), - }; - - // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement - assert_eq!( - iface.inner.process_ethernet( - &mut sockets, - PacketMeta::default(), - frame.into_inner(), - &mut iface.fragments - ), - 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, - &IpAddress::Ipv6(local_ip_addr), - &IpAddress::Ipv6(remote_ip_addr), - &mut iface.fragmenter, - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); -} - -#[test] -#[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))] -fn test_handle_other_arp_request() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x03]), - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - - // Ensure an ARP Request for someone else does not trigger an ARP Reply - assert_eq!( - iface.inner.process_ethernet( - &mut sockets, - PacketMeta::default(), - frame.into_inner(), - &mut iface.fragments - ), - None - ); - - // Ensure the address of the requestor was NOT entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), - &IpAddress::Ipv4(remote_ip_addr), - &mut iface.fragmenter, - ), - Err(DispatchError::NeighborPending) - ); -} - -#[test] -#[cfg(all( - feature = "medium-ethernet", - feature = "proto-ipv4", - not(feature = "medium-ieee802154") -))] -fn test_arp_flush_after_update_ip() { - let (mut iface, mut sockets, _device) = create_ethernet(); - - let mut eth_bytes = vec![0u8; 42]; - - let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); - let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); - - let repr = ArpRepr::EthernetIpv4 { - operation: ArpOperation::Request, - source_hardware_addr: remote_hw_addr, - source_protocol_addr: remote_ip_addr, - target_hardware_addr: EthernetAddress::default(), - target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), - }; - - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Arp); - { - let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); - repr.emit(&mut packet); - } - - // Ensure an ARP Request for us triggers an ARP Reply - assert_eq!( - iface.inner.process_ethernet( - &mut sockets, - PacketMeta::default(), - frame.into_inner(), - &mut iface.fragments - ), - Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { - operation: ArpOperation::Reply, - source_hardware_addr: local_hw_addr, - source_protocol_addr: local_ip_addr, - target_hardware_addr: remote_hw_addr, - target_protocol_addr: remote_ip_addr - })) - ); - - // Ensure the address of the requestor was entered in the cache - assert_eq!( - iface.inner.lookup_hardware_addr( - MockTxToken, - &IpAddress::Ipv4(local_ip_addr), - &IpAddress::Ipv4(remote_ip_addr), - &mut iface.fragmenter, - ), - Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) - ); - - // Update IP addrs to trigger ARP cache flush - let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); - iface.update_ip_addrs(|addrs| { - addrs.iter_mut().next().map(|addr| { - *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); - }); - }); - - // ARP cache flush after address change - assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); -} - -#[test] -#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))] -fn test_icmpv4_socket() { - use crate::wire::Icmpv4Packet; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); - let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); - - let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); - - let socket_handle = sockets.add(icmpv4_socket); - - let ident = 0x1234; - let seq_no = 0x5432; - let echo_data = &[0xff; 16]; - - let socket = sockets.get_mut::(socket_handle); - // Bind to the ID 0x1234 - assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); - - // Ensure the ident we bound to and the ident of the packet are the same. - let mut bytes = [0xff; 24]; - let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); - let echo_repr = Icmpv4Repr::EchoRequest { - ident, - seq_no, - data: echo_data, - }; - echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); - let icmp_data = &*packet.into_inner(); - - let ipv4_repr = Ipv4Repr { - src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), - dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), - next_header: IpProtocol::Icmp, - payload_len: 24, - hop_limit: 64, - }; - let ip_repr = IpRepr::Ipv4(ipv4_repr); - - // Open a socket and ensure the packet is handled due to the listening - // socket. - assert!(!sockets.get_mut::(socket_handle).can_recv()); - - // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening - let echo_reply = Icmpv4Repr::EchoReply { - ident, - seq_no, - data: echo_data, - }; - let ipv4_reply = Ipv4Repr { - src_addr: ipv4_repr.dst_addr, - dst_addr: ipv4_repr.src_addr, - ..ipv4_repr - }; - assert_eq!( - iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), - Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) - ); - - let socket = sockets.get_mut::(socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok(( - icmp_data, - IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) - )) - ); -} - -#[test] -#[cfg(feature = "proto-ipv6")] -fn test_solicited_node_addrs() { - let (mut iface, _, _device) = create(MEDIUM); - let mut new_addrs = heapless::Vec::::new(); - new_addrs - .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) - .unwrap(); - new_addrs - .push(IpCidr::new( - IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), - 64, - )) - .unwrap(); - iface.update_ip_addrs(|addrs| { - new_addrs.extend(addrs.to_vec()); - *addrs = new_addrs; - }); - assert!(iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); - assert!(iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); - assert!(!iface - .inner - .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); -} - -#[test] -#[cfg(feature = "proto-ipv6")] -fn test_icmpv6_nxthdr_unknown() { - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - - let payload = [0x12, 0x34, 0x56, 0x78]; - - let ipv6_repr = Ipv6Repr { - src_addr: remote_ip_addr, - dst_addr: Ipv6Address::LOOPBACK, - next_header: IpProtocol::HopByHop, - payload_len: 12, - hop_limit: 0x40, - }; - - let mut bytes = vec![0; 52]; - let frame = { - let ip_repr = IpRepr::Ipv6(ipv6_repr); - ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); - let mut offset = ipv6_repr.buffer_len(); - { - let mut hbh_pkt = Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); - hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); - hbh_pkt.set_header_len(0); - offset += 8; - { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.payload_mut()[..]); - Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt); - } - { - let mut pad_pkt = Ipv6Option::new_unchecked(&mut hbh_pkt.payload_mut()[5..]); - Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); - } - } - bytes[offset..].copy_from_slice(&payload); - Ipv6Packet::new_unchecked(&bytes) - }; - - let reply_icmp_repr = Icmpv6Repr::ParamProblem { - reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, - pointer: 40, - header: ipv6_repr, - data: &payload[..], - }; - - let reply_ipv6_repr = Ipv6Repr { - src_addr: Ipv6Address::LOOPBACK, - dst_addr: remote_ip_addr, - next_header: IpProtocol::Icmpv6, - payload_len: reply_icmp_repr.buffer_len(), - hop_limit: 0x40, - }; - - // 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 sockets, PacketMeta::default(), &frame), - Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))) - ); -} - -#[test] -#[cfg(feature = "proto-igmp")] -fn test_handle_igmp() { - fn recv_igmp(device: &mut Loopback, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { - let caps = device.capabilities(); - let checksum_caps = &caps.checksum; - recv_all(device, timestamp) - .iter() - .filter_map(|frame| { - let ipv4_packet = match caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - let eth_frame = EthernetFrame::new_checked(frame).ok()?; - Ipv4Packet::new_checked(eth_frame.payload()).ok()? - } - #[cfg(feature = "medium-ip")] - Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => todo!(), - }; - let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; - let ip_payload = ipv4_packet.payload(); - let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; - let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; - Some((ipv4_repr, igmp_repr)) - }) - .collect::>() - } - - let groups = [ - Ipv4Address::new(224, 0, 0, 22), - Ipv4Address::new(224, 0, 0, 56), - ]; - - let (mut iface, mut sockets, mut device) = create(MEDIUM); - - // Join multicast groups - let timestamp = Instant::now(); - for group in &groups { - iface - .join_multicast_group(&mut device, *group, timestamp) - .unwrap(); - } - - let reports = recv_igmp(&mut device, timestamp); - assert_eq!(reports.len(), 2); - for (i, group_addr) in groups.iter().enumerate() { - assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); - assert_eq!(reports[i].0.dst_addr, *group_addr); - assert_eq!( - reports[i].1, - IgmpRepr::MembershipReport { - group_addr: *group_addr, - version: IgmpVersion::Version2, - } - ); - } - - // General query - let timestamp = Instant::now(); - const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, - 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - { - // Transmit GENERAL_QUERY_BYTES into loopback - let tx_token = device.transmit(timestamp).unwrap(); - tx_token.consume(GENERAL_QUERY_BYTES.len(), |buffer| { - buffer.copy_from_slice(GENERAL_QUERY_BYTES); - }); - } - // Trigger processing until all packets received through the - // 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 device, &mut sockets); - - // Leave multicast groups - let timestamp = Instant::now(); - for group in &groups { - iface - .leave_multicast_group(&mut device, *group, timestamp) - .unwrap(); - } - - let leaves = recv_igmp(&mut device, timestamp); - assert_eq!(leaves.len(), 2); - for (i, group_addr) in groups.iter().cloned().enumerate() { - assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); - assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); - assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); - } -} - -#[test] -#[cfg(all(feature = "proto-ipv4", feature = "socket-raw"))] -fn test_raw_socket_no_reply() { - use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let packets = 1; - let rx_buffer = - raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let tx_buffer = raw::PacketBuffer::new( - vec![raw::PacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ); - let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); - sockets.add(raw_socket); - - let src_addr = Ipv4Address([127, 0, 0, 2]); - let dst_addr = Ipv4Address([127, 0, 0, 1]); - - const PAYLOAD_LEN: usize = 10; - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - let ipv4_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + PAYLOAD_LEN, - }; - - // Emit to frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - udp_repr.emit( - &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &src_addr.into(), - &dst_addr.into(), - PAYLOAD_LEN, - |buf| fill_slice(buf, 0x2a), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - PacketMeta::default(), - &frame, - &mut iface.fragments - ), - None - ); -} - -#[test] -#[cfg(all(feature = "proto-ipv4", feature = "socket-raw", feature = "socket-udp"))] -fn test_raw_socket_with_udp_socket() { - use crate::{ - socket::udp::UdpMetadata, - wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}, - }; - - static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; - - let (mut iface, mut sockets, _device) = create(MEDIUM); - - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); - let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let udp_socket_handle = sockets.add(udp_socket); - - // Bind the socket to port 68 - let socket = sockets.get_mut::(udp_socket_handle); - assert_eq!(socket.bind(68), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - - let packets = 1; - let raw_rx_buffer = - raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); - let raw_tx_buffer = raw::PacketBuffer::new( - vec![raw::PacketMetadata::EMPTY; packets], - vec![0; 48 * packets], - ); - let raw_socket = raw::Socket::new( - IpVersion::Ipv4, - IpProtocol::Udp, - raw_rx_buffer, - raw_tx_buffer, - ); - sockets.add(raw_socket); - - let src_addr = Ipv4Address([127, 0, 0, 2]); - let dst_addr = Ipv4Address([127, 0, 0, 1]); - - let udp_repr = UdpRepr { - src_port: 67, - dst_port: 68, - }; - let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; - let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); - udp_repr.emit( - &mut packet, - &src_addr.into(), - &dst_addr.into(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - let ipv4_repr = Ipv4Repr { - src_addr, - dst_addr, - next_header: IpProtocol::Udp, - hop_limit: 64, - payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), - }; - - // Emit to frame - let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; - let frame = { - ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(&mut bytes), - &ChecksumCapabilities::default(), - ); - udp_repr.emit( - &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), - &src_addr.into(), - &dst_addr.into(), - UDP_PAYLOAD.len(), - |buf| buf.copy_from_slice(&UDP_PAYLOAD), - &ChecksumCapabilities::default(), - ); - Ipv4Packet::new_unchecked(&bytes) - }; - - assert_eq!( - iface.inner.process_ipv4( - &mut sockets, - PacketMeta::default(), - &frame, - &mut iface.fragments - ), - None - ); - - // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP - let socket = sockets.get_mut::(udp_socket_handle); - assert!(socket.can_recv()); - assert_eq!( - socket.recv(), - Ok(( - &UDP_PAYLOAD[..], - UdpMetadata { - endpoint: IpEndpoint::new(src_addr.into(), 67), - meta: PacketMeta::default() - } - )) - ); -} - -#[cfg(all( - not(feature = "medium-ethernet"), - feature = "proto-sixlowpan", - feature = "proto-sixlowpan-fragmentation" -))] -#[test] -fn test_echo_request_sixlowpan_128_bytes() { - use crate::phy::Checksum; - - let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154); - // TODO: modify the example, such that we can also test if the checksum is correctly - // computed. - iface.inner.caps.checksum.icmpv6 = Checksum::None; - - assert_eq!(iface.inner.caps.medium, Medium::Ieee802154); - let now = iface.inner.now(); - - iface.inner.neighbor_cache.fill( - Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0]).into(), - HardwareAddress::Ieee802154(Ieee802154Address::default()), - now, - ); - - let mut ieee802154_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(5), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: Some(Ieee802154Pan(0xbeef)), - dst_addr: Some(Ieee802154Address::Extended([ - 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, - ])), - src_pan_id: Some(Ieee802154Pan(0xbeef)), - src_addr: Some(Ieee802154Address::Extended([ - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, - ])), - }; - - // NOTE: this data is retrieved from tests with Contiki-NG - - let request_first_part_packet = SixlowpanFragPacket::new_checked(&[ - 0xc0, 0xb0, 0x00, 0x8e, 0x6a, 0x33, 0x05, 0x25, 0x2c, 0x3a, 0x80, 0x00, 0xe0, 0x71, 0x00, - 0x27, 0x00, 0x02, 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, - 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - ]) - .unwrap(); - - let request_first_part_iphc_packet = - SixlowpanIphcPacket::new_checked(request_first_part_packet.payload()).unwrap(); - - let request_first_part_iphc_repr = SixlowpanIphcRepr::parse( - &request_first_part_iphc_packet, - ieee802154_repr.src_addr, - ieee802154_repr.dst_addr, - &iface.inner.sixlowpan_address_context, - ) - .unwrap(); - - assert_eq!( - request_first_part_iphc_repr.src_addr, - Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, - 0x1a, - ]), - ); - assert_eq!( - request_first_part_iphc_repr.dst_addr, - Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, - 0x76, - ]), - ); - - let request_second_part = [ - 0xe0, 0xb0, 0x00, 0x8e, 0x10, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - ]; - - assert_eq!( - iface.inner.process_sixlowpan( - &mut sockets, - PacketMeta::default(), - &ieee802154_repr, - &request_first_part_packet.into_inner(), - &mut iface.fragments - ), - None - ); - - ieee802154_repr.sequence_number = Some(6); - - // data that was generated when using `ping -s 128` - let data = &[ - 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, - 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - ]; - - let result = iface.inner.process_sixlowpan( - &mut sockets, - PacketMeta::default(), - &ieee802154_repr, - &request_second_part, - &mut iface.fragments, - ); - - assert_eq!( - result, - Some(IpPacket::Icmpv6(( - Ipv6Repr { - src_addr: Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, - 0xfc, 0x76, - ]), - dst_addr: Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, - 0xb, 0x1a, - ]), - next_header: IpProtocol::Icmpv6, - payload_len: 136, - hop_limit: 64, - }, - Icmpv6Repr::EchoReply { - ident: 39, - seq_no: 2, - data, - } - ))) - ); - - iface.inner.neighbor_cache.fill( - IpAddress::Ipv6(Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, - ])), - HardwareAddress::Ieee802154(Ieee802154Address::default()), - Instant::now(), - ); - - let tx_token = device.transmit(Instant::now()).unwrap(); - iface.inner.dispatch_ieee802154( - Ieee802154Address::default(), - tx_token, - PacketMeta::default(), - result.unwrap(), - &mut iface.fragmenter, - ); - - assert_eq!( - device.queue[0], - &[ - 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb0, 0x5, 0x4e, 0x7a, 0x11, 0x3a, 0x92, 0xfc, 0x48, 0xc2, - 0xa4, 0x41, 0xfc, 0x76, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, 0x81, 0x0, 0x0, - 0x0, 0x0, 0x27, 0x0, 0x2, 0xa2, 0xc2, 0x2d, 0x63, 0x0, 0x0, 0x0, 0x0, 0xd9, 0x5e, 0xc, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, - 0x44, 0x45, 0x46, 0x47, - ] - ); - - iface.poll(Instant::now(), &mut device, &mut sockets); - - assert_eq!( - device.queue[1], - &[ - 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb0, 0x5, 0x4e, 0xf, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, - 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, - 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, - ] - ); -} - -#[cfg(all( - not(feature = "medium-ethernet"), - feature = "proto-sixlowpan", - feature = "proto-sixlowpan-fragmentation" -))] -#[test] -fn test_sixlowpan_udp_with_fragmentation() { - use crate::phy::Checksum; - - let mut ieee802154_repr = Ieee802154Repr { - frame_type: Ieee802154FrameType::Data, - security_enabled: false, - frame_pending: false, - ack_request: false, - sequence_number: Some(5), - pan_id_compression: true, - frame_version: Ieee802154FrameVersion::Ieee802154_2003, - dst_pan_id: Some(Ieee802154Pan(0xbeef)), - dst_addr: Some(Ieee802154Address::Extended([ - 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, - ])), - src_pan_id: Some(Ieee802154Pan(0xbeef)), - src_addr: Some(Ieee802154Address::Extended([ - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, - ])), - }; - - let (mut iface, mut sockets, mut device) = create(Medium::Ieee802154); - iface.inner.caps.checksum.udp = Checksum::None; - - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); - let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); - let udp_socket_handle = sockets.add(udp_socket); - - { - let socket = sockets.get_mut::(udp_socket_handle); - assert_eq!(socket.bind(6969), Ok(())); - assert!(!socket.can_recv()); - assert!(socket.can_send()); - } - - let udp_first_part = &[ - 0xc0, 0xbc, 0x00, 0x92, 0x6e, 0x33, 0x07, 0xe7, 0xdc, 0xf0, 0xd3, 0xc9, 0x1b, 0x39, 0xbf, - 0xa0, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, - 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, - 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, - 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x49, 0x6e, - 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, 0x73, 0x20, 0x74, 0x6f, 0x72, - 0x74, 0x6f, 0x72, 0x2e, 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, - ]; - - assert_eq!( - iface.inner.process_sixlowpan( - &mut sockets, - PacketMeta::default(), - &ieee802154_repr, - udp_first_part, - &mut iface.fragments - ), - None - ); - - ieee802154_repr.sequence_number = Some(6); - - let udp_second_part = &[ - 0xe0, 0xbc, 0x00, 0x92, 0x11, 0x64, 0x69, 0x74, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, - 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, - 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, - ]; - - assert_eq!( - iface.inner.process_sixlowpan( - &mut sockets, - PacketMeta::default(), - &ieee802154_repr, - udp_second_part, - &mut iface.fragments - ), - None - ); - - let socket = sockets.get_mut::(udp_socket_handle); - - let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ - In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec."; - - assert_eq!( - socket.recv().map(|(data, meta)| (data, meta.endpoint)), - Ok(( - &udp_data[..], - IpEndpoint { - addr: IpAddress::Ipv6(Ipv6Address([ - 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, - 0xb, 0x1a, - ])), - port: 54217, - } - )) - ); - - let tx_token = device.transmit(Instant::now()).unwrap(); - iface.inner.dispatch_ieee802154( - Ieee802154Address::default(), - tx_token, - PacketMeta::default(), - IpPacket::Udp(( - IpRepr::Ipv6(Ipv6Repr { - src_addr: Ipv6Address::default(), - dst_addr: Ipv6Address::default(), - next_header: IpProtocol::Udp, - payload_len: udp_data.len(), - hop_limit: 64, - }), - UdpRepr { - src_port: 1234, - dst_port: 1234, - }, - udp_data, - )), - &mut iface.fragmenter, - ); - - iface.poll(Instant::now(), &mut device, &mut sockets); - - assert_eq!( - device.queue[0], - &[ - 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb4, 0x5, 0x4e, 0x7e, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x4, 0xd2, 0x4, 0xd2, 0xf6, - 0x4d, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, - 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, - 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, - 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, - 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, - 0x73, 0x20, 0x74, - ] - ); - - assert_eq!( - device.queue[1], - &[ - 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb4, 0x5, 0x4e, 0xf, 0x6f, 0x72, 0x74, 0x6f, 0x72, 0x2e, - 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, 0x64, 0x69, 0x74, 0x20, - 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, - 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, - 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, - 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, - ] - ); -} diff --git a/src/iface/interface/tests/ipv4.rs b/src/iface/interface/tests/ipv4.rs new file mode 100644 index 000000000..d109dc4f3 --- /dev/null +++ b/src/iface/interface/tests/ipv4.rs @@ -0,0 +1,961 @@ +use super::*; + +#[rstest] +#[case(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_no_icmp_no_unicast(#[case] medium: Medium) { + let (mut iface, mut sockets, _) = setup(medium); + + // Unknown Ipv4 Protocol + // + // Because the destination is the broadcast address + // this should not trigger and Destination Unreachable + // response. See RFC 1122 § 3.2.2. + let repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + hop_limit: 0x40, + }); + + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // 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 sockets, + PacketMeta::default(), + &frame, + &mut iface.fragments + ), + None + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_icmp_error_no_payload(#[case] medium: Medium) { + static NO_BYTES: [u8; 0] = []; + let (mut iface, mut sockets, _device) = setup(medium); + + // Unknown Ipv4 Protocol with no payload + let repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + hop_limit: 0x40, + }); + + let mut bytes = vec![0u8; 34]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // The expected Destination Unreachable response due to the + // unknown protocol + let icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::ProtoUnreachable, + header: Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Unknown(12), + payload_len: 0, + hop_limit: 64, + }, + data: &NO_BYTES, + }; + + let expected_repr = IpPacket::Icmpv4(( + Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }, + icmp_repr, + )); + + // Ensure that the unknown protocol triggers an error response. + // And we correctly handle no payload. + + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + PacketMeta::default(), + &frame, + &mut iface.fragments + ), + Some(expected_repr) + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_local_subnet_broadcasts(#[case] medium: Medium) { + let (mut iface, _, _device) = setup(medium); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); + }); + }); + + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); + assert!(iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 255]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 254]))); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); + }); + }); + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([192, 168, 23, 255]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([192, 168, 23, 254]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([192, 168, 255, 254]))); + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([192, 168, 255, 255]))); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); + }); + }); + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 255]))); + assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 254]))); + assert!(!iface + .inner + .is_broadcast_v4(Ipv4Address([192, 255, 255, 254]))); + assert!(iface + .inner + .is_broadcast_v4(Ipv4Address([192, 255, 255, 255]))); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "medium-ip", feature = "socket-udp"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "medium-ethernet", feature = "socket-udp"))] +fn test_icmp_error_port_unreachable(#[case] medium: Medium) { + static UDP_PAYLOAD: [u8; 12] = [ + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, + ]; + let (mut iface, mut sockets, _device) = setup(medium); + + let mut udp_bytes_unicast = vec![0u8; 20]; + let mut udp_bytes_broadcast = vec![0u8; 20]; + let mut packet_unicast = UdpPacket::new_unchecked(&mut udp_bytes_unicast); + let mut packet_broadcast = UdpPacket::new_unchecked(&mut udp_bytes_broadcast); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }); + + // Emit the representations to a packet + udp_repr.emit( + &mut packet_unicast, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + let data = packet_unicast.into_inner(); + + // The expected Destination Unreachable ICMPv4 error response due + // to no sockets listening on the destination port. + let icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::PortUnreachable, + header: Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }, + data, + }; + let expected_repr = IpPacket::Icmpv4(( + Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + next_header: IpProtocol::Icmp, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }, + icmp_repr, + )); + + // Ensure that the unknown protocol triggers an error response. + // And we correctly handle no payload. + assert_eq!( + iface.inner.process_udp( + &mut sockets, + PacketMeta::default(), + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + data + ), + Some(expected_repr) + ); + + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 64, + }); + + // Emit the representations to a packet + udp_repr.emit( + &mut packet_broadcast, + &ip_repr.src_addr(), + &IpAddress::Ipv4(Ipv4Address::BROADCAST), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + // 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 sockets, + PacketMeta::default(), + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet_broadcast.into_inner(), + ), + None + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_handle_ipv4_broadcast(#[case] medium: Medium) { + use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Packet}; + + let (mut iface, mut sockets, _device) = setup(medium); + + let our_ipv4_addr = iface.ipv4_addr().unwrap(); + let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); + + // ICMPv4 echo request + let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; + let icmpv4_repr = Icmpv4Repr::EchoRequest { + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, + }; + + // Send to IPv4 broadcast address + let ipv4_repr = Ipv4Repr { + src_addr: src_ipv4_addr, + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: icmpv4_repr.buffer_len(), + }; + + // Emit to ip frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + icmpv4_repr.emit( + &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + // Expected ICMPv4 echo reply + let expected_icmpv4_repr = Icmpv4Repr::EchoReply { + ident: 0x1234, + seq_no: 0xabcd, + data: &icmpv4_data, + }; + let expected_ipv4_repr = Ipv4Repr { + src_addr: our_ipv4_addr, + dst_addr: src_ipv4_addr, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: expected_icmpv4_repr.buffer_len(), + }; + let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); + + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + PacketMeta::default(), + &frame, + &mut iface.fragments + ), + Some(expected_packet) + ); +} + +#[rstest] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_handle_valid_arp_request(#[case] medium: Medium) { + let (mut iface, mut sockets, _device) = setup(medium); + + let mut eth_bytes = vec![0u8; 42]; + + let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: local_ip_addr, + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + + // Ensure an ARP Request for us triggers an ARP Reply + assert_eq!( + iface.inner.process_ethernet( + &mut sockets, + PacketMeta::default(), + frame.into_inner(), + &mut iface.fragments + ), + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: local_hw_addr, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + })) + ); + + // Ensure the address of the requestor was entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); +} + +#[rstest] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_handle_other_arp_request(#[case] medium: Medium) { + let (mut iface, mut sockets, _device) = setup(medium); + + let mut eth_bytes = vec![0u8; 42]; + + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x03]), + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + + // Ensure an ARP Request for someone else does not trigger an ARP Reply + assert_eq!( + iface.inner.process_ethernet( + &mut sockets, + PacketMeta::default(), + frame.into_inner(), + &mut iface.fragments + ), + None + ); + + // Ensure the address of the requestor was NOT entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, + ), + Err(DispatchError::NeighborPending) + ); +} + +#[rstest] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_arp_flush_after_update_ip(#[case] medium: Medium) { + let (mut iface, mut sockets, _device) = setup(medium); + + let mut eth_bytes = vec![0u8; 42]; + + let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let repr = ArpRepr::EthernetIpv4 { + operation: ArpOperation::Request, + source_hardware_addr: remote_hw_addr, + source_protocol_addr: remote_ip_addr, + target_hardware_addr: EthernetAddress::default(), + target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), + }; + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress::BROADCAST); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Arp); + { + let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); + repr.emit(&mut packet); + } + + // Ensure an ARP Request for us triggers an ARP Reply + assert_eq!( + iface.inner.process_ethernet( + &mut sockets, + PacketMeta::default(), + frame.into_inner(), + &mut iface.fragments + ), + Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { + operation: ArpOperation::Reply, + source_hardware_addr: local_hw_addr, + source_protocol_addr: local_ip_addr, + target_hardware_addr: remote_hw_addr, + target_protocol_addr: remote_ip_addr + })) + ); + + // Ensure the address of the requestor was entered in the cache + assert_eq!( + iface.inner.lookup_hardware_addr( + MockTxToken, + &IpAddress::Ipv4(local_ip_addr), + &IpAddress::Ipv4(remote_ip_addr), + &mut iface.fragmenter, + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); + + // Update IP addrs to trigger ARP cache flush + let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); + }); + }); + + // ARP cache flush after address change + assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "socket-icmp", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "socket-icmp", feature = "medium-ethernet"))] +fn test_icmpv4_socket(#[case] medium: Medium) { + use crate::wire::Icmpv4Packet; + + let (mut iface, mut sockets, _device) = setup(medium); + + let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); + let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); + + let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); + + let socket_handle = sockets.add(icmpv4_socket); + + let ident = 0x1234; + let seq_no = 0x5432; + let echo_data = &[0xff; 16]; + + let socket = sockets.get_mut::(socket_handle); + // Bind to the ID 0x1234 + assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); + + // Ensure the ident we bound to and the ident of the packet are the same. + let mut bytes = [0xff; 24]; + let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); + let echo_repr = Icmpv4Repr::EchoRequest { + ident, + seq_no, + data: echo_data, + }; + echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); + let icmp_data = &*packet.into_inner(); + + let ipv4_repr = Ipv4Repr { + src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), + dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), + next_header: IpProtocol::Icmp, + payload_len: 24, + hop_limit: 64, + }; + let ip_repr = IpRepr::Ipv4(ipv4_repr); + + // Open a socket and ensure the packet is handled due to the listening + // socket. + assert!(!sockets.get_mut::(socket_handle).can_recv()); + + // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening + let echo_reply = Icmpv4Repr::EchoReply { + ident, + seq_no, + data: echo_data, + }; + let ipv4_reply = Ipv4Repr { + src_addr: ipv4_repr.dst_addr, + dst_addr: ipv4_repr.src_addr, + ..ipv4_repr + }; + assert_eq!( + iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), + Some(IpPacket::Icmpv4((ipv4_reply, echo_reply))) + ); + + let socket = sockets.get_mut::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok(( + icmp_data, + IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) + )) + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "proto-igmp", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "proto-igmp", feature = "medium-ethernet"))] +fn test_handle_igmp(#[case] medium: Medium) { + fn recv_igmp(device: &mut Loopback, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + let caps = device.capabilities(); + let checksum_caps = &caps.checksum; + recv_all(device, timestamp) + .iter() + .filter_map(|frame| { + let ipv4_packet = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => { + let eth_frame = EthernetFrame::new_checked(frame).ok()?; + Ipv4Packet::new_checked(eth_frame.payload()).ok()? + } + #[cfg(feature = "medium-ip")] + Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => todo!(), + }; + let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; + let ip_payload = ipv4_packet.payload(); + let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; + let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; + Some((ipv4_repr, igmp_repr)) + }) + .collect::>() + } + + let groups = [ + Ipv4Address::new(224, 0, 0, 22), + Ipv4Address::new(224, 0, 0, 56), + ]; + + let (mut iface, mut sockets, mut device) = setup(medium); + + // Join multicast groups + let timestamp = Instant::now(); + for group in &groups { + iface + .join_multicast_group(&mut device, *group, timestamp) + .unwrap(); + } + + let reports = recv_igmp(&mut device, timestamp); + assert_eq!(reports.len(), 2); + for (i, group_addr) in groups.iter().enumerate() { + assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); + assert_eq!(reports[i].0.dst_addr, *group_addr); + assert_eq!( + reports[i].1, + IgmpRepr::MembershipReport { + group_addr: *group_addr, + version: IgmpVersion::Version2, + } + ); + } + + // General query + let timestamp = Instant::now(); + const GENERAL_QUERY_BYTES: &[u8] = &[ + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, + 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + ]; + { + // Transmit GENERAL_QUERY_BYTES into loopback + let tx_token = device.transmit(timestamp).unwrap(); + tx_token.consume(GENERAL_QUERY_BYTES.len(), |buffer| { + buffer.copy_from_slice(GENERAL_QUERY_BYTES); + }); + } + // Trigger processing until all packets received through the + // 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 device, &mut sockets); + + // Leave multicast groups + let timestamp = Instant::now(); + for group in &groups { + iface + .leave_multicast_group(&mut device, *group, timestamp) + .unwrap(); + } + + let leaves = recv_igmp(&mut device, timestamp); + assert_eq!(leaves.len(), 2); + for (i, group_addr) in groups.iter().cloned().enumerate() { + assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); + assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); + assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); + } +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "socket-raw", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "socket-raw", feature = "medium-ethernet"))] +fn test_raw_socket_no_reply(#[case] medium: Medium) { + use crate::wire::{IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + + let (mut iface, mut sockets, _) = setup(medium); + + let packets = 1; + let rx_buffer = + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); + let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); + sockets.add(raw_socket); + + let src_addr = Ipv4Address([127, 0, 0, 2]); + let dst_addr = Ipv4Address([127, 0, 0, 1]); + + const PAYLOAD_LEN: usize = 10; + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + let ipv4_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + PAYLOAD_LEN, + }; + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + udp_repr.emit( + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &src_addr.into(), + &dst_addr.into(), + PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + PacketMeta::default(), + &frame, + &mut iface.fragments + ), + None + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "socket-raw", feature = "socket-udp", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all( + feature = "socket-raw", + feature = "socket-udp", + feature = "medium-ethernet" +))] +fn test_raw_socket_with_udp_socket(#[case] medium: Medium) { + use crate::wire::{IpEndpoint, IpVersion, Ipv4Packet, UdpPacket, UdpRepr}; + + static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; + + let (mut iface, mut sockets, _) = setup(medium); + + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + let udp_socket_handle = sockets.add(udp_socket); + + // Bind the socket to port 68 + let socket = sockets.get_mut::(udp_socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + + let packets = 1; + let raw_rx_buffer = + raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); + let raw_tx_buffer = raw::PacketBuffer::new( + vec![raw::PacketMetadata::EMPTY; packets], + vec![0; 48 * packets], + ); + let raw_socket = raw::Socket::new( + IpVersion::Ipv4, + IpProtocol::Udp, + raw_rx_buffer, + raw_tx_buffer, + ); + sockets.add(raw_socket); + + let src_addr = Ipv4Address([127, 0, 0, 2]); + let dst_addr = Ipv4Address([127, 0, 0, 1]); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + let ipv4_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + }; + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; + let frame = { + ipv4_repr.emit( + &mut Ipv4Packet::new_unchecked(&mut bytes), + &ChecksumCapabilities::default(), + ); + udp_repr.emit( + &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), + &src_addr.into(), + &dst_addr.into(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + Ipv4Packet::new_unchecked(&bytes) + }; + + assert_eq!( + iface.inner.process_ipv4( + &mut sockets, + PacketMeta::default(), + &frame, + &mut iface.fragments + ), + None + ); + + // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP + let socket = sockets.get_mut::(udp_socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok(( + &UDP_PAYLOAD[..], + IpEndpoint::new(src_addr.into(), 67).into() + )) + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "socket-udp", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "socket-udp", feature = "medium-ethernet"))] +fn test_icmp_reply_size(#[case] medium: Medium) { + use crate::wire::IPV4_MIN_MTU as MIN_MTU; + const MAX_PAYLOAD_LEN: usize = 528; + + let (mut iface, mut sockets, _device) = setup(medium); + + let src_addr = Ipv4Address([192, 168, 1, 1]); + let dst_addr = Ipv4Address([192, 168, 1, 2]); + + // UDP packet that if not tructated will cause a icmp port unreachable reply + // to exeed the minimum mtu bytes in length. + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + MAX_PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + + let ip_repr = Ipv4Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, + }; + let payload = packet.into_inner(); + + let expected_icmp_repr = Icmpv4Repr::DstUnreachable { + reason: Icmpv4DstUnreachable::PortUnreachable, + header: ip_repr, + data: &payload[..MAX_PAYLOAD_LEN], + }; + + let expected_ip_repr = Ipv4Repr { + src_addr: dst_addr, + dst_addr: src_addr, + next_header: IpProtocol::Icmp, + hop_limit: 64, + payload_len: expected_icmp_repr.buffer_len(), + }; + + assert_eq!( + expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), + MIN_MTU + ); + + assert_eq!( + iface.inner.process_udp( + &mut sockets, + PacketMeta::default(), + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), + Some(IpPacket::Icmpv4((expected_ip_repr, expected_icmp_repr))) + ); +} diff --git a/src/iface/interface/tests/ipv6.rs b/src/iface/interface/tests/ipv6.rs new file mode 100644 index 000000000..920d6da3b --- /dev/null +++ b/src/iface/interface/tests/ipv6.rs @@ -0,0 +1,732 @@ +use super::*; + +fn parse_ipv6(data: &[u8]) -> crate::wire::Result> { + let ipv6_header = Ipv6Packet::new_checked(data)?; + let ipv6 = Ipv6Repr::parse(&ipv6_header)?; + + match ipv6.next_header { + IpProtocol::HopByHop => todo!(), + IpProtocol::Icmp => todo!(), + IpProtocol::Igmp => todo!(), + IpProtocol::Tcp => todo!(), + IpProtocol::Udp => todo!(), + IpProtocol::Ipv6Route => todo!(), + IpProtocol::Ipv6Frag => todo!(), + IpProtocol::Icmpv6 => { + let icmp = Icmpv6Repr::parse( + &ipv6.src_addr.into(), + &ipv6.dst_addr.into(), + &Icmpv6Packet::new_checked(ipv6_header.payload())?, + &Default::default(), + )?; + Ok(IpPacket::Icmpv6((ipv6, icmp))) + } + IpProtocol::Ipv6NoNxt => todo!(), + IpProtocol::Ipv6Opts => todo!(), + IpProtocol::Unknown(_) => todo!(), + } +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn multicast_source_address(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, + ]; + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn hop_by_hop_skip_with_icmp(#[case] medium: Medium) { + // The following contains: + // - IPv6 header + // - Hop-by-hop, with options: + // - PADN (skipped) + // - Unknown option (skipped) + // - ICMP echo request + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x1, 0x0, 0xf, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, + 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + let response = Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 19, + }, + Icmpv6Repr::EchoReply { + ident: 42, + seq_no: 420, + data: b"Lorem Ipsum", + }, + ))); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn hop_by_hop_discard_with_icmp(#[case] medium: Medium) { + // The following contains: + // - IPv6 header + // - Hop-by-hop, with options: + // - PADN (skipped) + // - Unknown option (discard) + // - ICMP echo request + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x1, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, + 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn imcp_empty_echo_request(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x8, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0, 0x84, 0x3c, 0x0, 0x0, 0x0, 0x0, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 8, + }, + Icmpv6Repr::EchoRequest { + ident: 0, + seq_no: 0, + data: b"", + } + ))) + ); + + let response = Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 8, + }, + Icmpv6Repr::EchoReply { + ident: 0, + seq_no: 0, + data: b"", + }, + ))); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn icmp_echo_request(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x13, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, + 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 19, + }, + Icmpv6Repr::EchoRequest { + ident: 42, + seq_no: 420, + data: b"Lorem Ipsum", + } + ))) + ); + + let response = Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 19, + }, + Icmpv6Repr::EchoReply { + ident: 42, + seq_no: 420, + data: b"Lorem Ipsum", + }, + ))); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn icmp_echo_reply_as_input(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x13, 0x3a, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x81, 0x0, 0x2d, 0x56, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x6f, 0x72, 0x65, + 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 19, + }, + Icmpv6Repr::EchoReply { + ident: 0, + seq_no: 0, + data: b"Lorem Ipsum", + } + ))) + ); + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn unknown_proto_with_multicast_dst_address(#[case] medium: Medium) { + // Since the destination address is multicast, we should not answer with an ICMPv6 message. + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, + ]; + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn unknown_proto(#[case] medium: Medium) { + // Since the destination address is multicast, we should not answer with an ICMPv6 message. + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, + ]; + + let response = Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 48, + }, + Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, + pointer: 40, + header: Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 64, + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + }, + data: &[], + }, + ))); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn ndsic_neighbor_advertisement_ethernet(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0x9f, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 255, + next_header: IpProtocol::Icmpv6, + payload_len: 32, + }, + Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]), + lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 1])), + }) + ))) + ); + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); + + assert_eq!( + iface.inner.neighbor_cache.lookup( + &IpAddress::Ipv6(Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002])), + iface.inner.now, + ), + NeighborAnswer::Found(HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[ + 0, 0, 0, 0, 0, 1 + ]))), + ); +} + +#[rstest] +#[case::ethernet(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn ndsic_neighbor_advertisement_ethernet_multicast_addr(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0xa0, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x1, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 255, + next_header: IpProtocol::Icmpv6, + payload_len: 32, + }, + Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]), + lladdr: Some(RawHardwareAddress::from_bytes(&[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + ])), + }) + ))) + ); + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); + + assert_eq!( + iface.inner.neighbor_cache.lookup( + &IpAddress::Ipv6(Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002])), + iface.inner.now, + ), + NeighborAnswer::NotFound, + ); +} + +#[rstest] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn ndsic_neighbor_advertisement_ieee802154(#[case] medium: Medium) { + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x28, 0x3a, 0xff, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x88, 0x0, 0x3b, 0x96, 0x40, 0x0, 0x0, 0x0, 0xfe, 0x80, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ]; + + assert_eq!( + parse_ipv6(&data), + Ok(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 255, + next_header: IpProtocol::Icmpv6, + payload_len: 40, + }, + Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 0x0002]), + lladdr: Some(RawHardwareAddress::from_bytes(&[0, 0, 0, 0, 0, 0, 0, 1])), + }) + ))) + ); + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data).unwrap() + ), + response + ); + + assert_eq!( + iface.inner.neighbor_cache.lookup( + &IpAddress::Ipv6(Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002])), + iface.inner.now, + ), + NeighborAnswer::Found(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes( + &[0, 0, 0, 0, 0, 0, 0, 1] + ))), + ); +} + +#[rstest] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +fn test_handle_valid_ndisc_request(#[case] medium: Medium) { + let (mut iface, mut sockets, _device) = setup(medium); + + let mut eth_bytes = vec![0u8; 86]; + + let local_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1); + let remote_ip_addr = Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2); + let local_hw_addr = EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); + + let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { + target_addr: local_ip_addr, + lladdr: Some(remote_hw_addr.into()), + }); + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: remote_ip_addr, + dst_addr: local_ip_addr.solicited_node(), + next_header: IpProtocol::Icmpv6, + hop_limit: 0xff, + payload_len: solicit.buffer_len(), + }); + + let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); + frame.set_dst_addr(EthernetAddress([0x33, 0x33, 0x00, 0x00, 0x00, 0x00])); + frame.set_src_addr(remote_hw_addr); + frame.set_ethertype(EthernetProtocol::Ipv6); + ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); + solicit.emit( + &remote_ip_addr.into(), + &local_ip_addr.solicited_node().into(), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), + &ChecksumCapabilities::default(), + ); + + let icmpv6_expected = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { + flags: NdiscNeighborFlags::SOLICITED, + target_addr: local_ip_addr, + lladdr: Some(local_hw_addr.into()), + }); + + let ipv6_expected = Ipv6Repr { + src_addr: local_ip_addr, + dst_addr: remote_ip_addr, + next_header: IpProtocol::Icmpv6, + hop_limit: 0xff, + payload_len: icmpv6_expected.buffer_len(), + }; + + // Ensure an Neighbor Solicitation triggers a Neighbor Advertisement + assert_eq!( + iface.inner.process_ethernet( + &mut sockets, + PacketMeta::default(), + frame.into_inner(), + &mut iface.fragments + ), + 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, + &IpAddress::Ipv6(local_ip_addr), + &IpAddress::Ipv6(remote_ip_addr), + &mut iface.fragmenter, + ), + Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) + ); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(feature = "medium-ip")] +#[case(Medium::Ethernet)] +#[cfg(feature = "medium-ethernet")] +#[case(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn test_solicited_node_addrs(#[case] medium: Medium) { + let (mut iface, _, _) = setup(medium); + let mut new_addrs = heapless::Vec::::new(); + new_addrs + .push(IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 1, 2, 0, 2), 64)) + .unwrap(); + new_addrs + .push(IpCidr::new( + IpAddress::v6(0xfe80, 0, 0, 0, 3, 4, 0, 0xffff), + 64, + )) + .unwrap(); + iface.update_ip_addrs(|addrs| { + new_addrs.extend(addrs.to_vec()); + *addrs = new_addrs; + }); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0002))); + assert!(iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0xffff))); + assert!(!iface + .inner + .has_solicited_node(Ipv6Address::new(0xff02, 0, 0, 0, 0, 1, 0xff00, 0x0003))); +} + +#[rstest] +#[case(Medium::Ip)] +#[cfg(all(feature = "socket-udp", feature = "medium-ip"))] +#[case(Medium::Ethernet)] +#[cfg(all(feature = "socket-udp", feature = "medium-ethernet"))] +#[case(Medium::Ieee802154)] +#[cfg(all(feature = "socket-udp", feature = "medium-ieee802154"))] +fn test_icmp_reply_size(#[case] medium: Medium) { + use crate::wire::Icmpv6DstUnreachable; + use crate::wire::IPV6_MIN_MTU as MIN_MTU; + const MAX_PAYLOAD_LEN: usize = 1192; + + let (mut iface, mut sockets, _device) = setup(medium); + + let src_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + let dst_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2); + + // UDP packet that if not tructated will cause a icmp port unreachable reply + // to exeed the minimum mtu bytes in length. + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; + let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); + udp_repr.emit( + &mut packet, + &src_addr.into(), + &dst_addr.into(), + MAX_PAYLOAD_LEN, + |buf| fill_slice(buf, 0x2a), + &ChecksumCapabilities::default(), + ); + + let ip_repr = Ipv6Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Udp, + hop_limit: 64, + payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, + }; + let payload = packet.into_inner(); + + let expected_icmp_repr = Icmpv6Repr::DstUnreachable { + reason: Icmpv6DstUnreachable::PortUnreachable, + header: ip_repr, + data: &payload[..MAX_PAYLOAD_LEN], + }; + + let expected_ip_repr = Ipv6Repr { + src_addr: dst_addr, + dst_addr: src_addr, + next_header: IpProtocol::Icmpv6, + hop_limit: 64, + payload_len: expected_icmp_repr.buffer_len(), + }; + + assert_eq!( + expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), + MIN_MTU + ); + + assert_eq!( + iface.inner.process_udp( + &mut sockets, + PacketMeta::default(), + ip_repr.into(), + udp_repr, + false, + &vec![0x2a; MAX_PAYLOAD_LEN], + payload, + ), + Some(IpPacket::Icmpv6((expected_ip_repr, expected_icmp_repr))) + ); +} diff --git a/src/iface/interface/tests/mod.rs b/src/iface/interface/tests/mod.rs new file mode 100644 index 000000000..c781f0c76 --- /dev/null +++ b/src/iface/interface/tests/mod.rs @@ -0,0 +1,183 @@ +#[cfg(feature = "proto-ipv4")] +mod ipv4; +#[cfg(feature = "proto-ipv6")] +mod ipv6; +#[cfg(feature = "proto-sixlowpan")] +mod sixlowpan; + +#[cfg(feature = "proto-igmp")] +use std::vec::Vec; + +use rstest::*; + +use super::*; + +use crate::iface::Interface; +use crate::phy::{ChecksumCapabilities, Loopback}; +use crate::time::Instant; + +#[allow(unused)] +fn fill_slice(s: &mut [u8], val: u8) { + for x in s.iter_mut() { + *x = val + } +} + +fn setup<'a>(medium: Medium) -> (Interface, SocketSet<'a>, Loopback) { + let mut device = Loopback::new(medium); + + let config = Config::new(match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => HardwareAddress::Ethernet(Default::default()), + #[cfg(feature = "medium-ip")] + Medium::Ip => HardwareAddress::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => HardwareAddress::Ieee802154(Default::default()), + }); + + let mut iface = Interface::new(config, &mut device, Instant::ZERO); + + #[cfg(feature = "proto-ipv4")] + { + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + }); + } + + #[cfg(feature = "proto-ipv6")] + { + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128)) + .unwrap(); + ip_addrs + .push(IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64)) + .unwrap(); + }); + } + + (iface, SocketSet::new(vec![]), device) +} + +#[cfg(feature = "proto-igmp")] +fn recv_all(device: &mut Loopback, timestamp: Instant) -> Vec> { + let mut pkts = Vec::new(); + while let Some((rx, _tx)) = device.receive(timestamp) { + rx.consume(|pkt| { + pkts.push(pkt.to_vec()); + }); + } + pkts +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct MockTxToken; + +impl TxToken for MockTxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut junk = [0; 1536]; + f(&mut junk[..len]) + } +} + +#[test] +#[should_panic(expected = "The hardware address does not match the medium of the interface.")] +#[cfg(all(feature = "medium-ip", feature = "medium-ethernet"))] +fn test_new_panic() { + let mut device = Loopback::new(Medium::Ethernet); + let config = Config::new(HardwareAddress::Ip); + Interface::new(config, &mut device, Instant::ZERO); +} + +#[rstest] +#[cfg(feature = "default")] +fn test_handle_udp_broadcast( + #[values(Medium::Ip, Medium::Ethernet, Medium::Ieee802154)] medium: Medium, +) { + use crate::wire::IpEndpoint; + + static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; + + let (mut iface, mut sockets, _device) = setup(medium); + + let rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + let tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); + + let udp_socket = udp::Socket::new(rx_buffer, tx_buffer); + + let mut udp_bytes = vec![0u8; 13]; + let mut packet = UdpPacket::new_unchecked(&mut udp_bytes); + + let socket_handle = sockets.add(udp_socket); + + #[cfg(feature = "proto-ipv6")] + let src_ip = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] + let src_ip = Ipv4Address::new(0x7f, 0x00, 0x00, 0x02); + + let udp_repr = UdpRepr { + src_port: 67, + dst_port: 68, + }; + + #[cfg(feature = "proto-ipv6")] + let ip_repr = IpRepr::Ipv6(Ipv6Repr { + src_addr: src_ip, + dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 0x40, + }); + #[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))] + let ip_repr = IpRepr::Ipv4(Ipv4Repr { + src_addr: src_ip, + dst_addr: Ipv4Address::BROADCAST, + next_header: IpProtocol::Udp, + payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), + hop_limit: 0x40, + }); + + // Bind the socket to port 68 + let socket = sockets.get_mut::(socket_handle); + assert_eq!(socket.bind(68), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + + udp_repr.emit( + &mut packet, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + UDP_PAYLOAD.len(), + |buf| buf.copy_from_slice(&UDP_PAYLOAD), + &ChecksumCapabilities::default(), + ); + + // Packet should be handled by bound UDP socket + assert_eq!( + iface.inner.process_udp( + &mut sockets, + PacketMeta::default(), + ip_repr, + udp_repr, + false, + &UDP_PAYLOAD, + packet.into_inner(), + ), + None + ); + + // Make sure the payload to the UDP packet processed by process_udp is + // appended to the bound sockets rx_buffer + let socket = sockets.get_mut::(socket_handle); + assert!(socket.can_recv()); + assert_eq!( + socket.recv(), + Ok((&UDP_PAYLOAD[..], IpEndpoint::new(src_ip.into(), 67).into())) + ); +} diff --git a/src/iface/interface/tests/sixlowpan.rs b/src/iface/interface/tests/sixlowpan.rs new file mode 100644 index 000000000..011f45a8b --- /dev/null +++ b/src/iface/interface/tests/sixlowpan.rs @@ -0,0 +1,411 @@ +use super::*; + +#[rstest] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn ieee802154_wrong_pan_id(#[case] medium: Medium) { + let data = [ + 0x41, 0xcc, 0x3b, 0xff, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0x62, 0x3a, + 0xa6, 0x34, 0x57, 0x29, 0x1c, 0x26, + ]; + + let response = None; + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ieee802154( + &mut sockets, + PacketMeta::default(), + &data[..], + &mut iface.fragments + ), + response, + ); +} + +#[rstest] +#[case::ieee802154(Medium::Ieee802154)] +#[cfg(feature = "medium-ieee802154")] +fn icmp_echo_request(#[case] medium: Medium) { + let data = [ + 0x41, 0xcc, 0x3b, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0x62, 0x3a, + 0xa6, 0x34, 0x57, 0x29, 0x1c, 0x26, 0x6a, 0x33, 0x0a, 0x62, 0x17, 0x3a, 0x80, 0x00, 0xb0, + 0xe3, 0x00, 0x04, 0x00, 0x01, 0x82, 0xf2, 0x82, 0x64, 0x00, 0x00, 0x00, 0x00, 0x66, 0x23, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, + ]; + + let response = Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242]), + dst_addr: Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0x241c, 0x2957, 0x34a6, 0x3a62]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 64, + }, + Icmpv6Repr::EchoReply { + ident: 4, + seq_no: 1, + data: &[ + 0x82, 0xf2, 0x82, 0x64, 0x00, 0x00, 0x00, 0x00, 0x66, 0x23, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + ], + }, + ))); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ieee802154( + &mut sockets, + PacketMeta::default(), + &data[..], + &mut iface.fragments + ), + response, + ); +} + +#[test] +#[cfg(feature = "proto-sixlowpan-fragmentation")] +fn test_echo_request_sixlowpan_128_bytes() { + use crate::phy::Checksum; + + let (mut iface, mut sockets, mut device) = setup(Medium::Ieee802154); + // TODO: modify the example, such that we can also test if the checksum is correctly + // computed. + iface.inner.caps.checksum.icmpv6 = Checksum::None; + + assert_eq!(iface.inner.caps.medium, Medium::Ieee802154); + let now = iface.inner.now(); + + iface.inner.neighbor_cache.fill( + Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0]).into(), + HardwareAddress::Ieee802154(Ieee802154Address::default()), + now, + ); + + let mut ieee802154_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(5), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: Some(Ieee802154Pan(0xbeef)), + dst_addr: Some(Ieee802154Address::Extended([ + 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, + ])), + src_pan_id: Some(Ieee802154Pan(0xbeef)), + src_addr: Some(Ieee802154Address::Extended([ + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, + ])), + }; + + // NOTE: this data is retrieved from tests with Contiki-NG + + let request_first_part_packet = SixlowpanFragPacket::new_checked(&[ + 0xc0, 0xb0, 0x00, 0x8e, 0x6a, 0x33, 0x05, 0x25, 0x2c, 0x3a, 0x80, 0x00, 0xe0, 0x71, 0x00, + 0x27, 0x00, 0x02, 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + ]) + .unwrap(); + + let request_first_part_iphc_packet = + SixlowpanIphcPacket::new_checked(request_first_part_packet.payload()).unwrap(); + + let request_first_part_iphc_repr = SixlowpanIphcRepr::parse( + &request_first_part_iphc_packet, + ieee802154_repr.src_addr, + ieee802154_repr.dst_addr, + &iface.inner.sixlowpan_address_context, + ) + .unwrap(); + + assert_eq!( + request_first_part_iphc_repr.src_addr, + Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, + 0x1a, + ]), + ); + assert_eq!( + request_first_part_iphc_repr.dst_addr, + Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, + 0x76, + ]), + ); + + let request_second_part = [ + 0xe0, 0xb0, 0x00, 0x8e, 0x10, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + PacketMeta::default(), + &ieee802154_repr, + &request_first_part_packet.into_inner(), + &mut iface.fragments + ), + None + ); + + ieee802154_repr.sequence_number = Some(6); + + // data that was generated when using `ping -s 128` + let data = &[ + 0xa2, 0xc2, 0x2d, 0x63, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x5e, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ]; + + let result = iface.inner.process_sixlowpan( + &mut sockets, + PacketMeta::default(), + &ieee802154_repr, + &request_second_part, + &mut iface.fragments, + ); + + assert_eq!( + result, + Some(IpPacket::Icmpv6(( + Ipv6Repr { + src_addr: Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x92, 0xfc, 0x48, 0xc2, 0xa4, 0x41, + 0xfc, 0x76, + ]), + dst_addr: Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, + 0xb, 0x1a, + ]), + next_header: IpProtocol::Icmpv6, + payload_len: 136, + hop_limit: 64, + }, + Icmpv6Repr::EchoReply { + ident: 39, + seq_no: 2, + data, + } + ))) + ); + + iface.inner.neighbor_cache.fill( + IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, + ])), + HardwareAddress::Ieee802154(Ieee802154Address::default()), + Instant::now(), + ); + + let tx_token = device.transmit(Instant::now()).unwrap(); + iface.inner.dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + PacketMeta::default(), + result.unwrap(), + &mut iface.fragmenter, + ); + + assert_eq!( + device.queue[0], + &[ + 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb0, 0x5, 0x4e, 0x7a, 0x11, 0x3a, 0x92, 0xfc, 0x48, 0xc2, + 0xa4, 0x41, 0xfc, 0x76, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0xb, 0x1a, 0x81, 0x0, 0x0, + 0x0, 0x0, 0x27, 0x0, 0x2, 0xa2, 0xc2, 0x2d, 0x63, 0x0, 0x0, 0x0, 0x0, 0xd9, 0x5e, 0xc, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, + ] + ); + + iface.poll(Instant::now(), &mut device, &mut sockets); + + assert_eq!( + device.queue[1], + &[ + 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb0, 0x5, 0x4e, 0xf, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, + 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + ] + ); +} + +#[test] +#[cfg(feature = "proto-sixlowpan-fragmentation")] +fn test_sixlowpan_udp_with_fragmentation() { + use crate::phy::Checksum; + + let mut ieee802154_repr = Ieee802154Repr { + frame_type: Ieee802154FrameType::Data, + security_enabled: false, + frame_pending: false, + ack_request: false, + sequence_number: Some(5), + pan_id_compression: true, + frame_version: Ieee802154FrameVersion::Ieee802154_2003, + dst_pan_id: Some(Ieee802154Pan(0xbeef)), + dst_addr: Some(Ieee802154Address::Extended([ + 0x90, 0xfc, 0x48, 0xc2, 0xa4, 0x41, 0xfc, 0x76, + ])), + src_pan_id: Some(Ieee802154Pan(0xbeef)), + src_addr: Some(Ieee802154Address::Extended([ + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, + ])), + }; + + let (mut iface, mut sockets, mut device) = setup(Medium::Ieee802154); + iface.inner.caps.checksum.udp = Checksum::None; + + let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); + let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); + let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + let udp_socket_handle = sockets.add(udp_socket); + + { + let socket = sockets.get_mut::(udp_socket_handle); + assert_eq!(socket.bind(6969), Ok(())); + assert!(!socket.can_recv()); + assert!(socket.can_send()); + } + + let udp_first_part = &[ + 0xc0, 0xbc, 0x00, 0x92, 0x6e, 0x33, 0x07, 0xe7, 0xdc, 0xf0, 0xd3, 0xc9, 0x1b, 0x39, 0xbf, + 0xa0, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, + 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, + 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x49, 0x6e, + 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, 0x73, 0x20, 0x74, 0x6f, 0x72, + 0x74, 0x6f, 0x72, 0x2e, 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + PacketMeta::default(), + &ieee802154_repr, + udp_first_part, + &mut iface.fragments + ), + None + ); + + ieee802154_repr.sequence_number = Some(6); + + let udp_second_part = &[ + 0xe0, 0xbc, 0x00, 0x92, 0x11, 0x64, 0x69, 0x74, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, + 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, + 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, + ]; + + assert_eq!( + iface.inner.process_sixlowpan( + &mut sockets, + PacketMeta::default(), + &ieee802154_repr, + udp_second_part, + &mut iface.fragments + ), + None + ); + + let socket = sockets.get_mut::(udp_socket_handle); + + let udp_data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ +In at rhoncus tortor. Cras blandit tellus diam, varius vestibulum nibh commodo nec."; + assert_eq!( + socket.recv(), + Ok(( + &udp_data[..], + IpEndpoint { + addr: IpAddress::Ipv6(Ipv6Address([ + 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, + 0xb, 0x1a, + ])), + port: 54217, + } + .into() + )) + ); + + let tx_token = device.transmit(Instant::now()).unwrap(); + iface.inner.dispatch_ieee802154( + Ieee802154Address::default(), + tx_token, + PacketMeta::default(), + IpPacket::Udp(( + IpRepr::Ipv6(Ipv6Repr { + src_addr: Ipv6Address::default(), + dst_addr: Ipv6Address::default(), + next_header: IpProtocol::Udp, + payload_len: udp_data.len(), + hop_limit: 64, + }), + UdpRepr { + src_port: 1234, + dst_port: 1234, + }, + udp_data, + )), + &mut iface.fragmenter, + ); + + iface.poll(Instant::now(), &mut device, &mut sockets); + + assert_eq!( + device.queue[0], + &[ + 0x41, 0xcc, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xc0, 0xb4, 0x5, 0x4e, 0x7e, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x4, 0xd2, 0x4, 0xd2, 0xf6, + 0x4d, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, + 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, + 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, + 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x74, 0x20, 0x72, 0x68, 0x6f, 0x6e, 0x63, 0x75, + 0x73, 0x20, 0x74, + ] + ); + + assert_eq!( + device.queue[1], + &[ + 0x41, 0xcc, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xe0, 0xb4, 0x5, 0x4e, 0xf, 0x6f, 0x72, 0x74, 0x6f, 0x72, 0x2e, + 0x20, 0x43, 0x72, 0x61, 0x73, 0x20, 0x62, 0x6c, 0x61, 0x6e, 0x64, 0x69, 0x74, 0x20, + 0x74, 0x65, 0x6c, 0x6c, 0x75, 0x73, 0x20, 0x64, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x76, + 0x61, 0x72, 0x69, 0x75, 0x73, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6c, + 0x75, 0x6d, 0x20, 0x6e, 0x69, 0x62, 0x68, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, + 0x6f, 0x20, 0x6e, 0x65, 0x63, 0x2e, + ] + ); +} From 9cfd5cf7a6daf476973be345290a81a433dcf4d6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 02:10:36 +0200 Subject: [PATCH 566/566] wire: Reexport DnsRcode Fixes #786 --- src/wire/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 3b2c5ffc6..85f7d8b34 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -268,8 +268,8 @@ pub use self::dhcpv4::{ #[cfg(feature = "proto-dns")] pub use self::dns::{ - Flags as DnsFlags, Opcode as DnsOpcode, Packet as DnsPacket, Repr as DnsRepr, - Type as DnsQueryType, + Flags as DnsFlags, Opcode as DnsOpcode, Packet as DnsPacket, Rcode as DnsRcode, + Repr as DnsRepr, Type as DnsQueryType, }; /// Parsing a packet failed.