smoltcp
smoltcp copied to clipboard
Support IP fragmentation
In a similar spirit, I would also like to implement support for IP fragmentation. The logic behind fragmentation is pretty straightforward, but the way to implement it less so.
So far it looks like it would need:
- a queue for the fragmented IP packets, where they can be stored before reassembling and passing them to the higher layers
- timers associated with each fragmented packet (similar to #25 ) so they can be dropped after a timeout
I think this means adding a queue to the https://github.com/m-labs/smoltcp/blob/master/src/iface/ethernet.rs
Thoughts?
Basically. What you need is...
- one
storage::Assembler
per fragmented packet; - one
managed::ManagedSlice
per fragmented packet; - one
storage::RingBuffer
per interface, storingFragmentedPacket
s in a FIFO queue; - one
managed::ManagedMap
per interface, mappingOption<u16>
(ident) tousize
s (positions in the queue).
Broadly, how it would work is:
- every time a fragmented packet is received, its ident is looked up in the map and the corresponding FragmentedPacket is updated if it's still within the bounds;
- if it's not within bounds, the entry is removed;
- positions in the queue in the map and positions in the ring buffer are mapped to each other through an offset that is increased every time the queue has a packet removed from it.
The main issue here is that managed::ManagedMap
doesn't exist. It should be a generalized version of SliceArpCache
, using a slice on heap-free platforms and a BTreeMap otherwise.
#51 also needs a ManagedMap.
There is now a managed::ManagedMap
. Also, scratch what I said about FIFO buffers, that doesn't sound like a very good idea on reflection. I'm not actually sure what sort of data structure can be used to implement reassembly without fixing MTU upfront or suffering internal fragmentation.
After looking at RFC 815 I have some ideas.
First, the buffer for each fragmented packet will have to be manually allocated (similar to defining buffers for sockets), so the number and size can be user-configured.
Second, we need to check packet ID, as well as source and destination address. Something like this:
struct FragmentedPacket {
assebler: Assembler,
rx_buffer: RingBuffer<'a>,
src_addr: Ipv4Address,
dst_addr: Ipv4Address,
id: u16,
// timeout?
}
The interface then will have a ManagedSlice
of FragmentedPacket
elements, similar to SocketSet
.
The actual data processing would happen in process_ipv4
like this:
...
let mut ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?;
let checksum_caps = self.device_capabilities.checksum.clone();
// >> here we handle fragmentation
// verify checksum before making repr (in case we need to fragment)
if checksum_caps.ipv4.rx() && !ipv4_packet.verify_checksum() { return Err(Error::Checksum) }
// contains more fragments ?
if ipv4_packet.more_frags() || ipv4_packet.frag_offset() > 0 {
// pass to the set of fragmented packets
// iter over existing IDs, either add a new fragment
// or work with an existing one
// potentially we end up with a new ipv4_packet
}
// Repr doesn't support fragmentation yet, so don't give it fragmented packets
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?;
...
Plus an appropriate feature guards.
If the buffer backing fragmented packet is exhausted (too small), the packet is dropped.
Thoughts? Does it look like a sane idea?
Sounds reasonable, though I think FragmentedPacket::rx_buffer
shouldn't be a RingBuffer
.
Thanks. It could simply be ManagedSlice<u8>
to be closer to the example in RFC 815
IPv4 fragmentation has been implemented in #634. We still need fragmentation for IPv6.
IPv4 fragmentation has been implemented in #634
Does it need an special configuration to enable? I have a setup that smoltcp responses ping
and ping -s 1000
but when I use ping -s 2000
so that my kernel fragment the icmp packet, it doesn't response.
You need to increase the reassembly buffer size of smoltcp. You can do this using feature flags. The default buffer size is 1500 bytes. https://github.com/smoltcp-rs/smoltcp?tab=readme-ov-file#reassembly_buffer_size