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

MTU is ignored for new Device #383

Closed
mustermeiszer opened this issue Oct 23, 2020 · 8 comments
Closed

MTU is ignored for new Device #383

mustermeiszer opened this issue Oct 23, 2020 · 8 comments

Comments

@mustermeiszer
Copy link

mustermeiszer commented Oct 23, 2020

Hello everybody,

I am having an issue, where it seems as if smoltcp is ignoring the provided MTU (provided by a Virtio device). The reason why this is important is, that I want to measure the performance of my driver, for small packets and for different MTU's.
Allthough I think the inability to send small packets is due to the current implementation of the kernel, as it seems as if it is not possible to set the "no_dealy" option, of rust's std::net::stream.

Short overview of the context of the problem:
I am working on a rust based unikernel, who uses smoltcp as its network stack in user-space. Smoltcp communicates via some syscalls, with a kernel based virtio network driver.

When I now set the MTU of the smoltcp device to a given value, it seems to be ignored. I only assume this, as the buffers, smoltcp requests for sending always follow the slow start pattern of TCP until they reach their maximum size of 1514 bytes, regardless which MTU is set in the smoltcp device.

Is this the expected behaviour? I wondered if I am not understanding the protocol correctly or something else?
I also digged through the smoltcp code-base, up to the point, where the ethernet interface dispatches. Here it seems as if the interface is checking with the device's MTU. I also read something about a hack (Something with differnces in buffer size and window size) needed because of the working of the TCP protocol.

I appreciate any help on the topic.

The smoltcp device is implemented as follows:

impl<'a> Device<'a> for HermitNet {
	type RxToken = RxToken;
	type TxToken = TxToken;

	fn capabilities(&self) -> DeviceCapabilities {
		let mut cap = DeviceCapabilities::default();
		cap.max_transmission_unit = self.mtu.into();
		cap
	}

	fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
		match unsafe { sys_receive_rx_buffer() } {
			Ok(buffer) => Some((RxToken::new(buffer), TxToken::new())),
			_ => None,
		}
	}

	fn transmit(&'a mut self) -> Option<Self::TxToken> {
		trace!("create TxToken to transfer data");
		Some(TxToken::new())
	}
}

During init of the device, the MTU is evaluated by a syscal, which my driver will answer with the MTU set by Virtio device. I checked, and this call returns the value I assigned to the virtio device.

Thank you for your help!

Resources
Kernel: https://github.com/hermitcore
Device in Kernel: https://github.com/hermitcore/rusty-hermit/blob/master/hermit-sys/src/net/device.rs
Implementation of rust's network interface: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/hermit/net.rs

Edited:
I updated the comment, as I noticed I missed providing resources and the problem of small packets not being used.

@Dirbaio
Copy link
Member

Dirbaio commented Oct 23, 2020

I think this is a bug, yes.

When sending packets, only the remote Win and MSS is taken into account. I think it should also take into account our own MTU, to not send packets larger than that.

Should be easy to fix.

let size = cmp::min(self.remote_win_len, self.remote_mss);

@whitequark
Copy link
Contributor

@Dirbaio is correct.

@mustermeiszer
Copy link
Author

Thank you very much for the fast help and the fast fix!
Should I create a pull-request or do you want to create one @Dirbaio?

I am fairly new to open-source development and unsure about this.

Once again. Thank you!

And one further question:

  • Can I force smoltcp to send packets directly? I am unsure, if this is a smoltcp issue or depends on the implementation of the kernel...

@Dirbaio
Copy link
Member

Dirbaio commented Oct 23, 2020

Go ahead with the PR :)

Can I force smoltcp to send packets directly?

Not sure what you mean.
If you want to send raw IP packets, you can use RawSocket. There's no way to send raw Ethernet packets currently.
If you want smoltcp to send packets as soon as you write to the sockets, that's not currently possible. By design sockets buffer the data and send it when you poll the interface. Changing this would be a major redesign and I don't think it'd play nicely with the borrow checker.

@whitequark
Copy link
Contributor

Changing this would be a major redesign and I don't think it'd play nicely with the borrow checker.

Also correct. Arguably the most important design principle of smoltcp is "only safe Rust in core code". That's not the only way to do a network stack (indeed it would be perfectly reasonable to have a network stack not abiding by this principle), but it is the way smoltcp works, chiefly because it was written as a response to inexplicable lwip-related memory corruption issues. And I don't believe there is any way to do this without unsafe code.

@mustermeiszer
Copy link
Author

Thank you both again for the quick reply. I can perfectly use these arguments in my thesis.
Is there a timeout, after which the socket buffers are emptied?

Pull Request is made.

@Dirbaio
Copy link
Member

Dirbaio commented Oct 23, 2020

You can set a timeut with .set_timeout(). If the timeout passes wihtout hearing from the other end, the socket changes to Closed. That doesn't instantly clear the buffers (so you can still get queued data). They get cleared when you .connect() or .listen() to reuse the socket.

@whitequark
Copy link
Contributor

Fixed by #384.

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

No branches or pull requests

3 participants