VM Networking
Quick Reference
# List networks
virsh net-list --all
# Start default network
virsh net-start default
virsh net-autostart default
# Network info
virsh net-info default
virsh net-dhcp-leases default
# Create bridge network
virsh net-define bridge.xml
virsh net-start br-network
# VM network interfaces
virsh domiflist vm-name
virsh domifaddr vm-name
Network Modes Overview
Available Network Types
| Mode | Description | Use Case |
|---|---|---|
NAT (default) |
VMs share host IP via NAT |
Development, isolated testing |
Bridge |
VMs appear as physical hosts |
Production, servers |
Routed |
Host routes traffic to VMs |
Specific routing requirements |
macvtap |
Direct connection to physical NIC |
Performance-critical VMs |
Isolated |
VMs can only talk to each other |
Security testing, sandboxing |
Host-only |
VMs talk only to host |
Local development |
NAT Network (Default)
How NAT Works
The default libvirt network uses NAT:
-
Creates virtual bridge (virbr0)
-
Runs dnsmasq for DHCP/DNS
-
NAT rules via iptables/nftables
-
VMs get private IPs (192.168.122.0/24)
-
VMs can reach internet via host
Manage Default Network
# Check default network
virsh net-list --all
# Start default network
virsh net-start default
# Enable autostart
virsh net-autostart default
# View configuration
virsh net-dumpxml default
# View DHCP leases
virsh net-dhcp-leases default
# Restart network
virsh net-destroy default
virsh net-start default
Default Network XML
<network>
<name>default</name>
<uuid>...</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:xx:xx:xx'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
Custom NAT Network
# Create custom NAT network
cat > /tmp/nat-custom.xml << 'EOF'
<network>
<name>nat-custom</name>
<forward mode='nat'/>
<bridge name='virbr1' stp='on' delay='0'/>
<ip address='10.10.10.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.10.10.100' end='10.10.10.200'/>
<!-- Static DHCP reservation -->
<host mac='52:54:00:aa:bb:cc' name='myvm' ip='10.10.10.50'/>
</dhcp>
</ip>
</network>
EOF
virsh net-define /tmp/nat-custom.xml
virsh net-start nat-custom
virsh net-autostart nat-custom
Port Forwarding with NAT
# Forward host port to VM (iptables)
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \
-j DNAT --to-destination 192.168.122.100:80
sudo iptables -A FORWARD -p tcp -d 192.168.122.100 --dport 80 \
-m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# Forward host port to VM (nftables)
sudo nft add rule ip nat prerouting tcp dport 8080 \
dnat to 192.168.122.100:80
# libvirt hook for persistent port forwarding
# /etc/libvirt/hooks/qemu
Bridged Networking
Bridged Network Overview
Bridged networking connects VMs directly to the physical network:
-
VMs get IPs from physical network DHCP
-
VMs appear as separate hosts on network
-
Best for production servers
-
Requires bridge on host
Create Linux Bridge (NetworkManager)
# Create bridge
nmcli con add type bridge ifname br0 con-name br0
# Add physical interface to bridge
nmcli con add type bridge-slave ifname eth0 master br0
# Configure bridge IP (or use DHCP)
nmcli con modify br0 ipv4.method auto
# Bring up bridge
nmcli con up br0
# Verify
ip addr show br0
bridge link show
Create Linux Bridge (systemd-networkd)
# /etc/systemd/network/br0.netdev
cat > /etc/systemd/network/br0.netdev << 'EOF'
[NetDev]
Name=br0
Kind=bridge
EOF
# /etc/systemd/network/br0-bind.network
cat > /etc/systemd/network/br0-bind.network << 'EOF'
[Match]
Name=eth0
[Network]
Bridge=br0
EOF
# /etc/systemd/network/br0.network
cat > /etc/systemd/network/br0.network << 'EOF'
[Match]
Name=br0
[Network]
DHCP=yes
EOF
# Restart networkd
sudo systemctl restart systemd-networkd
Define Bridge Network in libvirt
# Create bridge network definition
cat > /tmp/bridge-network.xml << 'EOF'
<network>
<name>br-network</name>
<forward mode='bridge'/>
<bridge name='br0'/>
</network>
EOF
virsh net-define /tmp/bridge-network.xml
virsh net-start br-network
virsh net-autostart br-network
Use Bridge with VM
# Create VM with bridge
virt-install \
--name myvm \
--ram 2048 \
--vcpus 2 \
--disk size=20 \
--cdrom /path/to/iso \
--network bridge=br0 \
--os-variant generic
# Attach existing VM to bridge
virsh attach-interface vm-name bridge br0 --model virtio --persistent
# Or edit VM XML
virsh edit vm-name
# Change network section to:
# <interface type='bridge'>
# <source bridge='br0'/>
# <model type='virtio'/>
# </interface>
Isolated Network
Create Isolated Network
VMs can only communicate with each other, not host or external:
cat > /tmp/isolated.xml << 'EOF'
<network>
<name>isolated</name>
<bridge name='virbr2' stp='on' delay='0'/>
<ip address='10.20.30.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.20.30.100' end='10.20.30.200'/>
</dhcp>
</ip>
</network>
EOF
virsh net-define /tmp/isolated.xml
virsh net-start isolated
virsh net-autostart isolated
Host-Only Network
Create Host-Only Network
VMs can communicate with host and each other, but not external:
cat > /tmp/hostonly.xml << 'EOF'
<network>
<name>hostonly</name>
<forward mode='nat'>
<!-- NAT but no external access via firewall rules -->
</forward>
<bridge name='virbr3' stp='on' delay='0'/>
<ip address='10.30.30.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.30.30.100' end='10.30.30.200'/>
</dhcp>
</ip>
</network>
EOF
virsh net-define /tmp/hostonly.xml
virsh net-start hostonly
# Block external access
sudo iptables -I FORWARD -i virbr3 -o eth0 -j REJECT
macvtap (Direct Connection)
macvtap Overview
macvtap provides direct connection to physical NIC without bridge:
-
Better performance than bridged
-
VM gets its own MAC on physical network
-
Cannot communicate with host directly (use vepa mode)
Use macvtap
# Create VM with macvtap
virt-install \
--name myvm \
--ram 2048 \
--disk size=20 \
--cdrom /path/to/iso \
--network type=direct,source=eth0,source_mode=bridge \
--os-variant generic
# macvtap modes:
# bridge - VMs can communicate with each other and external
# vepa - Traffic goes through external switch (requires VEPA switch)
# private - VMs isolated from each other
# passthrough - Single VM gets exclusive NIC access
Static IP Configuration
DHCP Reservations
# Add static DHCP entry to network
virsh net-update default add ip-dhcp-host \
"<host mac='52:54:00:aa:bb:cc' name='myvm' ip='192.168.122.50'/>" \
--live --config
# Remove entry
virsh net-update default delete ip-dhcp-host \
"<host mac='52:54:00:aa:bb:cc' ip='192.168.122.50'/>" \
--live --config
# List reservations
virsh net-dumpxml default | grep host
Static IP in Network XML
<network>
<name>default</name>
<forward mode='nat'/>
<bridge name='virbr0'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.100' end='192.168.122.200'/>
<!-- Static reservations -->
<host mac='52:54:00:aa:bb:01' name='web' ip='192.168.122.10'/>
<host mac='52:54:00:aa:bb:02' name='db' ip='192.168.122.11'/>
<host mac='52:54:00:aa:bb:03' name='app' ip='192.168.122.12'/>
</dhcp>
</ip>
</network>
VM Network Interface Management
List VM Interfaces
# List interfaces
virsh domiflist vm-name
# Get IP addresses
virsh domifaddr vm-name
# Interface statistics
virsh domifstat vm-name vnet0
# Get MAC address
virsh domiflist vm-name | grep -oE '([0-9a-f]{2}:){5}[0-9a-f]{2}'
Attach/Detach Interfaces
# Attach new interface to running VM
virsh attach-interface vm-name network default --model virtio --live
# Attach to bridge
virsh attach-interface vm-name bridge br0 --model virtio --live
# Make persistent (survives reboot)
virsh attach-interface vm-name network default --model virtio --persistent
# Detach interface
virsh detach-interface vm-name network --mac 52:54:00:xx:xx:xx
# Detach by type
virsh detach-interface vm-name bridge
Modify Interface
# Edit VM and change network
virsh edit vm-name
# Change interface section:
<interface type='network'>
<mac address='52:54:00:aa:bb:cc'/>
<source network='default'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
# Set specific MAC address
virsh attach-interface vm-name network default \
--model virtio --mac 52:54:00:aa:bb:cc
DNS and DHCP Configuration
libvirt dnsmasq
libvirt runs dnsmasq for each NAT network:
# View dnsmasq process
ps aux | grep dnsmasq
# DHCP leases file
cat /var/lib/libvirt/dnsmasq/default.leases
# DNS hosts file
cat /var/lib/libvirt/dnsmasq/default.hostsfile
# dnsmasq config
cat /var/lib/libvirt/dnsmasq/default.conf
Custom DNS Entries
<!-- Add to network XML -->
<network>
<name>default</name>
<!-- ... -->
<dns>
<!-- Forward DNS server -->
<forwarder addr='8.8.8.8'/>
<!-- Local DNS entry -->
<host ip='192.168.122.10'>
<hostname>myapp.local</hostname>
<hostname>www.myapp.local</hostname>
</host>
</dns>
</network>
Modify DNS at Runtime
# Add DNS host entry
virsh net-update default add dns-host \
"<host ip='192.168.122.10'><hostname>myapp.local</hostname></host>" \
--live --config
# Remove DNS entry
virsh net-update default delete dns-host \
"<host ip='192.168.122.10'><hostname>myapp.local</hostname></host>" \
--live --config
Multiple Network Interfaces
VM with Multiple NICs
# Create VM with two networks
virt-install \
--name multinic \
--ram 2048 \
--disk size=20 \
--cdrom /path/to/iso \
--network network=default \
--network network=isolated \
--os-variant generic
# Or add second NIC to existing VM
virsh attach-interface vm-name network isolated --model virtio --persistent
Interface XML for Multiple NICs
<devices>
<!-- First interface - NAT -->
<interface type='network'>
<mac address='52:54:00:aa:bb:01'/>
<source network='default'/>
<model type='virtio'/>
</interface>
<!-- Second interface - Isolated -->
<interface type='network'>
<mac address='52:54:00:aa:bb:02'/>
<source network='isolated'/>
<model type='virtio'/>
</interface>
<!-- Third interface - Bridge -->
<interface type='bridge'>
<mac address='52:54:00:aa:bb:03'/>
<source bridge='br0'/>
<model type='virtio'/>
</interface>
</devices>
Network Troubleshooting
Diagnostic Commands
# Check network status
virsh net-list --all
virsh net-info default
# Check bridge
ip link show virbr0
bridge link show
# Check VM connectivity
virsh domifaddr vm-name
ping 192.168.122.x
# Check dnsmasq
systemctl status libvirtd
ps aux | grep dnsmasq
# Check iptables NAT
sudo iptables -t nat -L -n -v | grep virbr
# Check DHCP leases
virsh net-dhcp-leases default
Common Issues
# Network won't start
virsh net-destroy default
virsh net-start default
# No DHCP address
# Check dnsmasq is running
# Check network is active
# Check VM interface connected to correct network
# Can't reach internet from VM
# Check NAT rules
sudo iptables -t nat -L POSTROUTING
# Check IP forwarding
cat /proc/sys/net/ipv4/ip_forward
# Enable if needed
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
# Bridge not working
# Check physical interface is enslaved
bridge link show br0
# Check IP on bridge (not physical interface)
ip addr show br0
Network Hooks
# Create hook script
cat > /etc/libvirt/hooks/network << 'EOF'
#!/bin/bash
# $1 = network name
# $2 = action (start, started, stopped, etc.)
NETWORK=$1
ACTION=$2
case "$NETWORK" in
default)
case "$ACTION" in
started)
# Add custom iptables rules
;;
stopped)
# Remove custom rules
;;
esac
;;
esac
EOF
chmod +x /etc/libvirt/hooks/network
Quick Command Reference
# Network management
virsh net-list --all # List networks
virsh net-start NETWORK # Start network
virsh net-destroy NETWORK # Stop network
virsh net-autostart NETWORK # Enable autostart
virsh net-dumpxml NETWORK # View config
virsh net-edit NETWORK # Edit config
# Network creation
virsh net-define file.xml # Define network
virsh net-undefine NETWORK # Remove network
# DHCP/DNS
virsh net-dhcp-leases NETWORK # View leases
virsh net-update NETWORK add ip-dhcp-host ... # Add static IP
virsh net-update NETWORK add dns-host ... # Add DNS entry
# VM interfaces
virsh domiflist VM # List VM NICs
virsh domifaddr VM # Get VM IPs
virsh attach-interface VM network NETWORK # Add NIC
virsh detach-interface VM network --mac MAC # Remove NIC
# Bridge commands
ip link show BRIDGE # Show bridge
bridge link show # Show bridge members
nmcli con add type bridge ifname br0 # Create bridge