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

macvtap XML Configuration

<interface type='direct'>
  <source dev='eth0' mode='bridge'/>
  <model type='virtio'/>
</interface>

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