Virtualization
Pass a physical network card (or a single port of one) straight through to a VM on XCP-ng 8.2 / 8.3. The VM gets the real hardware with its own driver, which is ideal for firewall/router VMs (pfSense, OPNsense, VyOS) and for NICs that need features Xen's virtual NICs don't offer.
Before you start
- In the server's BIOS/UEFI, enable VT-d (Intel) or AMD-Vi / IOMMU (AMD).
- Make sure you can reach the host's local console or IPMI/iDRAC in case networking changes go wrong.
- A VM with a passed-through device can't be live-migrated to another host, and snapshots with memory are limited.
Find the NIC's PCI address
On the XCP-ng host (SSH or the console in Xen Orchestra):
lspci | grep -i ethernet
03:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
03:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
04:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)
Each port of a multi-port card is usually its own function (.0, .1 …) and can be passed through on its own. Write the address in full form with the domain prefix, e.g. 0000:04:00.0.
Check which ports XCP-ng is currently using, so you don't take the management one:
xe pif-list params=device,MAC,management,IP
ls -l /sys/class/net/ | grep -i pci # maps eth0/eth1… to PCI addresses
Hide the NIC from the host
The host (dom0) must release the device before a VM can have it. List every function you want to pass through, each in its own brackets:
/opt/xensource/libexec/xen-cmdline --set-dom0 "xen-pciback.hide=(0000:04:00.0)"
# two ports:
# /opt/xensource/libexec/xen-cmdline --set-dom0 "xen-pciback.hide=(0000:03:00.0)(0000:03:00.1)"
If XCP-ng had already set up the port as a network interface (PIF), remove it from any networks/bonds in Xen Orchestra first. Then reboot the host:
reboot
After the reboot, confirm the device is available for passthrough:
xl pci-assignable-list
Your address (e.g. 0000:04:00.0) should be listed. Any leftover PIF for that port will show as disconnected; you can forget it with xe pif-forget uuid=<pif-uuid>.
Attach the NIC to the VM
Shut the VM down, then:
xe vm-list name-label="pfsense" params=uuid # get the VM's uuid
xe vm-param-set uuid=<vm-uuid> other-config:pci=0/0000:04:00.0
For several devices, separate them with commas:
xe vm-param-set uuid=<vm-uuid> other-config:pci=0/0000:03:00.0,0/0000:03:00.1
Recent versions of Xen Orchestra can also do this from the VM's Advanced tab (Attach PCIs) once the device is hidden from dom0.
Start the VM.
Check it inside the VM
- Linux:
lspci | grep -i ethernetandip link– the real NIC appears next to any Xen virtual NICs. - pfSense / OPNsense: the port appears with its real driver name (e.g.
igb0,ix0) under Interfaces → Assignments. - Windows: install the vendor driver if Device Manager shows an unknown Ethernet controller.
Removing passthrough
# detach from the VM (VM shut down)
xe vm-param-remove uuid=<vm-uuid> param-name=other-config param-key=pci
# give the device back to the host, then reboot
/opt/xensource/libexec/xen-cmdline --delete-dom0 xen-pciback.hide
reboot
Troubleshooting
- VM won't start: "PCI device not assignable" – the address isn't hidden (check
xl pci-assignable-list), or it's typed differently from thelspcioutput (it must include the0000:prefix). - VM starts but no NIC appears – check VT-d/IOMMU is on:
xl dmesg | grep -i -e iommu -e vt-dshould show it enabled. - The host lost network after the reboot – you hid the management NIC. From the local console run the
--delete-dom0command above, reboot, and pick a different port. - You need the NIC shared by several VMs – passthrough gives the whole port to one VM. For sharing, look at the card's SR-IOV support instead.
See also: XCP-ng Intel iGPU Passthrough.
