Reassemble incomming IPv4 packets.

This commit is contained in:
Thibaut Vandervelden 2022-06-08 14:34:12 +02:00
parent 19a78bb577
commit 3065959c70
9 changed files with 680 additions and 341 deletions

View File

@ -43,10 +43,12 @@ verbose = []
"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
"proto-ipv4" = [] "proto-ipv4" = []
"proto-ipv4-fragmentation" = ["proto-ipv4"]
"proto-igmp" = ["proto-ipv4"] "proto-igmp" = ["proto-ipv4"]
"proto-dhcpv4" = ["proto-ipv4"] "proto-dhcpv4" = ["proto-ipv4"]
"proto-ipv6" = [] "proto-ipv6" = []
"proto-sixlowpan" = ["proto-ipv6"] "proto-sixlowpan" = ["proto-ipv6"]
"proto-sixlowpan-fragmentation" = ["proto-sixlowpan"]
"proto-dns" = [] "proto-dns" = []
"socket" = [] "socket" = []
@ -63,7 +65,8 @@ default = [
"std", "log", # needed for `cargo test --no-default-features --features default` :/ "std", "log", # needed for `cargo test --no-default-features --features default` :/
"medium-ethernet", "medium-ip", "medium-ieee802154", "medium-ethernet", "medium-ip", "medium-ieee802154",
"phy-raw_socket", "phy-tuntap_interface", "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", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns",
"async" "async"
] ]
@ -114,10 +117,11 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac
[[example]] [[example]]
name = "sixlowpan" 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]] [[example]]
name = "sixlowpan_benchmark" 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]] [[example]]
name = "dns" name = "dns"

View File

@ -5,6 +5,12 @@ use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::str::{self, FromStr}; 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::iface::{InterfaceBuilder, NeighborCache, Routes, SocketSet};
use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::tcp; use smoltcp::socket::tcp;
@ -31,8 +37,8 @@ fn main() {
let neighbor_cache = NeighborCache::new(BTreeMap::new()); let neighbor_cache = NeighborCache::new(BTreeMap::new());
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]);
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 128]); let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]);
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
@ -44,6 +50,23 @@ fn main() {
let medium = device.capabilities().medium; 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).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 { if medium == Medium::Ethernet {
builder = builder builder = builder
.hardware_addr(ethernet_addr.into()) .hardware_addr(ethernet_addr.into())

View File

@ -6,6 +6,11 @@ use std::fmt::Write;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::str; 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::iface::{InterfaceBuilder, NeighborCache, SocketSet};
use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{tcp, udp}; use smoltcp::socket::{tcp, udp};
@ -27,8 +32,8 @@ fn main() {
let neighbor_cache = NeighborCache::new(BTreeMap::new()); let neighbor_cache = NeighborCache::new(BTreeMap::new());
let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 64]); 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; 128]); 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 udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]);
@ -56,6 +61,23 @@ fn main() {
let medium = device.capabilities().medium; let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); 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 { if medium == Medium::Ethernet {
builder = builder builder = builder
.hardware_addr(ethernet_addr.into()) .hardware_addr(ethernet_addr.into())

View File

@ -3,13 +3,12 @@
use managed::{ManagedMap, ManagedSlice}; use managed::{ManagedMap, ManagedSlice};
use crate::storage::Assembler; use crate::storage::Assembler;
use crate::time::Instant; use crate::time::{Duration, Instant};
use crate::Error; use crate::Error;
use crate::Result; use crate::Result;
/// Holds different fragments of one packet, used for assembling fragmented packets. /// Holds different fragments of one packet, used for assembling fragmented packets.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketAssembler<'a> { pub struct PacketAssembler<'a> {
buffer: ManagedSlice<'a, u8>, buffer: ManagedSlice<'a, u8>,
assembler: AssemblerState, assembler: AssemblerState,
@ -17,14 +16,12 @@ pub struct PacketAssembler<'a> {
/// Holds the state of the assembling of one packet. /// Holds the state of the assembling of one packet.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum AssemblerState { enum AssemblerState {
NotInit, NotInit,
Assembling { Assembling {
assembler: Assembler, assembler: Assembler,
total_size: usize, total_size: Option<usize>,
last_updated: Instant, expires_at: Instant,
started_on: Instant,
offset_correction: isize, offset_correction: isize,
}, },
} }
@ -51,32 +48,71 @@ impl<'a> PacketAssembler<'a> {
/// fragments of a packet. /// fragments of a packet.
pub(crate) fn start( pub(crate) fn start(
&mut self, &mut self,
total_size: usize, total_size: Option<usize>,
start_time: Instant, expires_at: Instant,
offset_correction: isize, offset_correction: isize,
) -> Result<()> { ) -> Result<()> {
match &mut self.buffer { match &mut self.buffer {
ManagedSlice::Borrowed(b) if b.len() < total_size => { ManagedSlice::Borrowed(b) => {
if let Some(total_size) = total_size {
if b.len() < total_size {
return Err(Error::PacketAssemblerBufferTooSmall); return Err(Error::PacketAssemblerBufferTooSmall);
} }
ManagedSlice::Borrowed(_) => (), }
}
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
ManagedSlice::Owned(b) => { ManagedSlice::Owned(b) => {
if let Some(total_size) = total_size {
b.resize(total_size, 0); b.resize(total_size, 0);
} }
} }
}
self.assembler = AssemblerState::Assembling { 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, total_size,
last_updated: start_time, expires_at,
started_on: start_time,
offset_correction, offset_correction,
}; };
Ok(()) 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<Instant> {
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. /// Add a fragment into the packet that is being reassembled.
/// ///
/// # Errors /// # Errors
@ -86,22 +122,31 @@ impl<'a> PacketAssembler<'a> {
/// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing /// - Returns [`Error::PacketAssemblerBufferTooSmall`] when trying to add data into the buffer at a non-existing
/// place. /// place.
/// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data. /// - Returns [`Error::PacketAssemblerOverlap`] when there was an overlap when adding data.
pub(crate) fn add(&mut self, data: &[u8], offset: usize, now: Instant) -> Result<bool> { pub(crate) fn add(&mut self, data: &[u8], offset: usize) -> Result<bool> {
match self.assembler { match self.assembler {
AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit), AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit),
AssemblerState::Assembling { AssemblerState::Assembling {
ref mut assembler, ref mut assembler,
total_size, total_size,
ref mut last_updated,
offset_correction, offset_correction,
.. ..
} => { } => {
let offset = offset as isize + offset_correction; let offset = offset as isize + offset_correction;
let offset = if offset <= 0 { 0 } else { offset as usize }; let offset = if offset <= 0 { 0 } else { offset as usize };
if offset + data.len() > total_size { match &mut self.buffer {
ManagedSlice::Borrowed(b) => {
if offset + data.len() > b.len() {
return Err(Error::PacketAssemblerBufferTooSmall); 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(); let len = data.len();
self.buffer[offset..][..len].copy_from_slice(data); self.buffer[offset..][..len].copy_from_slice(data);
@ -111,7 +156,6 @@ impl<'a> PacketAssembler<'a> {
if overlap { if overlap {
net_debug!("packet was added, but there was an overlap."); net_debug!("packet was added, but there was an overlap.");
} }
*last_updated = now;
self.is_complete() self.is_complete()
} }
// NOTE(thvdveld): hopefully we wont get too many holes errors I guess? // 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::NotInit => return Err(Error::PacketAssemblerNotInit),
AssemblerState::Assembling { total_size, .. } => { AssemblerState::Assembling { total_size, .. } => {
if self.is_complete()? { 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]; let a = &self.buffer[..total_size];
self.assembler = AssemblerState::NotInit; self.assembler = AssemblerState::NotInit;
a a
@ -158,13 +204,10 @@ impl<'a> PacketAssembler<'a> {
assembler, assembler,
total_size, total_size,
.. ..
} => { } => match (total_size, assembler.peek_front()) {
if let Some(front) = assembler.peek_front() { (Some(total_size), Some(front)) => Ok(front == *total_size),
Ok(front == *total_size) _ => Ok(false),
} else { },
Ok(false)
}
}
} }
} }
@ -173,40 +216,20 @@ impl<'a> PacketAssembler<'a> {
self.assembler == AssemblerState::NotInit 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<Instant> {
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<Instant> {
match self.assembler {
AssemblerState::NotInit => Err(Error::PacketAssemblerNotInit),
AssemblerState::Assembling { last_updated, .. } => Ok(last_updated),
}
}
/// Mark this assembler as [`AssemblerState::NotInit`]. /// Mark this assembler as [`AssemblerState::NotInit`].
/// This is then cleaned up by the [`PacketAssemblerSet`]. /// This is then cleaned up by the [`PacketAssemblerSet`].
pub fn mark_discarded(&mut self) { pub fn mark_discarded(&mut self) {
self.assembler = AssemblerState::NotInit; 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`]. /// Set holding multiple [`PacketAssembler`].
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy> { pub struct PacketAssemblerSet<'a, Key: Eq + Ord + Clone + Copy> {
packet_buffer: ManagedSlice<'a, PacketAssembler<'a>>, packet_buffer: ManagedSlice<'a, PacketAssembler<'a>>,
index_buffer: ManagedMap<'a, Key, usize>, 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 { match &mut self.packet_buffer {
ManagedSlice::Borrowed(_) => (), ManagedSlice::Borrowed(_) => (),
#[cfg(any(feature = "std", feature = "alloc"))] #[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 self.packet_buffer
@ -341,7 +364,10 @@ impl<'a, K: Eq + Ord + Clone + Copy> PacketAssemblerSet<'a, K> {
loop { loop {
let mut key = None; let mut key = None;
for (k, i) in self.index_buffer.iter() { 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); key = Some(*k);
break; 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)`. /// Mark all [`PacketAssembler`]s as discarded for which `f` returns `Ok(true)`.
pub fn remove_when( /// This does not remove them from the buffer.
&mut self, pub fn mark_discarded_when<F>(&mut self, f: F) -> Result<()>
f: impl Fn(&mut PacketAssembler<'_>) -> Result<bool>, where
) -> Result<()> { F: Fn(&mut PacketAssembler<'_>) -> Result<bool>,
{
for (_, i) in &mut self.index_buffer.iter() { for (_, i) in &mut self.index_buffer.iter() {
let frag = &mut self.packet_buffer[*i as usize]; let frag = &mut self.packet_buffer[*i as usize];
if f(frag)? { if f(frag)? {
frag.mark_discarded(); frag.mark_discarded();
} }
} }
Ok(())
}
/// Remove all [`PacketAssembler`]s for which `f` returns `Ok(true)`.
pub fn remove_when<F>(&mut self, f: F) -> Result<()>
where
F: Fn(&mut PacketAssembler<'_>) -> Result<bool>,
{
self.mark_discarded_when(f)?;
self.remove_discarded(); self.remove_discarded();
Ok(()) Ok(())
@ -386,7 +423,7 @@ mod tests {
let mut p_assembler = PacketAssembler::new(vec![]); let mut p_assembler = PacketAssembler::new(vec![]);
let data = b"Hello World!"; let data = b"Hello World!";
assert_eq!( assert_eq!(
p_assembler.add(&data[..], data.len(), Instant::now()), p_assembler.add(&data[..], data.len()),
Err(Error::PacketAssemblerNotInit) Err(Error::PacketAssemblerNotInit)
); );
@ -403,14 +440,14 @@ mod tests {
let mut p_assembler = PacketAssembler::new(&mut storage[..]); let mut p_assembler = PacketAssembler::new(&mut storage[..]);
assert_eq!( assert_eq!(
p_assembler.start(2, Instant::now(), 0), p_assembler.start(Some(2), Instant::from_secs(0), 0),
Err(Error::PacketAssemblerBufferTooSmall) 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!"; let data = b"Hello World!";
assert_eq!( assert_eq!(
p_assembler.add(&data[..], data.len(), Instant::now()), p_assembler.add(&data[..], data.len()),
Err(Error::PacketAssemblerBufferTooSmall) Err(Error::PacketAssemblerBufferTooSmall)
); );
} }
@ -420,12 +457,14 @@ mod tests {
let mut storage = [0u8; 5]; let mut storage = [0u8; 5];
let mut p_assembler = PacketAssembler::new(&mut storage[..]); 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"; 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] #[test]
@ -435,18 +474,40 @@ mod tests {
let data = b"Hello World!"; 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!( assert_eq!(
p_assembler.assemble(), p_assembler.assemble(),
Err(Error::PacketAssemblerIncomplete) 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 p_assembler
.add(b"World!", b"Hello ".len(), Instant::now()) .start(Some(data.len()), Instant::from_secs(0), 0)
.unwrap(); .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!"[..])); assert_eq!(p_assembler.assemble(), Ok(&b"Hello World!"[..]));
} }
@ -494,7 +555,7 @@ mod tests {
set.reserve_with_key(&key).unwrap(); set.reserve_with_key(&key).unwrap();
set.get_packet_assembler_mut(&key) set.get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.start(0, Instant::now(), 0) .start(Some(0), Instant::from_secs(0), 0)
.unwrap(); .unwrap();
set.get_assembled_packet(&key).unwrap(); set.get_assembled_packet(&key).unwrap();
@ -502,7 +563,7 @@ mod tests {
set.reserve_with_key(&key).unwrap(); set.reserve_with_key(&key).unwrap();
set.get_packet_assembler_mut(&key) set.get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.start(0, Instant::now(), 0) .start(Some(0), Instant::from_secs(0), 0)
.unwrap(); .unwrap();
set.get_assembled_packet(&key).unwrap(); set.get_assembled_packet(&key).unwrap();
@ -510,7 +571,7 @@ mod tests {
set.reserve_with_key(&key).unwrap(); set.reserve_with_key(&key).unwrap();
set.get_packet_assembler_mut(&key) set.get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.start(0, Instant::now(), 0) .start(Some(0), Instant::from_secs(0), 0)
.unwrap(); .unwrap();
set.get_assembled_packet(&key).unwrap(); set.get_assembled_packet(&key).unwrap();
} }

View File

@ -5,7 +5,7 @@
use core::cmp; use core::cmp;
use managed::{ManagedMap, ManagedSlice}; use managed::{ManagedMap, ManagedSlice};
#[cfg(feature = "proto-sixlowpan")] #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
use super::fragmentation::PacketAssemblerSet; use super::fragmentation::PacketAssemblerSet;
use super::socket_set::SocketSet; use super::socket_set::SocketSet;
use crate::iface::Routes; use crate::iface::Routes;
@ -23,19 +23,27 @@ use crate::wire::*;
use crate::{Error, Result}; use crate::{Error, Result};
pub(crate) struct FragmentsBuffer<'a> { 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>, 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 ()>, _lifetime: core::marker::PhantomData<&'a ()>,
} }
pub(crate) struct OutPackets<'a> { pub(crate) struct OutPackets<'a> {
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_out_packet: SixlowpanOutPacket<'a>, sixlowpan_out_packet: SixlowpanOutPacket<'a>,
#[cfg(not(feature = "proto-sixlowpan"))]
#[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
_lifetime: core::marker::PhantomData<&'a ()>, _lifetime: core::marker::PhantomData<&'a ()>,
} }
#[allow(unused)]
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan")]
pub(crate) struct SixlowpanOutPacket<'a> { pub(crate) struct SixlowpanOutPacket<'a> {
/// The buffer that holds the unfragmented 6LoWPAN packet. /// The buffer that holds the unfragmented 6LoWPAN packet.
@ -59,7 +67,7 @@ pub(crate) struct SixlowpanOutPacket<'a> {
ll_src_addr: Ieee802154Address, ll_src_addr: Ieee802154Address,
} }
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
impl<'a> SixlowpanOutPacket<'a> { impl<'a> SixlowpanOutPacket<'a> {
pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self {
Self { Self {
@ -121,7 +129,7 @@ pub struct InterfaceInner<'a> {
sequence_no: u8, sequence_no: u8,
#[cfg(feature = "medium-ieee802154")] #[cfg(feature = "medium-ieee802154")]
pan_id: Option<Ieee802154Pan>, pan_id: Option<Ieee802154Pan>,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
tag: u16, tag: u16,
ip_addrs: ManagedSlice<'a, IpCidr>, ip_addrs: ManagedSlice<'a, IpCidr>,
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
@ -151,9 +159,13 @@ pub struct InterfaceBuilder<'a> {
#[cfg(feature = "proto-igmp")] #[cfg(feature = "proto-igmp")]
ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>, ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>,
random_seed: u64, random_seed: u64,
#[cfg(feature = "proto-sixlowpan")]
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_fragments: Option<PacketAssemblerSet<'a, Ipv4FragKey>>,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_fragments: Option<PacketAssemblerSet<'a, SixlowpanFragKey>>, sixlowpan_fragments: Option<PacketAssemblerSet<'a, SixlowpanFragKey>>,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_out_buffer: Option<ManagedSlice<'a, u8>>, sixlowpan_out_buffer: Option<ManagedSlice<'a, u8>>,
} }
@ -167,6 +179,8 @@ impl<'a> InterfaceBuilder<'a> {
``` ```
# use std::collections::BTreeMap; # use std::collections::BTreeMap;
#[cfg(feature = "proto-ipv4-fragmentation")]
use smoltcp::iface::FragmentsCache;
use smoltcp::iface::{InterfaceBuilder, NeighborCache}; use smoltcp::iface::{InterfaceBuilder, NeighborCache};
# use smoltcp::phy::{Loopback, Medium}; # use smoltcp::phy::{Loopback, Medium};
use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress};
@ -177,13 +191,20 @@ let hw_addr = // ...
# EthernetAddress::default(); # EthernetAddress::default();
let neighbor_cache = // ... let neighbor_cache = // ...
# NeighborCache::new(BTreeMap::new()); # NeighborCache::new(BTreeMap::new());
# #[cfg(feature = "proto-ipv4-fragmentation")]
# let ipv4_frag_cache = // ...
# FragmentsCache::new(vec![], BTreeMap::new());
let ip_addrs = // ... let ip_addrs = // ...
# []; # [];
let iface = InterfaceBuilder::new() let builder = InterfaceBuilder::new()
.hardware_addr(hw_addr.into()) .hardware_addr(hw_addr.into())
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs);
.finalize(&mut device);
# #[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 []), ipv4_multicast_groups: ManagedMap::Borrowed(&mut []),
random_seed: 0, random_seed: 0,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_fragments: None,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_fragments: None, sixlowpan_fragments: None,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_out_buffer: None, sixlowpan_out_buffer: None,
} }
} }
@ -321,7 +345,13 @@ let iface = InterfaceBuilder::new()
self 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( pub fn sixlowpan_fragments_cache(
mut self, mut self,
storage: PacketAssemblerSet<'a, SixlowpanFragKey>, storage: PacketAssemblerSet<'a, SixlowpanFragKey>,
@ -330,7 +360,7 @@ let iface = InterfaceBuilder::new()
self self
} }
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
pub fn sixlowpan_out_packet_cache<T>(mut self, storage: T) -> Self pub fn sixlowpan_out_packet_cache<T>(mut self, storage: T) -> Self
where where
T: Into<ManagedSlice<'a, u8>>, T: Into<ManagedSlice<'a, u8>>,
@ -422,22 +452,29 @@ let iface = InterfaceBuilder::new()
Interface { Interface {
fragments: FragmentsBuffer { 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: self
.sixlowpan_fragments .sixlowpan_fragments
.expect("Cache for incoming 6LoWPAN fragments is required"), .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, _lifetime: core::marker::PhantomData,
}, },
out_packets: OutPackets { out_packets: OutPackets {
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_out_packet: SixlowpanOutPacket::new( sixlowpan_out_packet: SixlowpanOutPacket::new(
self.sixlowpan_out_buffer 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, _lifetime: core::marker::PhantomData,
}, },
inner: InterfaceInner { inner: InterfaceInner {
@ -459,7 +496,7 @@ let iface = InterfaceBuilder::new()
sequence_no, sequence_no,
#[cfg(feature = "medium-ieee802154")] #[cfg(feature = "medium-ieee802154")]
pan_id: self.pan_id, pan_id: self.pan_id,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
tag, tag,
rand, rand,
}, },
@ -760,12 +797,11 @@ impl<'a> Interface<'a> {
pub fn ipv4_addr(&self) -> Option<Ipv4Address> { pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
self.ip_addrs() self.ip_addrs()
.iter() .iter()
.filter_map(|cidr| match cidr.address() { .find_map(|cidr| match cidr.address() {
IpAddress::Ipv4(addr) => Some(addr), IpAddress::Ipv4(addr) => Some(addr),
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
_ => None, _ => None,
}) })
.next()
} }
/// Update the IP addresses of the interface. /// Update the IP addresses of the interface.
@ -825,35 +861,33 @@ impl<'a> Interface<'a> {
{ {
self.inner.now = timestamp; self.inner.now = timestamp;
let mut readiness_may_have_changed = false; #[cfg(feature = "proto-ipv4-fragmentation")]
if let Err(e) = self
#[cfg(feature = "proto-sixlowpan")] .fragments
.ipv4_fragments
.remove_when(|frag| Ok(timestamp >= frag.expires_at()?))
{ {
let SixlowpanOutPacket { return Err(e);
packet_len,
sent_bytes,
..
} = &self.out_packets.sixlowpan_out_packet;
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);
}
} }
return Ok(true); #[cfg(feature = "proto-sixlowpan-fragmentation")]
if let Err(e) = self
.fragments
.sixlowpan_fragments
.remove_when(|frag| Ok(timestamp >= frag.expires_at()?))
{
return Err(e);
} }
#[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 { loop {
let processed_any = self.socket_ingress(device, sockets); let processed_any = self.socket_ingress(device, sockets);
let emitted_any = self.socket_egress(device, sockets); let emitted_any = self.socket_egress(device, sockets);
@ -923,15 +957,16 @@ impl<'a> Interface<'a> {
let mut processed_any = false; let mut processed_any = false;
let Self { let Self {
inner, inner,
fragments: _fragments, fragments: ref mut _fragments,
out_packets: _out_packets, out_packets: _out_packets,
} = self; } = self;
while let Some((rx_token, tx_token)) = device.receive() { while let Some((rx_token, tx_token)) = device.receive() {
let res = rx_token.consume(inner.now, |frame| { let res = rx_token.consume(inner.now, |frame| {
match inner.caps.medium { match inner.caps.medium {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
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) { if let Err(err) = inner.dispatch(tx_token, packet) {
net_debug!("Failed to send response: {}", err); net_debug!("Failed to send response: {}", err);
} }
@ -939,7 +974,7 @@ impl<'a> Interface<'a> {
} }
#[cfg(feature = "medium-ip")] #[cfg(feature = "medium-ip")]
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) { if let Err(err) = inner.dispatch_ip(tx_token, packet, None) {
net_debug!("Failed to send response: {}", err); net_debug!("Failed to send response: {}", err);
} }
@ -947,11 +982,8 @@ impl<'a> Interface<'a> {
} }
#[cfg(feature = "medium-ieee802154")] #[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => { Medium::Ieee802154 => {
if let Some(packet) = inner.process_ieee802154( if let Some(packet) = inner.process_ieee802154(sockets, &frame, _fragments)
sockets, {
&frame,
&mut _fragments.sixlowpan_fragments,
) {
if let Err(err) = if let Err(err) =
inner.dispatch_ip(tx_token, packet, Some(_out_packets)) 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| { let mut respond = |inner: &mut InterfaceInner, response: IpPacket| {
neighbor_addr = Some(response.ip_repr().dst_addr()); neighbor_addr = Some(response.ip_repr().dst_addr());
match device.transmit().ok_or(Error::Exhausted) { match device.transmit().ok_or(Error::Exhausted) {
Ok(t) => { Ok(_t) => {
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
if let Err(_e) = inner.dispatch_ip(t, response, Some(_out_packets)) { if let Err(_e) = inner.dispatch_ip(_t, response, Some(_out_packets)) {
net_debug!("failed to dispatch IP: {}", _e); net_debug!("failed to dispatch IP: {}", _e);
} }
#[cfg(not(feature = "proto-sixlowpan"))] #[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
if let Err(_e) = inner.dispatch_ip(t, response, None) { if let Err(_e) = inner.dispatch_ip(_t, response, None) {
net_debug!("failed to dispatch IP: {}", _e); net_debug!("failed to dispatch IP: {}", _e);
} }
emitted_any = true; emitted_any = true;
@ -1140,6 +1172,41 @@ impl<'a> Interface<'a> {
_ => Ok(false), _ => Ok(false),
} }
} }
#[cfg(feature = "proto-sixlowpan-fragmentation")]
fn sixlowpan_egress<D>(&mut self, device: &mut D) -> Result<bool>
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> { impl<'a> InterfaceInner<'a> {
@ -1256,7 +1323,7 @@ impl<'a> InterfaceInner<'a> {
#[cfg(feature = "medium-ieee802154")] #[cfg(feature = "medium-ieee802154")]
sequence_no: 1, sequence_no: 1,
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
tag: 1, tag: 1,
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
@ -1301,7 +1368,7 @@ impl<'a> InterfaceInner<'a> {
no no
} }
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan-fragmentation")]
fn get_sixlowpan_fragment_tag(&mut self) -> u16 { fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
let tag = self.tag; let tag = self.tag;
self.tag = self.tag.wrapping_add(1); self.tag = self.tag.wrapping_add(1);
@ -1336,14 +1403,11 @@ impl<'a> InterfaceInner<'a> {
/// Get the first IPv4 address of the interface. /// Get the first IPv4 address of the interface.
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
pub fn ipv4_address(&self) -> Option<Ipv4Address> { pub fn ipv4_address(&self) -> Option<Ipv4Address> {
self.ip_addrs self.ip_addrs.iter().find_map(|addr| match *addr {
.iter()
.filter_map(|addr| match *addr {
IpCidr::Ipv4(cidr) => Some(cidr.address()), IpCidr::Ipv4(cidr) => Some(cidr.address()),
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
IpCidr::Ipv6(_) => None, IpCidr::Ipv6(_) => None,
}) })
.next()
} }
/// Check whether the interface listens to given destination multicast IP address. /// Check whether the interface listens to given destination multicast IP address.
@ -1367,6 +1431,7 @@ impl<'a> InterfaceInner<'a> {
&mut self, &mut self,
sockets: &mut SocketSet, sockets: &mut SocketSet,
frame: &'frame T, frame: &'frame T,
_fragments: &'frame mut FragmentsBuffer<'a>,
) -> Option<EthernetPacket<'frame>> { ) -> Option<EthernetPacket<'frame>> {
let eth_frame = check!(EthernetFrame::new_checked(frame)); let eth_frame = check!(EthernetFrame::new_checked(frame));
@ -1384,8 +1449,14 @@ impl<'a> InterfaceInner<'a> {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
EthernetProtocol::Ipv4 => { EthernetProtocol::Ipv4 => {
let ipv4_packet = check!(Ipv4Packet::new_checked(eth_frame.payload())); 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")] #[cfg(feature = "proto-ipv6")]
EthernetProtocol::Ipv6 => { EthernetProtocol::Ipv6 => {
@ -1403,12 +1474,20 @@ impl<'a> InterfaceInner<'a> {
&mut self, &mut self,
sockets: &mut SocketSet, sockets: &mut SocketSet,
ip_payload: &'frame T, ip_payload: &'frame T,
_fragments: &'frame mut FragmentsBuffer<'a>,
) -> Option<IpPacket<'frame>> { ) -> Option<IpPacket<'frame>> {
match IpVersion::of_packet(ip_payload.as_ref()) { match IpVersion::of_packet(ip_payload.as_ref()) {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
Ok(IpVersion::Ipv4) => { Ok(IpVersion::Ipv4) => {
let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload)); 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")] #[cfg(feature = "proto-ipv6")]
Ok(IpVersion::Ipv6) => { Ok(IpVersion::Ipv6) => {
@ -1425,7 +1504,7 @@ impl<'a> InterfaceInner<'a> {
&mut self, &mut self,
sockets: &mut SocketSet, sockets: &mut SocketSet,
sixlowpan_payload: &'payload T, sixlowpan_payload: &'payload T,
fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, _fragments: &'output mut FragmentsBuffer<'a>,
) -> Option<IpPacket<'output>> { ) -> Option<IpPacket<'output>> {
let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload)); let ieee802154_frame = check!(Ieee802154Frame::new_checked(sixlowpan_payload));
let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame)); let ieee802154_repr = check!(Ieee802154Repr::parse(&ieee802154_frame));
@ -1449,7 +1528,15 @@ impl<'a> InterfaceInner<'a> {
} }
match ieee802154_frame.payload() { 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, None => None,
} }
} }
@ -1460,15 +1547,18 @@ impl<'a> InterfaceInner<'a> {
sockets: &mut SocketSet, sockets: &mut SocketSet,
ieee802154_repr: &Ieee802154Repr, ieee802154_repr: &Ieee802154Repr,
payload: &'payload T, payload: &'payload T,
fragments: &'output mut PacketAssemblerSet<'a, SixlowpanFragKey>, _fragments: Option<&'output mut PacketAssemblerSet<'a, SixlowpanFragKey>>,
) -> Option<IpPacket<'output>> { ) -> Option<IpPacket<'output>> {
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)) { let payload = match check!(SixlowpanPacket::dispatch(payload)) {
#[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
SixlowpanPacket::FragmentHeader => { 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, // We have a fragment header, which means we cannot process the 6LoWPAN packet,
// unless we have a complete one after processing this fragment. // unless we have a complete one after processing this fragment.
let frag = check!(SixlowpanFragPacket::new_checked(payload)); 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 // We also pass the header size, since this is needed when other fragments
// (other than the first one) are added. // (other than the first one) are added.
check!(check!(fragments.reserve_with_key(&key)).start( check!(check!(fragments.reserve_with_key(&key)).start(
Some(
frag.datagram_size() as usize - uncompressed_header_size frag.datagram_size() as usize - uncompressed_header_size
+ compressed_header_size, + compressed_header_size
self.now, ),
self.now + Duration::from_secs(60),
-((uncompressed_header_size - compressed_header_size) as isize), -((uncompressed_header_size - compressed_header_size) as isize),
)); ));
} }
let frags = check!(fragments.get_packet_assembler_mut(&key)); 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"); net_trace!("6LoWPAN: received packet fragment");
// Add the fragment to the packet assembler. // Add the fragment to the packet assembler.
match frags.add(frag.payload(), offset, self.now) { match frags.add(frag.payload(), offset) {
Ok(true) => { Ok(true) => {
net_trace!("6LoWPAN: fragmented packet now complete"); net_trace!("6LoWPAN: fragmented packet now complete");
check!(fragments.get_assembled_packet(&key)) check!(fragments.get_assembled_packet(&key))
@ -1830,22 +1916,76 @@ impl<'a> InterfaceInner<'a> {
} }
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized>( fn process_ipv4<'output, 'payload: 'output, T: AsRef<[u8]> + ?Sized>(
&mut self, &mut self,
sockets: &mut SocketSet, sockets: &mut SocketSet,
ipv4_packet: &Ipv4Packet<&'frame T>, ipv4_packet: &Ipv4Packet<&'payload T>,
) -> Option<IpPacket<'frame>> { _fragments: Option<&'output mut PacketAssemblerSet<'a, Ipv4FragKey>>,
) -> Option<IpPacket<'output>> {
let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum)); let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
if !self.is_unicast_v4(ipv4_repr.src_addr) { if !self.is_unicast_v4(ipv4_repr.src_addr) {
// Discard packets with non-unicast source addresses. // Discard packets with non-unicast source addresses.
net_debug!("non-unicast source address"); net_debug!("non-unicast source address");
return None; 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_payload = ipv4_packet.payload();
let ip_repr = IpRepr::Ipv4(ipv4_repr);
#[cfg(feature = "socket-raw")] #[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(sockets, &ip_repr, ip_payload);
#[cfg(not(feature = "socket-raw"))] #[cfg(not(feature = "socket-raw"))]
@ -1862,8 +2002,7 @@ impl<'a> InterfaceInner<'a> {
{ {
if let Some(dhcp_socket) = sockets if let Some(dhcp_socket) = sockets
.items_mut() .items_mut()
.filter_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket)) .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket))
.next()
{ {
let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr()); let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
let udp_repr = check!(UdpRepr::parse( 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<Tx: TxToken>( fn dispatch_ieee802154<Tx: TxToken>(
&mut self, &mut self,
ll_dst_a: Ieee802154Address, ll_dst_a: Ieee802154Address,
ip_repr: &IpRepr, ip_repr: &IpRepr,
tx_token: Tx, tx_token: Tx,
packet: IpPacket, packet: IpPacket,
out_packet: Option<&mut OutPackets>, _out_packet: Option<&mut OutPackets>,
) -> Result<()> { ) -> Result<()> {
// We first need to convert the IPv6 packet to a 6LoWPAN compressed packet. // 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 // 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. // We need to know this, such that we know when to do the fragmentation.
let mut total_size = 0; let mut total_size = 0;
total_size += iphc_repr.buffer_len(); 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)] #[allow(unreachable_patterns)]
match packet { match packet {
#[cfg(feature = "socket-udp")] #[cfg(feature = "socket-udp")]
IpPacket::Udp((_, udpv6_repr, payload)) => { IpPacket::Udp((_, udpv6_repr, payload)) => {
let udp_repr = SixlowpanUdpNhcRepr(udpv6_repr); 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(); total_size += udp_repr.header_len() + payload.len();
} }
#[cfg(feature = "socket-tcp")] #[cfg(feature = "socket-tcp")]
@ -2822,6 +2961,8 @@ impl<'a> InterfaceInner<'a> {
let ieee_len = ieee_repr.buffer_len(); let ieee_len = ieee_repr.buffer_len();
if total_size + ieee_len > 125 { 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. // 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 `out_packet.buffer` from the interface.
// After emitting everything into that buffer, we send the first fragment heere. // After emitting everything into that buffer, we send the first fragment heere.
@ -2840,7 +2981,7 @@ impl<'a> InterfaceInner<'a> {
ll_dst_addr, ll_dst_addr,
ll_src_addr, ll_src_addr,
.. ..
} = &mut out_packet.unwrap().sixlowpan_out_packet; } = &mut _out_packet.unwrap().sixlowpan_out_packet;
*ll_dst_addr = ll_dst_a; *ll_dst_addr = ll_dst_a;
*ll_src_addr = ll_src_a; *ll_src_addr = ll_src_a;
@ -2869,7 +3010,8 @@ impl<'a> InterfaceInner<'a> {
} }
#[cfg(feature = "socket-tcp")] #[cfg(feature = "socket-tcp")]
IpPacket::Tcp((_, tcp_repr)) => { 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( tcp_repr.emit(
&mut tcp_packet, &mut tcp_packet,
&iphc_repr.src_addr.into(), &iphc_repr.src_addr.into(),
@ -2919,9 +3061,9 @@ impl<'a> InterfaceInner<'a> {
// in multiples of 8 octets. This is explained in [RFC 4944 § 5.3]. // 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 // [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) let frag1_size = ((125 - ieee_len - frag1.buffer_len() - _compressed_headers_len)
& 0xffff_fff8) & 0xffff_fff8)
+ compressed_headers_len; + _compressed_headers_len;
*fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8; *fragn_size = (125 - ieee_len - fragn.buffer_len()) & 0xffff_fff8;
*sent_bytes = frag1_size; *sent_bytes = frag1_size;
@ -2931,7 +3073,8 @@ impl<'a> InterfaceInner<'a> {
ieee_len + frag1.buffer_len() + frag1_size, ieee_len + frag1.buffer_len() + frag1_size,
|mut tx_buf| { |mut tx_buf| {
// Add the IEEE header. // 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); ieee_repr.emit(&mut ieee_packet);
tx_buf = &mut tx_buf[ieee_len..]; tx_buf = &mut tx_buf[ieee_len..];
@ -2946,6 +3089,11 @@ impl<'a> InterfaceInner<'a> {
Ok(()) Ok(())
}, },
) )
} else {
net_debug!("Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support.");
Ok(())
}
}
} else { } else {
// We don't need fragmentation, so we emit everything to the TX token. // 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(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<Tx: TxToken>( fn dispatch_ieee802154_out_packet<Tx: TxToken>(
&mut self, &mut self,
tx_token: Tx, tx_token: Tx,
@ -3162,6 +3313,11 @@ mod test {
]; ];
let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); 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")] #[cfg(feature = "proto-igmp")]
let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new());
let iface = iface_builder.finalize(&mut device); 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), IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64),
]; ];
#[cfg(feature = "proto-sixlowpan")]
let iface_builder = InterfaceBuilder::new() let iface_builder = InterfaceBuilder::new()
.hardware_addr(EthernetAddress::default().into()) .hardware_addr(EthernetAddress::default().into())
.neighbor_cache(NeighborCache::new(BTreeMap::new())) .neighbor_cache(NeighborCache::new(BTreeMap::new()))
.sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new()))
.sixlowpan_out_packet_cache(vec![])
.ip_addrs(ip_addrs); .ip_addrs(ip_addrs);
#[cfg(not(feature = "proto-sixlowpan"))] #[cfg(feature = "proto-sixlowpan-fragmentation")]
let iface_builder = InterfaceBuilder::new() let iface_builder = iface_builder
.hardware_addr(EthernetAddress::default().into()) .sixlowpan_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new()))
.neighbor_cache(NeighborCache::new(BTreeMap::new())) .sixlowpan_out_packet_cache(vec![]);
.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")] #[cfg(feature = "proto-igmp")]
let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); 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 // Ensure that the unknown protocol frame does not trigger an
// ICMP error response when the destination address is a // ICMP error response when the destination address is a
// broadcast address // 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] #[test]
@ -3339,8 +3506,20 @@ mod test {
// Ensure that the unknown protocol triggers an error response. // Ensure that the unknown protocol triggers an error response.
// And we correctly handle no payload. // And we correctly handle no payload.
#[cfg(not(feature = "proto-ipv4-fragmentation"))]
assert_eq!( 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) Some(expected_repr)
); );
} }
@ -3632,8 +3811,19 @@ mod test {
}; };
let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr)); let expected_packet = IpPacket::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
#[cfg(not(feature = "proto-ipv4-fragmentation"))]
assert_eq!( assert_eq!(
iface.inner.process_ipv4(&mut sockets, &frame), 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) Some(expected_packet)
); );
} }
@ -3682,8 +3872,8 @@ mod test {
); );
#[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
let ip_repr = Ipv4Repr { let ip_repr = Ipv4Repr {
src_addr: src_addr, src_addr,
dst_addr: dst_addr, dst_addr,
next_header: IpProtocol::Udp, next_header: IpProtocol::Udp,
hop_limit: 64, hop_limit: 64,
payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN,
@ -3788,7 +3978,7 @@ mod test {
assert_eq!( assert_eq!(
iface iface
.inner .inner
.process_ethernet(&mut sockets, frame.into_inner()), .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply, operation: ArpOperation::Reply,
source_hardware_addr: local_hw_addr, source_hardware_addr: local_hw_addr,
@ -3863,7 +4053,7 @@ mod test {
assert_eq!( assert_eq!(
iface iface
.inner .inner
.process_ethernet(&mut sockets, frame.into_inner()), .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
Some(EthernetPacket::Ip(IpPacket::Icmpv6(( Some(EthernetPacket::Ip(IpPacket::Icmpv6((
ipv6_expected, ipv6_expected,
icmpv6_expected icmpv6_expected
@ -3910,7 +4100,7 @@ mod test {
assert_eq!( assert_eq!(
iface iface
.inner .inner
.process_ethernet(&mut sockets, frame.into_inner()), .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
None None
); );
@ -3962,7 +4152,7 @@ mod test {
assert_eq!( assert_eq!(
iface iface
.inner .inner
.process_ethernet(&mut sockets, frame.into_inner()), .process_ethernet(&mut sockets, frame.into_inner(), &mut iface.fragments),
Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply, operation: ArpOperation::Reply,
source_hardware_addr: local_hw_addr, source_hardware_addr: local_hw_addr,
@ -4289,8 +4479,8 @@ mod test {
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
); );
let ipv4_repr = Ipv4Repr { let ipv4_repr = Ipv4Repr {
src_addr: src_addr, src_addr,
dst_addr: dst_addr, dst_addr,
next_header: IpProtocol::Udp, next_header: IpProtocol::Udp,
hop_limit: 64, hop_limit: 64,
payload_len: udp_repr.header_len() + PAYLOAD_LEN, payload_len: udp_repr.header_len() + PAYLOAD_LEN,
@ -4314,7 +4504,17 @@ mod test {
Ipv4Packet::new_unchecked(&bytes) 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] #[test]
@ -4370,8 +4570,8 @@ mod test {
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
); );
let ipv4_repr = Ipv4Repr { let ipv4_repr = Ipv4Repr {
src_addr: src_addr, src_addr,
dst_addr: dst_addr, dst_addr,
next_header: IpProtocol::Udp, next_header: IpProtocol::Udp,
hop_limit: 64, hop_limit: 64,
payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(),
@ -4396,7 +4596,17 @@ mod test {
Ipv4Packet::new_unchecked(&bytes) 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 // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP
let socket = sockets.get_mut::<udp::Socket>(udp_socket_handle); let socket = sockets.get_mut::<udp::Socket>(udp_socket_handle);

View File

@ -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. 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 fragmentation;
mod interface; mod interface;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[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 self::route::{Route, Routes};
pub use socket_set::{SocketHandle, SocketSet, SocketStorage}; 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::fragmentation::{PacketAssembler, PacketAssemblerSet as FragmentsCache};
pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context}; pub use self::interface::{Interface, InterfaceBuilder, InterfaceInner as Context};

View File

@ -21,6 +21,13 @@ pub use super::IpProtocol as Protocol;
// accept a packet of the following size. // accept a packet of the following size.
pub const MIN_MTU: usize = 576; 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. /// A four-octet IPv4 address.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Address(pub [u8; 4]); pub struct Address(pub [u8; 4]);
@ -443,6 +450,15 @@ impl<T: AsRef<[u8]>> Packet<T> {
let data = self.buffer.as_ref(); let data = self.buffer.as_ref();
checksum::data(&data[..self.header_len() as usize]) == !0 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> { impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
@ -617,15 +633,14 @@ impl Repr {
if checksum_caps.ipv4.rx() && !packet.verify_checksum() { if checksum_caps.ipv4.rx() && !packet.verify_checksum() {
return Err(Error); return Err(Error);
} }
#[cfg(not(feature = "proto-ipv4-fragmentation"))]
// We do not support fragmentation. // We do not support fragmentation.
if packet.more_frags() || packet.frag_offset() != 0 { if packet.more_frags() || packet.frag_offset() != 0 {
return Err(Error); 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; 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 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 ECN values are acceptable, since ECN requires opt-in from both endpoints.
@ -634,7 +649,7 @@ impl Repr {
src_addr: packet.src_addr(), src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(), dst_addr: packet.dst_addr(),
next_header: packet.next_header(), next_header: packet.next_header(),
payload_len: payload_len, payload_len,
hop_limit: packet.hop_limit(), hop_limit: packet.hop_limit(),
}) })
} }
@ -749,10 +764,21 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) { Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) {
Err(_) => return Ok(()), Err(_) => return Ok(()),
Ok(ip_repr) => { Ok(ip_repr) => {
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)?; write!(f, "{}{}", indent, ip_repr)?;
format_checksum(f, ip_packet.verify_checksum())?; format_checksum(f, ip_packet.verify_checksum())?;
(ip_repr, ip_packet.payload()) (ip_repr, ip_packet.payload())
} }
}
}, },
}; };

View File

@ -169,8 +169,8 @@ pub use self::ip::{
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
pub use self::ipv4::{ pub use self::ipv4::{
Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr, Address as Ipv4Address, Cidr as Ipv4Cidr, Key as Ipv4FragKey, Packet as Ipv4Packet,
HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
}; };
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]

View File

@ -359,6 +359,7 @@ pub mod frag {
} }
impl Repr { impl Repr {
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub(crate) fn set_offset(&mut self, value: u8) { pub(crate) fn set_offset(&mut self, value: u8) {
match self { match self {
Repr::FirstFragment { .. } => (), Repr::FirstFragment { .. } => (),
@ -2105,15 +2106,15 @@ mod test {
.reserve_with_key(&key) .reserve_with_key(&key)
.unwrap() .unwrap()
.start( .start(
frag.datagram_size() as usize - uncompressed + compressed, Some(frag.datagram_size() as usize - uncompressed + compressed),
Instant::now(), Instant::now() + crate::time::Duration::from_secs(60),
-((uncompressed - compressed) as isize), -((uncompressed - compressed) as isize),
) )
.unwrap(); .unwrap();
frags_cache frags_cache
.get_packet_assembler_mut(&key) .get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.add(frag.payload(), 0, Instant::now()) .add(frag.payload(), 0)
.unwrap(); .unwrap();
let frame2: &[u8] = &[ let frame2: &[u8] = &[
@ -2149,11 +2150,7 @@ mod test {
frags_cache frags_cache
.get_packet_assembler_mut(&key) .get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.add( .add(frag.payload(), frag.datagram_offset() as usize * 8)
frag.payload(),
frag.datagram_offset() as usize * 8,
Instant::now(),
)
.unwrap(); .unwrap();
let frame3: &[u8] = &[ let frame3: &[u8] = &[
@ -2188,11 +2185,7 @@ mod test {
frags_cache frags_cache
.get_packet_assembler_mut(&key) .get_packet_assembler_mut(&key)
.unwrap() .unwrap()
.add( .add(frag.payload(), frag.datagram_offset() as usize * 8)
frag.payload(),
frag.datagram_offset() as usize * 8,
Instant::now(),
)
.unwrap(); .unwrap();
let assembled_packet = frags_cache.get_assembled_packet(&key).unwrap(); let assembled_packet = frags_cache.get_assembled_packet(&key).unwrap();