Skip to content

virtio/net: Advertise MTU to the guest via VIRTIO_NET_F_MTU#5828

Open
ananos wants to merge 1 commit into
firecracker-microvm:mainfrom
nubificus:feat_mtu
Open

virtio/net: Advertise MTU to the guest via VIRTIO_NET_F_MTU#5828
ananos wants to merge 1 commit into
firecracker-microvm:mainfrom
nubificus:feat_mtu

Conversation

@ananos
Copy link
Copy Markdown

@ananos ananos commented Apr 9, 2026

Changes

Add MTU negotiation support to the virtio-net device. The tap interface MTU is read using SIOCGIFMTU and stored in the ConfigSpace alongside the MAC address, with the intermediate padding fields (_status, _max_virtqueue_pairs) added to match the virtio_net_config layout.

VIRTIO_NET_F_MTU is advertised in the available features so the guest driver can read the MTU from the config space and configure the network interface accordingly, avoiding fragmentation or oversized-packet issues when the underlying tap MTU differs from the default.

Reason

An MTU != 1500 is not honored in the VM, causing packet drops from the VM.

License Acceptance

By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license. For more information on following Developer
Certificate of Origin and signing off your commits, please check
CONTRIBUTING.md.

PR Checklist

  • I have read and understand CONTRIBUTING.md.
  • I have run tools/devtool checkbuild --all to verify that the PR passes
    build checks on all supported architectures.
  • I have run tools/devtool checkstyle to verify that the PR passes the
    automated style checks.
  • I have described what is done in these changes, why they are needed, and
    how they are solving the problem in a clear and encompassing way.
  • I have updated any relevant documentation (both in code and in the docs)
    in the PR.
  • I have mentioned all user-facing changes in CHANGELOG.md.
  • If a specific issue led to this PR, this PR closes the issue.
  • When making API changes, I have followed the
    Runbook for Firecracker API changes.
  • I have tested all new and changed functionalities in unit tests and/or
    integration tests.
  • I have linked an issue to every new TODO.

  • This functionality cannot be added in rust-vmm.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 95.45455% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 83.08%. Comparing base (c822ca1) to head (07291d1).
⚠️ Report is 42 commits behind head on main.

⚠️ Current head 07291d1 differs from pull request most recent head 9fce4f3

Please upload reports for the commit 9fce4f3 to get more accurate results.

Files with missing lines Patch % Lines
src/vmm/src/devices/virtio/net/tap.rs 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5828      +/-   ##
==========================================
+ Coverage   82.87%   83.08%   +0.20%     
==========================================
  Files         276      275       -1     
  Lines       29728    29481     -247     
==========================================
- Hits        24637    24494     -143     
+ Misses       5091     4987     -104     
Flag Coverage Δ
5.10-m5n.metal 83.41% <95.45%> (+0.24%) ⬆️
5.10-m6a.metal 82.75% <95.45%> (+0.24%) ⬆️
5.10-m6g.metal 79.99% <95.45%> (+0.20%) ⬆️
5.10-m6i.metal 83.41% <95.45%> (+0.23%) ⬆️
5.10-m7a.metal-48xl 82.73% <95.45%> (+0.24%) ⬆️
5.10-m7g.metal 79.99% <95.45%> (+0.20%) ⬆️
5.10-m7i.metal-24xl 83.38% <95.45%> (+0.23%) ⬆️
5.10-m7i.metal-48xl 83.39% <95.45%> (+0.24%) ⬆️
5.10-m8g.metal-24xl 79.99% <95.45%> (+0.20%) ⬆️
5.10-m8g.metal-48xl 79.99% <95.45%> (+0.20%) ⬆️
5.10-m8i.metal-48xl 83.39% <95.45%> (+0.24%) ⬆️
5.10-m8i.metal-96xl 83.39% <95.45%> (+0.24%) ⬆️
6.1-m5n.metal 83.44% <95.45%> (+0.24%) ⬆️
6.1-m6a.metal 82.78% <95.45%> (+0.24%) ⬆️
6.1-m6g.metal 79.99% <95.45%> (+0.20%) ⬆️
6.1-m6i.metal 83.43% <95.45%> (+0.23%) ⬆️
6.1-m7a.metal-48xl 82.76% <95.45%> (+0.24%) ⬆️
6.1-m7g.metal 79.99% <95.45%> (+0.20%) ⬆️
6.1-m7i.metal-24xl 83.45% <95.45%> (+0.24%) ⬆️
6.1-m7i.metal-48xl 83.45% <95.45%> (+0.24%) ⬆️
6.1-m8g.metal-24xl 79.98% <95.45%> (+0.20%) ⬆️
6.1-m8g.metal-48xl 79.99% <95.45%> (+0.20%) ⬆️
6.1-m8i.metal-48xl 83.46% <95.45%> (+0.24%) ⬆️
6.1-m8i.metal-96xl 83.45% <95.45%> (+0.24%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ilstam
Copy link
Copy Markdown
Contributor

ilstam commented Apr 10, 2026

Hi Anastassios,

Thank you for your PR.

We'll need some integration test for this. Perhaps a test where different TAPs are configured with different MTUs in the host and then checking that the guest sees the MTU we expect for each of them? Also, we should probably mention this change in the CHANGELOG.

Add MTU negotiation support to the virtio-net

This seems to purely be advertisement rather than negotiation, right?

Best,
Ilias

.map_err(NetError::TapSetVnetHdrSize)?;

Self::new_with_tap(id, tap, guest_mac, rx_rate_limiter, tx_rate_limiter)
let mtu = tap.mtu().map_err(NetError::TapGetMtu)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think perhaps it's better to not fail here, but instead log a warning and create the device without VIRTIO_NET_F_MTU.

Comment thread src/vmm/src/devices/virtio/net/tap.rs Outdated
}

/// Get the MTU of the tap interface.
pub fn mtu(&self) -> Result<u16, TapError> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be easier to just read /sys/class/net/{iface}/mtu?

import pytest
from tenacity import Retrying, stop_after_attempt, wait_fixed

import host_tools.network as net_tools # pylint: disable=import-error
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error seems to be globally suppressed in tests/pyproject.toml


def test_tap_mtu_advertised_to_guest(uvm_plain_any):
"""
Verify that VIRTIO_NET_F_MTU correctly advertises the TAP MTU to the guest.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also check that VIRTIO_NET_F_MTU is set?

Comment thread CHANGELOG.md Outdated
number of suppressed messages.
- [#5828](https://github.com/firecracker-microvm/firecracker/pull/5828):
Advertise TAP interface MTU to the guest via `VIRTIO_NET_F_MTU`. The
virtio-net device now reads the host TAP MTU using `SIOCGIFMTU` and stores it
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: How the MTU is obtained (e.g. via SIOCGIFMTU ) is an implementation detail and perhaps it's not very important for the Changelog.

@ilstam
Copy link
Copy Markdown
Contributor

ilstam commented Apr 28, 2026

Hi Anastassios. Thanks for sending the new version. Could you rebase on top of main and resolve the conflicts so that we can run the full testing pipeline and make sure everything passes?

@ananos ananos force-pushed the feat_mtu branch 2 times, most recently from f048435 to 4ee57a6 Compare April 28, 2026 11:37
@ananos
Copy link
Copy Markdown
Author

ananos commented Apr 28, 2026

Hi Anastassios. Thanks for sending the new version. Could you rebase on top of main and resolve the conflicts so that we can run the full testing pipeline and make sure everything passes?

running the tests locally ATM, trying to resolve some linting issues - will let you know when I'm ready to kick off the CI.

@ananos
Copy link
Copy Markdown
Author

ananos commented Apr 28, 2026

Hi Anastassios. Thanks for sending the new version. Could you rebase on top of main and resolve the conflicts so that we can run the full testing pipeline and make sure everything passes?

running the tests locally ATM, trying to resolve some linting issues - will let you know when I'm ready to kick off the CI.

ok, linters fail with a timeout, but I'm not sure if its this PR changes or some glitch. Can you kick-off the CI and we can iterate?

thanks!

@ilstam
Copy link
Copy Markdown
Contributor

ilstam commented Apr 28, 2026

Thinking further about this. I had a quick look at what QEMU does and it doesn't seem to try and detect the MTU. It rather lets the user optionally specify it in the command line. This also works when the backend is something like vhost-user-net which we might want to support in the future. Shall we consider doing the same, i.e. adding an optional 'mtu' setting to the network interface configuration and advertise VIRTIO_NET_F_MTU only when that's set? Would that work for your use-case?

Also a practical difference I noticed is that with VIRTIO_NET_F_MTU set the guest kernel will (rightly) not allow you to configure an MTU higher than what was advertised by the virtio backend. But when VIRTIO_NET_F_MTU is not set there is no limit to what you can configure inside the guest.

@ananos
Copy link
Copy Markdown
Author

ananos commented Apr 28, 2026

Thinking further about this. I had a quick look at what QEMU does and it doesn't seem to try and detect the MTU. It rather lets the user optionally specify it in the command line. This also works when the backend is something like vhost-user-net which we might want to support in the future. Shall we consider doing the same, i.e. adding an optional 'mtu' setting to the network interface configuration and advertise VIRTIO_NET_F_MTU only when that's set? Would that work for your use-case?

totally agree, happy to include this as well as part of this PR.

Also a practical difference I noticed is that with VIRTIO_NET_F_MTU set the guest kernel will (rightly) not allow you to configure an MTU higher than what was advertised by the virtio backend. But when VIRTIO_NET_F_MTU is not set there is no limit to what you can configure inside the guest.

are you concerned about not being able to use larger MTU sizes even when the host is bound to 1500, when(/if) FC virtio-net supports this kind of functionality (LRO/TSO etc.)?

I'm assuming the config option you suggested resolves this issue. If the config is set, then we take into account the MTU set by the host interface. If not, we ignore it (not advertise it via the driver) and the guest can set whatever they want.

@ilstam
Copy link
Copy Markdown
Contributor

ilstam commented Apr 28, 2026

totally agree, happy to include this as well as part of this PR.

Sounds good.

I'm assuming the config option you suggested resolves this issue.

Yep.

are you concerned about not being able to use larger MTU sizes even when the host is bound to 1500, when(/if) FC virtio-net supports this kind of functionality (LRO/TSO etc.)?

I guess the current behaviour allows you to do weird things such as increasing both the host TAP MTU and the guest interface MTU after starting the VM. I don't know if anyone does this, but if we tried to set VIRTIO_NET_F_MTU unconditionally then we would break things like that.

If the config is set, then we take into account the MTU set by the host interface.

To be clear, the config option will carry the MTU value, so it's on the administrator/operator to specify the MTU value (rather than Firecracker trying to determine it via sysfs or otherwise).

If not, we ignore it (not advertise it via the driver) and the guest can set whatever they want.

Yes, thus preserving the current behaviour by default.

@ananos ananos force-pushed the feat_mtu branch 2 times, most recently from 7efe2a8 to 12ad4c3 Compare April 28, 2026 20:31
Add an optional `mtu` field to the `NetworkInterface` API (both the REST
API and JSON config file). When set, the virtio-net device advertises
VIRTIO_NET_F_MTU and places the value in config space at offset 10
(matching the virtio_net_config layout), allowing the guest driver to
configure the interface MTU automatically.

When `mtu` is absent (the default), VIRTIO_NET_F_MTU is not advertised
and the guest uses its own default MTU — preserving existing behaviour.

Implementation details:
- Extend `ConfigSpace` with padding fields `_status` and
  `_max_virtqueue_pairs` at offsets 6 and 8 to correctly place `mtu`
  at offset 10 per the virtio spec (#[repr(C)], ByteValued safe)
- `Net::new()` and `new_with_tap()` accept `mtu: Option<u16>`; the
  feature bit is set only when `Some`
- `Net::mtu()` accessor reflects the advertised value for use in
  snapshot save and API GET responses
- `NetConfigSpaceState` gains `mtu: Option<u16>` with `#[serde(default)]`
  for backward-compatible snapshot restore
- Swagger, docs/device-api.md, and CHANGELOG updated accordingly

Signed-off-by: Anastassios Nanos <[email protected]>
@ananos
Copy link
Copy Markdown
Author

ananos commented Apr 28, 2026

made an attempt -- have a look at your convenience and let me know if you think the implementation makes sense.

mtu:
type: integer
description:
MTU to advertise to the guest via VIRTIO_NET_F_MTU. When set, the guest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Perhaps we can say "a compatible guest driver will configure the interface..."

/// Returns the configured MTU if `VIRTIO_NET_F_MTU` is advertised, otherwise `None`.
pub fn mtu(&self) -> Option<u16> {
if self.avail_features & (1 << VIRTIO_NET_F_MTU) != 0 {
Some(self.config_space.mtu)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the guest overwrites the mtu field in write_config()? Should we ignore writes to it?

pub host_dev_name: String,
/// Guest MAC address.
pub guest_mac: Option<MacAddr>,
/// MTU to advertise to the guest via VIRTIO_NET_F_MTU. When set, the guest driver
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this comment is a bit too verbose for this location. Perhaps "Maximum Transmission Unit" is enough here?

# The iperf version to run this tests with
IPERF_BINARY = "iperf3"

# VIRTIO_NET_F_MTU feature bit index (virtio spec 5.1.3)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This references a section of the spec, but it doesn't specify the spec version

Comment thread CHANGELOG.md
rate-limiter support to virtio-pmem device to allow control over I/O bandwidth
generated by the FLUSH requests from the guest.
- [#5828](https://github.com/firecracker-microvm/firecracker/pull/5828):
Advertise TAP interface MTU to the guest via `VIRTIO_NET_F_MTU`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

via a new optional mtu option in the network-interfaces API endpoint

for idx, mtu in iface_mtus:
iface = net_tools.NetIfaceConfig.with_id(idx)
vm.add_net_iface(iface, api=False)
vm.api.network.put(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call is done by add_net_iface, why don't we append the mtu option above?

@ilstam ilstam removed the Status: Awaiting review Indicates that a pull request is ready to be reviewed label May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants