Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IP fragmentation #52

Open
podhrmic opened this issue Oct 6, 2017 · 10 comments
Open

Support IP fragmentation #52

podhrmic opened this issue Oct 6, 2017 · 10 comments

Comments

@podhrmic
Copy link
Contributor

podhrmic commented Oct 6, 2017

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 Implement ARP-related timers #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?

@whitequark
Copy link
Contributor

Basically. What you need is...

  • one storage::Assembler per fragmented packet;
  • one managed::ManagedSlice per fragmented packet;
  • one storage::RingBuffer per interface, storing FragmentedPackets in a FIFO queue;
  • one managed::ManagedMap per interface, mapping Option<u16> (ident) to usizes (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.

@whitequark
Copy link
Contributor

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.

@whitequark
Copy link
Contributor

#51 also needs a ManagedMap.

Sorry, something went wrong.

@whitequark
Copy link
Contributor

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.

Sorry, something went wrong.

@podhrmic
Copy link
Contributor Author

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?

@whitequark
Copy link
Contributor

Sounds reasonable, though I think FragmentedPacket::rx_buffer shouldn't be a RingBuffer.

@podhrmic
Copy link
Contributor Author

Thanks. It could simply be ManagedSlice<u8> to be closer to the example in RFC 815

@thvdveld
Copy link
Contributor

IPv4 fragmentation has been implemented in #634. We still need fragmentation for IPv6.

@HKalbasi
Copy link
Contributor

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.

@thvdveld
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

5 participants