634: IPv4 fragmentation (for outgoing) r=Dirbaio a=thvdveld

Almost ready for review, just need to clean up some things 🎉 

Co-authored-by: Thibaut Vandervelden <thvdveld@vub.be>
This commit is contained in:
bors[bot] 2022-09-21 21:12:23 +00:00 committed by GitHub
commit 70915655b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 604 additions and 184 deletions

View File

@ -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};
@ -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);
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[..]);
}
#[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());
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 sixlowpan_out_packet_cache[..]);
}
if medium == Medium::Ethernet {

View File

@ -4,13 +4,12 @@ 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",
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};
@ -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);
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[..]);
}
#[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());
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 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::<Vec<_>>().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() {

View File

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

View File

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

View File

@ -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<Ieee802154Pan>,
#[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<PacketAssemblerSet<'a, Ipv4FragKey>>,
ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>,
#[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_out_buffer: ManagedSlice<'a, u8>,
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_fragments: Option<PacketAssemblerSet<'a, SixlowpanFragKey>>,
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: Option<ManagedSlice<'a, u8>>,
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_reassembly_buffer(ipv4_frag_cache)
.ipv4_fragmentation_buffer(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),
sixlowpan_reassembly_buffer_timeout: Duration::from_secs(60),
#[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_out_buffer: None,
sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]),
}
}
@ -384,35 +474,44 @@ 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);
pub fn ipv4_reassembly_buffer(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self {
self.ipv4_fragments = storage;
self
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub fn sixlowpan_fragments_cache(
mut self,
storage: PacketAssemblerSet<'a, SixlowpanFragKey>,
) -> Self {
self.sixlowpan_fragments = Some(storage);
self
}
#[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
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub fn sixlowpan_out_packet_cache<T>(mut self, storage: T) -> Self
#[cfg(feature = "proto-ipv4-fragmentation")]
pub fn ipv4_fragmentation_buffer<T>(mut self, storage: T) -> Self
where
T: Into<ManagedSlice<'a, u8>>,
{
self.sixlowpan_out_buffer = Some(storage.into());
self.ipv4_out_buffer = storage.into();
self
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub fn sixlowpan_reassembly_buffer(
mut self,
storage: PacketAssemblerSet<'a, SixlowpanFragKey>,
) -> Self {
self.sixlowpan_fragments = storage;
self
}
#[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
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub fn sixlowpan_fragmentation_buffer<T>(mut self, storage: T) -> Self
where
T: Into<ManagedSlice<'a, u8>>,
{
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,24 +587,31 @@ 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,
sixlowpan_fragments_cache_timeout: self.sixlowpan_reassembly_buffer_timeout,
#[cfg(not(any(
feature = "proto-ipv4-fragmentation",
@ -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<D>(&mut self, device: &mut D) -> Result<bool>
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<D>(&mut self, device: &mut D) -> Result<bool>
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<Tx>(&mut self, tx_token: Tx, packet: EthernetPacket) -> Result<()>
fn dispatch<Tx>(
&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!(),
};
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_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()..];
}
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);
})
emit_ip(&ip_repr, tx_buffer);
Ok(())
})
}
}
#[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);
// 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()..];
}
ip_repr.emit(&mut tx_buffer, &self.caps.checksum);
let payload = &mut tx_buffer[ip_repr.buffer_len()..];
packet.emit_payload(ip_repr, payload, &self.caps);
Ok(())
})
}
#[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)
}
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<Tx: TxToken>(
&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_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());
@ -3438,12 +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()));
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());
@ -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(),
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<T: AsRef<[u8]>> Packet<T> {
id: self.ident(),
src_addr: self.src_addr(),
dst_addr: self.dst_addr(),
protocol: self.next_header(),
}
}
}

View File

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

View File

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