Aller au contenu
Virtualisation medium

Dépannage réseau KVM/libvirt

27 min de lecture

Logo KVM

Ce guide de dépannage réseau KVM/libvirt couvre les problèmes les plus fréquents : VM sans IP, NAT cassé, bridge non fonctionnel, DNS KO, conflits Docker, firewall guest bloquant. Chaque section propose un diagnostic précis et une solution testée.

Ce que vous trouverez :

  • Diagnostic Host vs Guest — identifier où ça coince (50% des pannes sont côté VM)
  • Recettes par topologie — NAT, Bridge, DNS en onglets séparés
  • Services modularisés — virtnetworkd vs libvirtd sur distros récentes
  • Firewall backend — nftables/iptables selon votre distribution
  • Pièges classiquesvirsh domifaddr vide, virbr0 in use, conflits Docker

Utilisez l’arbre de décision pour aller directement à votre problème, ou parcourez les onglets NAT/Bridge/DNS.

Avant de plonger dans le dépannage, identifiez ça coince. 50% des pannes sont côté VM (guest), pas côté hôte.

VérificationCôtéCommande
Réseau libvirt actifHôtevirsh net-list --all
Bridge existeHôteip link show virbr0 ou br0
Services libvirtHôtesystemctl status virtnetworkd
Interface upVMip -br link
IP attribuéeVMip -br addr
Route par défautVMip route
DNS fonctionnelVMresolvectl status ou cat /etc/resolv.conf

Arbre de décision réseau KVM/libvirt

0) Confirmer la topologie (sinon vous dépannez le mauvais réseau)

Section intitulée « 0) Confirmer la topologie (sinon vous dépannez le mauvais réseau) »

Avant tout diagnostic, vérifiez sur quelle topologie vous êtes réellement :

Fenêtre de terminal
# Quelle interface la VM utilise ?
virsh domiflist ma-vm
# Quels réseaux libvirt existent ?
virsh net-list --all
Source affichéeTopologieDHCP fourni par
default / virbr0NATdnsmasq (libvirt)
br0BridgeDHCP du LAN
Autre nomRéseau customDépend de la config

5) Bridge ne marche pas / VM pas joignable depuis LAN

Section intitulée « 5) Bridge ne marche pas / VM pas joignable depuis LAN »

Exécutez ces commandes dans la VM pour identifier où ça coince :

Fenêtre de terminal
# 1. Interface up et IP ?
ip -br link
ip -br addr
# 2. Ping gateway
ping -c2 192.168.122.1 # NAT
# ou
ping -c2 <gateway-lan> # Bridge
# 3. Ping Internet
ping -c2 8.8.8.8
# 4. DNS
nslookup example.com
RésultatDiagnosticSection
(1) KO : pas d’IPDHCP host ou guestPas d’IP
(1) OK, (2) KOL2/L3 local, firewall guestFirewall guest
(2) OK, (3) KONAT/forwardingip_forward + NAT
(3) OK, (4) KODNSDNS

Symptôme : virsh net-list --all montre “default” en état “inactive”.

Fenêtre de terminal
# Démarrer et activer au boot
virsh net-start default
virsh net-autostart default

Si ça échoue avec “Network is already in use by interface virbr0”, voir virbr0 already in use.

libvirt lance un processus dnsmasq pour fournir DHCP et DNS aux VMs. S’il est absent, pas de DHCP.

Fenêtre de terminal
# Vérifier le processus
ps aux | grep dnsmasq | grep virbr0

Si absent :

Fenêtre de terminal
# Redémarrer le réseau (relance dnsmasq)
virsh net-destroy default
virsh net-start default

Avant de supposer que le DHCP ne marche pas, vérifiez les baux existants :

Fenêtre de terminal
# Voir les IPs attribuées par libvirt
virsh net-dhcp-leases default
# Ou directement le fichier
cat /var/lib/libvirt/dnsmasq/default.leases

Si la VM a un bail mais virsh domifaddr ne montre rien, c’est un problème de source (voir piège ci-dessous).

virsh domifaddr peut renvoyer “rien” alors que la VM a une IP. Par défaut, il utilise la source “lease” qui peut être vide si le bail a expiré ou si la VM a une IP statique.

Testez les différentes sources :

Fenêtre de terminal
virsh domifaddr ma-vm --source lease # depuis les baux DHCP
virsh domifaddr ma-vm --source arp # depuis la table ARP
virsh domifaddr ma-vm --source agent # via qemu-guest-agent

Pour fiabiliser --source agent, installez qemu-guest-agent dans la VM :

Fenêtre de terminal
# Debian/Ubuntu
sudo apt install qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
# RHEL/Rocky/Alma
sudo dnf install qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent

Symptôme : La VM a une IP (192.168.122.x), peut ping la gateway (192.168.122.1), mais pas 8.8.8.8.

  1. Vérifier ip_forward

    Fenêtre de terminal
    cat /proc/sys/net/ipv4/ip_forward
    # Doit être 1

    Si 0 :

    Fenêtre de terminal
    # Activer de façon permanente
    echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ip-forward.conf
    sudo sysctl -p /etc/sysctl.d/99-ip-forward.conf
  2. Vérifier les règles NAT (voir section Firewall backend)

  3. Redémarrer le réseau libvirt pour régénérer les règles :

    Fenêtre de terminal
    virsh net-destroy default
    virsh net-start default

Symptôme : Côté hôte tout est OK (dnsmasq tourne, leases disponibles), mais la VM n’a pas d’IP.

C’est très fréquent avec les cloud images minimalistes ou après modification de la config réseau.

Diagnostic dans la VM :

Fenêtre de terminal
# Interface up ?
ip -br link
# Si DOWN : sudo ip link set eth0 up
# Client DHCP actif ?
# Debian/Ubuntu (systemd-networkd)
systemctl status systemd-networkd
# Debian/Ubuntu (NetworkManager)
nmcli dev status
# RHEL/Rocky/Alma
nmcli connection show

Solutions selon le gestionnaire réseau :

Fenêtre de terminal
# Vérifier la config
cat /etc/systemd/network/*.network
# Exemple de config DHCP
cat << 'EOF' | sudo tee /etc/systemd/network/20-dhcp.network
[Match]
Name=en* eth*
[Network]
DHCP=yes
EOF
# Redémarrer
sudo systemctl restart systemd-networkd

Symptôme : Après clonage d’une VM, le réseau ne fonctionne plus ou est aléatoire.

Causes :

  1. machine-id dupliqué : Le DHCP attribue la même IP à toutes les clones
  2. Cloud-Init ne rejoue pas : La config réseau n’est pas régénérée
  3. Netplan avec config statique : L’ancienne IP/MAC est hardcodée

Solutions :

Fenêtre de terminal
# 1. Régénérer le machine-id
sudo rm /etc/machine-id
sudo systemd-machine-id-setup
# 2. Forcer Cloud-Init à rejouer
sudo cloud-init clean
sudo cloud-init init
# 3. Si netplan : vérifier qu'il n'y a pas de MAC/IP hardcodée
cat /etc/netplan/*.yaml

Sur les distributions récentes, libvirt est modularisé. La partie réseau est gérée par virtnetworkd, pas libvirtd.

Fenêtre de terminal
# Vérifier quel daemon est actif
systemctl status libvirtd # daemon monolithique (ancien)
systemctl status virtnetworkd # daemon réseau (nouveau)
systemctl status virtqemud # daemon QEMU (nouveau)
Fenêtre de terminal
# Daemon modularisé (préféré)
journalctl -u virtnetworkd --since "10 minutes ago"
# Daemon monolithique
journalctl -u libvirtd --since "10 minutes ago"
# Logs dnsmasq (si syslog)
sudo grep dnsmasq /var/log/syslog | tail -20

libvirt gère automatiquement les règles firewall pour le NAT, mais selon le backend (iptables ou nftables), le comportement diffère.

Fenêtre de terminal
# Fedora/RHEL 9+ : nftables par défaut
sudo nft list ruleset | grep -i libvirt | head -5
# Debian/Ubuntu : souvent iptables
sudo iptables -t nat -L -n | grep -i virbr

Les règles essentielles pour le NAT :

RègleRôle
MASQUERADE (POSTROUTING)Traduit les IPs des VMs vers l’IP de l’hôte
ACCEPT (FORWARD)Autorise le trafic des VMs vers l’extérieur
ACCEPT (INPUT)Autorise DHCP (port 67) et DNS (port 53) vers dnsmasq
Fenêtre de terminal
# nftables
sudo nft list chain ip nat POSTROUTING 2>/dev/null | grep -i masq
# iptables
sudo iptables -t nat -S POSTROUTING | grep -i virbr
sudo iptables -S FORWARD | grep -i virbr
Fenêtre de terminal
# Redémarrer le réseau régénère les règles
virsh net-destroy default
virsh net-start default
# Si ça ne suffit pas, redémarrer le daemon réseau
sudo systemctl restart virtnetworkd

Docker et libvirt peuvent entrer en conflit : Docker modifie ip_forward, ajoute ses propres règles NAT, et peut interférer avec les bridges.

Fenêtre de terminal
# Voir tous les bridges
ip -br link | grep -E 'docker0|virbr0|br0'
# Vérifier les règles NAT de Docker vs libvirt
sudo nft list ruleset | grep -nE 'docker|libvirt' | head -20
# ou
sudo iptables -t nat -L -n | grep -E 'DOCKER|virbr'
  1. Docker désactive ip_forward au redémarrage : ajoutez explicitement dans sysctl :

    Fenêtre de terminal
    echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ip-forward.conf
  2. Collision de subnet : Docker utilise 172.17.0.0/16, libvirt 192.168.122.0/24. Normalement pas de conflit, mais vérifiez :

    Fenêtre de terminal
    ip route | grep -E '172.17|192.168.122'
  3. Règles iptables écrasées : redémarrez le réseau libvirt après Docker :

    Fenêtre de terminal
    sudo systemctl restart docker
    virsh net-destroy default && virsh net-start default

Symptôme : IP OK, ping gateway OK, mais rien d’autre ne passe.

C’est souvent le firewall de la VM qui bloque. Vérifiez dans la VM :

Fenêtre de terminal
# iptables
sudo iptables -S | head -20
# nftables
sudo nft list ruleset 2>/dev/null | head -20
# UFW (Ubuntu)
sudo ufw status
# firewalld (RHEL/Fedora)
sudo firewall-cmd --list-all
Fenêtre de terminal
# UFW
sudo ufw disable
# firewalld
sudo systemctl stop firewalld
# iptables (flush)
sudo iptables -F

Symptôme :

error: Failed to start network default
error: internal error: Network is already in use by interface virbr0

Causes :

  1. Le bridge virbr0 existe mais n’est pas géré par libvirt
  2. Collision avec un autre réseau libvirt

Diagnostic :

Fenêtre de terminal
# Voir si virbr0 existe
ip link show virbr0
# Voir la définition du réseau
virsh net-dumpxml default | grep -E 'bridge|ip address'
# Vérifier les routes
ip route | grep 192.168.122

Solution :

Fenêtre de terminal
# Si virbr0 est orphelin, le supprimer
sudo ip link delete virbr0 2>/dev/null
# Puis redémarrer
virsh net-start default

Symptôme : virsh net-list --all ne montre pas “default”.

Fenêtre de terminal
# Vérifier si le fichier XML existe
ls /etc/libvirt/qemu/networks/default.xml
ls /usr/share/libvirt/networks/default.xml
# Recréer depuis le template
virsh net-define /usr/share/libvirt/networks/default.xml
virsh net-start default
virsh net-autostart default

Si le fichier template n’existe pas, créez-le :

Fenêtre de terminal
cat << 'EOF' | sudo tee /tmp/default-network.xml
<network>
<name>default</name>
<forward mode='nat'/>
<bridge name='virbr0' stp='on' delay='0'/>
<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>
EOF
virsh net-define /tmp/default-network.xml
virsh net-start default
virsh net-autostart default

Fenêtre de terminal
# 1. Arrêter toutes les VMs utilisant ce réseau
for vm in $(virsh list --name); do
if virsh domiflist $vm | grep -q default; then
virsh shutdown $vm
fi
done
# 2. Supprimer le réseau
virsh net-destroy default 2>/dev/null
virsh net-undefine default 2>/dev/null
# 3. Supprimer le bridge orphelin
sudo ip link delete virbr0 2>/dev/null
# 4. Recréer
virsh net-define /usr/share/libvirt/networks/default.xml
virsh net-start default
virsh net-autostart default
Fenêtre de terminal
# 1. Supprimer le bridge
sudo nmcli connection delete br0 2>/dev/null
sudo nmcli connection delete bridge-slave-eno1 2>/dev/null
# 2. Réactiver la connexion directe
sudo nmcli connection up "Wired connection 1"

Fenêtre de terminal
# Interfaces réseau
ip -br link
ip -br addr
# Bridges
bridge link show
# Routes
ip route
# Réseaux libvirt
virsh net-list --all
Fenêtre de terminal
# ip_forward
cat /proc/sys/net/ipv4/ip_forward
# Règles NAT
sudo iptables -t nat -L -n -v 2>/dev/null | head -20
sudo nft list chain ip nat POSTROUTING 2>/dev/null
# Processus dnsmasq
ps aux | grep dnsmasq
# Leases DHCP
virsh net-dhcp-leases default
cat /var/lib/libvirt/dnsmasq/default.leases
Fenêtre de terminal
# Membres du bridge
bridge link show br0
# Adresses sur le bridge
ip addr show br0
# STP
cat /sys/class/net/br0/bridge/stp_state

  1. Séparez Host vs Guest : la moitié des pannes sont côté VM (firewall, DHCP client, DNS)
  2. virsh domifaddr peut mentir : utilisez --source lease/arp/agent ou virsh net-dhcp-leases
  3. virtnetworkd remplace libvirtd pour le réseau sur les distros modernes
  4. Les règles firewall sont régénérées au redémarrage du réseau libvirt
  5. Docker peut interférer : vérifiez ip_forward et l’ordre des règles NAT
  6. “virbr0 in use” = bridge orphelin ou collision de réseau
  7. Le firewall du guest est souvent le coupable quand “tout est OK côté hôte”
  8. Testez toujours : ping gateway → ping 8.8.8.8 → nslookup

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.