How to Configure SR-IOV Virtual Functions in Ubuntu Server Using systemd-networkd

Single Root I/O Virtualization (SR-IOV) is a hardware specification that allows a single physical PCIe network adapter—known as a Physical Function (PF)—to partition its hardware resources into multiple virtual network interfaces called Virtual Functions (VFs). In high-performance enterprise virtualization (such as KVM/QEMU hypervisors) and low-latency container environments (such as Kubernetes SR-IOV device plugins), passing a dedicated Virtual Function directly into a guest operating system bypasses the host kernel’s software bridge stack, delivering near-bare-metal line-rate throughput and sub-microsecond packet latency. While administrators historically instantiated VFs using fragile shell scripts interacting with sysfs, Ubuntu Server provides robust, declarative orchestration of SR-IOV Virtual Functions directly through systemd-networkd. This article explains how to declare Virtual Functions, assign static MAC addresses, enforce hardware VLAN tagging, and configure spoof checking in Ubuntu using systemd-networkd.

Understanding SR-IOV Architecture and systemd-networkd Management

SR-IOV divides an enterprise network controller (such as an Intel X520, X710, E810, or Mellanox ConnectX NIC) into two architectural tiers:

  • Physical Function (PF): The primary PCIe function managed by the host operating system. The PF provides complete administrative access to device firmware, global port settings, and physical link negotiation.
  • Virtual Functions (VF): Lightweight virtual PCIe instances that share the adapter’s physical port and transceiver. Each VF has its own independent PCIe configuration space, transmit/receive FIFO queues, and interrupt vectors.

When managed through systemd-networkd, the network daemon handles the complete VF lifecycle declaratively within .network configuration files. Rather than relying on boot scripts, systemd-networkd allocates the requested number of VFs automatically upon link initialization and applies granular security parameters before guest virtual machines can attach to the hardware.

Verifying Hardware and Kernel IOMMU Support

Before configuring SR-IOV, verify that your server CPU and motherboard firmware support hardware virtualization and IOMMU (Intel VT-d or AMD-Vi). Check kernel boot parameters in /proc/cmdline:

cat /proc/cmdline | grep -E "intel_iommu=on|amd_iommu=on"

If not present, edit /etc/default/grub, append intel_iommu=on iommu=pt (or amd_iommu=on iommu=pt for AMD processors) to GRUB_CMDLINE_LINUX_DEFAULT, run sudo update-grub, and reboot the system.

Next, query your physical network adapter to verify maximum supported Virtual Functions using lspci or ethtool:

cat /sys/class/net/eth0/device/sriov_totalvfs

The reported integer (e.g. 32 or 64) defines the absolute hardware ceiling of Virtual Functions supported by the controller.

Configuring Declarative SR-IOV Virtual Functions (.network)

To configure SR-IOV in systemd-networkd, define the target configuration inside the physical adapter’s .network file located in /etc/systemd/network/. In this example, physical interface eth0 will instantiate 4 Virtual Functions with tailored security and VLAN settings.

Create or edit /etc/systemd/network/10-eth0.network:

[Match]
Name=eth0

[Network]
# Allocate 4 hardware Virtual Functions upon interface initialization
VirtualFunctions=4

[SR-IOV]
VF=0
VLANId=100
MACAddress=52:54:00:12:34:01
SpoofCheck=yes
Trust=no
QueryReceiveSideScaling=yes

[SR-IOV]
VF=1
VLANId=200
MACAddress=52:54:00:12:34:02
SpoofCheck=yes
Trust=no

[SR-IOV]
VF=2
MACAddress=52:54:00:12:34:03
Trust=yes

Understanding these critical directives ensures optimal hardware isolation:

  • VirtualFunctions=4: Directs the kernel driver to instantiate 4 virtual PCIe functions on the physical controller.
  • VLANId=100: Enforces hardware port VLAN tagging. The NIC automatically inserts VLAN tag 100 on outbound traffic and strips it on inbound packets, isolating guest VM traffic at the hardware level.
  • MACAddress=...: Assigns a fixed, persistent MAC address to the VF, preventing guest operating systems from generating randomized MACs.
  • SpoofCheck=yes: Instructs hardware filters to drop any frame transmitted by the guest whose source MAC does not match the administratively assigned MAC address.
  • Trust=no: Prevents the guest operating system from configuring promiscuous mode or altering physical multicast filters. Setting Trust=yes is reserved for trusted network appliances (such as software routers or firewalls) running inside VMs.

Applying Configuration and Verifying Virtual Functions

Apply file permissions and restart systemd-networkd to initialize the SR-IOV hardware partition:

sudo chmod 644 /etc/systemd/network/10-eth0.network
sudo systemctl restart systemd-networkd

Verify that the host created the Virtual Functions using ip link:

ip link show eth0

The terminal outputs the physical link along with each configured Virtual Function:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
    vf 0 MAC 52:54:00:12:34:01, vlan 100, spoof checking on, link-state auto, trust off
    vf 1 MAC 52:54:00:12:34:02, vlan 200, spoof checking on, link-state auto, trust off
    vf 2 MAC 52:54:00:12:34:03, spoof checking on, link-state auto, trust on

Confirm that the operating system has mapped each VF as an independent PCIe device using lspci:

lspci | grep -i "Virtual Function"

Each Virtual Function appears with its own unique PCIe Bus:Device.Function (BDF) identifier (e.g. 0000:03:10.0), ready for PCI passthrough into KVM virtual machines using virsh attach-device or OpenStack/Kubernetes orchestration.

By defining SR-IOV Virtual Functions declaratively via systemd-networkd, Ubuntu Server administrators guarantee consistent hardware provisioning across server reboots, ensuring maximum virtualization network performance and robust tenant isolation.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.