Aller au contenu
Virtualisation medium

Réseau KVM/libvirt : NAT vs Bridge, comprendre et configurer

50 min de lecture

Logo KVM

Vos VMs n'ont pas de réseau ? Vous ne savez pas si vous devez utiliser NAT ou Bridge ? Ce guide vous explique le modèle réseau libvirt et vous aide à configurer le bon mode.

SituationModeCe que ça fait
Lab / développementNAT (défaut)VMs isolées, accès Internet via l'hôte
Services exposés / productionBridgeVMs directement sur le LAN
  • La différence entre L2 (bridge) et L3 (NAT/routage)
  • Le fonctionnement du réseau NAT libvirt (virbr0 + dnsmasq)
  • Comment créer un bridge pour exposer vos VMs
  • Les commandes virsh essentielles pour le réseau

Avant de configurer, comprenez comment les paquets circulent.

CoucheCe qu'elle gèreIdentifiantÉquipement
L2 (liaison)Trames sur le même réseauAdresse MACSwitch
L3 (réseau)Paquets entre réseauxAdresse IPRouteur

Le point clé :

  • Un bridge = switch logiciel = travaille en L2 (il regarde les MAC)
  • Le NAT = travaille en L3 (il réécrit les IP)

Confusion fréquente : virbr0 est un bridge (L2). Le NAT vient de :

  1. <forward mode='nat'/> dans la définition XML
  2. Des règles firewall ajoutées automatiquement par libvirt

Le bridge virbr0 est juste le switch interne qui connecte vos VMs entre elles et à l'hôte.

Après installation de libvirt, un réseau "default" est créé automatiquement :

VM (eth0) → vnet0 → virbr0 → règles firewall (NAT) → interface physique → Internet

Ce que libvirt crée pour vous :

ComposantRôle
virbr0Bridge virtuel (switch L2 interne)
dnsmasqServeur DHCP + DNS pour les VMs
Règles firewallNAT (MASQUERADE) pour l'accès Internet

Les VMs obtiennent une IP en 192.168.122.x, avec l'hôte comme passerelle (192.168.122.1).

libvirt manipule quatre objets distincts que les messages d'erreur mélangent volontiers. Retenez surtout la différence entre le Virtual Network (l'objet que vous pilotez avec virsh net-*) et le Bridge (l'interface Linux qu'il crée) : quand une commande virsh échoue, elle parle du premier, quand ip ou bridge échoue, du second. Les interfaces tap de la dernière ligne apparaissent et disparaissent au démarrage et à l'arrêt de chaque VM, il est normal de ne pas les voir sur un hôte au repos.

ObjetCe que c'estExemple
Virtual NetworkRéseau géré par libvirtdefault (NAT)
BridgeSwitch virtuelvirbr0
vNICCarte réseau d'une VMDéfinie dans le XML
tapInterface hôte connectée à une vNICvnet0, vnet1

La ligne qui tranche réellement est Accès depuis le LAN. Toutes les autres différences en découlent : si personne d'autre que l'hôte n'a besoin de joindre vos VMs, le NAT suffit et vous évite de toucher à la configuration réseau de la machine. Dès qu'un collègue, un client ou une sonde de supervision doit atteindre une VM par son IP, il faut basculer en bridge ou ouvrir du port forwarding, et le bridge est alors la solution la plus simple à maintenir. La ligne Configuration vous dit ce que coûte ce choix.

CritèreNAT (default)Bridge
ConfigurationAutomatiqueManuelle
IP des VMsPrivées (192.168.122.x)LAN (même réseau que l'hôte)
Accès depuis le LANNon (sauf port forward)Oui
IsolationVMs isoléesVMs exposées
Cas d'usageLab, développementProduction, services

En mode bridge, chaque VM apparaît comme une machine physique sur le LAN. Elle obtient donc une IP du réseau (via DHCP ou statique). Si vous avez un plan d'adressage limité, c'est un point à considérer.

Première commande à lancer sur un hôte fraîchement installé : elle dit si le réseau default existe et s'il tourne. L'option --all est importante, sans elle un réseau défini mais arrêté n'apparaît pas et vous pourriez conclure à tort qu'il a disparu.

Fenêtre de terminal
virsh net-list --all

Sortie réelle :

Name State Autostart Persistent
--------------------------------------------
default active yes yes

Décryptage :

ColonneSignification
State: activeLe réseau est démarré, les VMs peuvent l'utiliser
Autostart: yesDémarre automatiquement au boot de l'hôte
Persistent: yesDéfini de façon permanente (pas juste en mémoire)

Si le réseau n'est pas actif :

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

net-info donne la vue résumée d'un réseau. La seule information que vous ne pouviez pas deviner depuis net-list est le nom du bridge : c'est le pont entre le monde libvirt et les commandes système classiques (ip, bridge, iptables), et c'est donc la valeur à noter avant tout diagnostic.

Fenêtre de terminal
virsh net-info default

Sortie réelle :

Name: default
UUID: 3f61d934-34f7-4f6d-b394-b186699c16f2
Active: yes
Persistent: yes
Autostart: yes
Bridge: virbr0

Le point clé : Bridge: virbr0, c'est l'interface sur laquelle vos VMs seront connectées.

Le XML est la source de vérité : tout ce que fait libvirt sur votre hôte (bridge, plage DHCP, règles NAT) découle de ce document. C'est aussi le seul endroit où lire la plage d'adresses réellement distribuée, une information absente des deux commandes précédentes. Sur un hôte déjà utilisé, vous y verrez apparaître des lignes supplémentaires, notamment des réservations DHCP par adresse MAC.

Fenêtre de terminal
virsh net-dumpxml default

Sortie réelle :

<network>
<name>default</name>
<uuid>3f61d934-34f7-4f6d-b394-b186699c16f2</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:a5:f2:39'/>
<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>

Décryptage ligne par ligne :

Élément XMLCe que ça fait
<forward mode='nat'>Active le NAT (MASQUERADE via iptables)
<port start='1024' end='65535'/>Ports sources utilisés pour le NAT
<bridge name='virbr0'/>Nom du bridge virtuel
<ip address='192.168.122.1'>IP de l'hôte sur ce réseau (passerelle)
<dhcp><range.../>Plage DHCP : .2 à .254 (253 IPs disponibles)

Deux écritures pour le même résultat. Utilisez --network network=default à la création de la VM ; le bloc XML sert quand vous modifiez une VM existante avec virsh edit. Dans les deux cas, on référence le nom du réseau libvirt, pas le nom du bridge : c'est ce qui permet à libvirt de démarrer le réseau tout seul si la VM boote avant lui. Le <model type='virtio'/> n'est pas décoratif, il choisit une carte réseau paravirtualisée nettement plus rapide que l'émulation par défaut.

Avec virt-install :

Fenêtre de terminal
virt-install --network network=default ...

Ou dans le XML de la VM :

<interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>

Les trois contrôles ci-dessous suivent le chemin d'un paquet sortant : d'abord le bridge qui reçoit la trame, puis dnsmasq qui a distribué l'adresse, enfin les règles NAT qui réécrivent l'IP source. Menez-les dans cet ordre lors d'un diagnostic ; s'arrêter au premier qui échoue évite de chercher un problème de firewall alors que le bridge n'existe même pas.

Le bridge virbr0 existe ?

Fenêtre de terminal
ip addr show virbr0

Sortie réelle :

178: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 52:54:00:a5:f2:39 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
valid_lft forever preferred_lft forever

Décryptage :

  • state DOWN + NO-CARRIER = normal si aucune VM n'est connectée
  • inet 192.168.122.1/24 = l'hôte est la passerelle pour les VMs

Le serveur DHCP (dnsmasq) tourne ?

Fenêtre de terminal
ps aux | grep dnsmasq | grep libvirt

Sortie réelle :

libvirt+ 3754413 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf ...

libvirt lance automatiquement dnsmasq pour fournir DHCP et DNS aux VMs.

Les règles NAT sont en place ?

Fenêtre de terminal
sudo iptables -t nat -L LIBVIRT_PRT -n -v

Sortie réelle :

Chain LIBVIRT_PRT (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN 0 -- * * 192.168.122.0/24 224.0.0.0/24
0 0 MASQUERADE 6 -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE 17 -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE 0 -- * * 192.168.122.0/24 !192.168.122.0/24

Décryptage : le trafic venant de 192.168.122.0/24 (vos VMs) vers l'extérieur est "masqué" (MASQUERADE = NAT dynamique).

Une fois votre VM démarrée et connectée au réseau default :

Fenêtre de terminal
# Dans la VM
ip addr # IP en 192.168.122.x ?
ping -c 2 192.168.122.1 # Passerelle (l'hôte)
ping -c 2 1.1.1.1 # Internet
host google.com # DNS (fourni par dnsmasq)

En mode bridge, vos VMs sont directement connectées au réseau physique.

Avant de créer quoi que ce soit, il faut savoir quelle carte porte réellement votre trafic. La commande filtre le bruit habituel (lo, les bridges Docker, les interfaces virtuelles) pour ne laisser que les cartes physiques. Le mot à chercher dans la sortie est UP : une carte DOWN n'a pas de câble ou n'est pas configurée, la mettre dans un bridge vous couperait le réseau.

Fenêtre de terminal
ip link show | grep -E '^[0-9]+:' | grep -v -E 'lo|virbr|docker|veth|br-'

Sortie exemple :

2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ... state UP ...
3: enp3s0: <BROADCAST,MULTICAST> mtu 1500 ... state DOWN ...

L'interface UP (ici enp2s0) est celle connectée au réseau.

C'est la méthode standard sur Ubuntu Server. Vérifiez votre config actuelle :

Fenêtre de terminal
cat /etc/netplan/*.yaml

Exemple de config actuelle :

network:
ethernets:
enp2s0:
dhcp4: true
version: 2

Créer un bridge : modifiez le fichier (ou créez /etc/netplan/01-bridge.yaml) :

network:
version: 2
ethernets:
enp2s0:
dhcp4: false # Plus de DHCP sur l'interface physique
bridges:
br0:
interfaces: [enp2s0] # L'interface physique rejoint le bridge
dhcp4: true # Le bridge obtient l'IP
# OU pour IP statique :
# addresses: [192.168.1.100/24]
# routes:
# - to: default
# via: 192.168.1.1
# nameservers:
# addresses: [192.168.1.1, 8.8.8.8]

Appliquer (avec rollback automatique si échec) :

Fenêtre de terminal
sudo netplan try # Applique pendant 120s, rollback si pas confirmé
# Si OK, tapez ENTER pour confirmer
sudo netplan apply # Application définitive

Méthode 2 : NetworkManager (Fedora, RHEL, Ubuntu Desktop)

Section intitulée « Méthode 2 : NetworkManager (Fedora, RHEL, Ubuntu Desktop) »

Sur les distributions pilotées par NetworkManager, tout se fait avec nmcli et rien n'est écrit à la main dans un fichier. L'étape sensible est la quatrième : couper la connexion filaire actuelle avant d'activer le bridge fait tomber le réseau pendant une à deux secondes, et vous perdez la session SSH si vous n'avez pas d'accès console. Le nom "Wired connection 1" est celui affiché par nmcli connection show sur votre machine, remplacez-le par le vôtre.

  1. Identifier votre interface physique

    Fenêtre de terminal
    nmcli device status
  2. Créer le bridge

    Fenêtre de terminal
    sudo nmcli connection add type bridge ifname br0 con-name br0
    sudo nmcli connection modify br0 ipv4.method auto # ou manual pour IP statique
  3. Connecter l'interface physique au bridge

    Fenêtre de terminal
    sudo nmcli connection add type bridge-slave ifname enp2s0 master br0
  4. Activer le bridge

    Fenêtre de terminal
    sudo nmcli connection down "Wired connection 1" # Connexion actuelle
    sudo nmcli connection up br0
  5. Vérifier

    Fenêtre de terminal
    ip addr show br0 # Le bridge a l'IP
    bridge link show # enp2s0 est connecté au bridge

Un bridge fonctionnel se reconnaît à deux signes, et il faut les deux. Premier signe : l'IP a migré de la carte physique vers br0, ce qui veut dire que enp2s0 n'a plus d'adresse. Second signe : la carte physique est déclarée master br0 et son état est forwarding. Un bridge qui a l'IP mais aucun esclave ne fait passer aucun trafic, c'est la panne la plus fréquente à ce stade.

Fenêtre de terminal
ip addr show br0

Sortie attendue :

XX: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ... state UP ...
inet 192.168.1.100/24 ... # L'IP est sur le bridge, pas sur enp2s0
Fenêtre de terminal
bridge link show

Sortie attendue :

2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> ... master br0 state forwarding

L'interface physique (enp2s0) est maintenant esclave du bridge (master br0).

Si vous voulez utiliser --network network=... plutôt que --network bridge=... :

Fenêtre de terminal
cat > /tmp/host-bridge.xml << 'EOF'
<network>
<name>host-bridge</name>
<forward mode="bridge"/>
<bridge name="br0"/>
</network>
EOF
virsh net-define /tmp/host-bridge.xml
virsh net-start host-bridge
virsh net-autostart host-bridge

Contrairement au mode NAT, on référence ici le nom de l'interface système (br0) et non un objet libvirt, sauf si vous avez déclaré le réseau host-bridge juste avant. Le type d'interface change aussi dans le XML : type='bridge' avec <source bridge='...'/>, là où le NAT utilisait type='network'. Confondre les deux produit une VM qui démarre mais reste sans réseau, sans message d'erreur au démarrage.

Option directe (recommandée) :

Fenêtre de terminal
virt-install --network bridge=br0 ...

Ou dans le XML :

<interface type='bridge'>
<source bridge='br0'/>
<model type='virtio'/>
</interface>

Le test qui compte n'est pas celui lancé depuis la VM, mais celui lancé depuis une autre machine du LAN. Une VM en bridge peut très bien joindre sa passerelle tout en restant injoignable de l'extérieur, par exemple si son pare-feu invité bloque l'ICMP ou si le switch physique filtre les adresses MAC inconnues. Faites les trois vérifications dans l'ordre : le bridge côté hôte, puis la VM vers le LAN, puis le LAN vers la VM.

Fenêtre de terminal
# Sur l'hôte : la VM est connectée au bridge
bridge link show br0
# Doit montrer : enp2s0 + vnetX
# Depuis la VM
ip addr # IP du LAN (ex: 192.168.1.x)
ping 192.168.1.1 # Passerelle du LAN
# Depuis une autre machine du LAN
ping <ip-de-la-vm> # Doit répondre

Ces sept commandes couvrent l'essentiel du travail quotidien sur le réseau libvirt. Les cinq premières agissent sur un réseau, les deux dernières sur une VM : c'est la distinction à garder en tête quand une commande répond « network not found » alors que vous lui avez passé un nom de machine. Une précision sur virsh domifaddr : par défaut il lit les baux DHCP de libvirt, ce qui fonctionne pour une VM sur le réseau NAT sans rien installer dedans. C'est seulement pour une VM en bridge, dont le bail est délivré par le DHCP du LAN, qu'il faut passer par --source agent (agent invité installé) ou --source arp.

CommandeDescriptionExemple de sortie
virsh net-list --allLister les réseauxdefault active yes yes
virsh net-info <nom>Détails d'un réseauBridge, UUID, état
virsh net-dumpxml <nom>Configuration XML complèteVoir plus haut
virsh net-start <nom>Démarrer un réseau
virsh net-autostart <nom>Activer au boot
virsh domiflist <vm>Interfaces d'une VMvnet0 bridge virbr0
virsh domifaddr <vm>IP d'une VMLit les baux DHCP par défaut ; --source agent ou arp en bridge

Ce tableau se lit de haut en bas comme un arbre de décision : la première ligne traite le cas où libvirt ne vous montre rien du tout, les suivantes supposent le réseau visible mais inopérant. La colonne Diagnostic contient la commande à exécuter avant de toucher à quoi que ce soit, car la moitié des pannes réseau en KVM se règlent en constatant qu'on travaille sur la mauvaise URI de connexion.

SymptômeDiagnosticSolution
virsh net-list videvirsh uriqemu:///sessionConfigurer LIBVIRT_DEFAULT_URI
Réseau default inactifvirsh net-list → State inactivevirsh net-start default
VM n'obtient pas d'IPps aux | grep dnsmasqVérifier que dnsmasq tourne
VM pas de sortie Internetsudo iptables -t nat -LVérifier règles MASQUERADE
Bridge br0 sans IPip addr show br0L'interface physique n'est pas esclave
  1. NAT = réseau privé (192.168.122.x), VMs isolées, accès Internet via l'hôte. C'est le défaut libvirt.

  2. Bridge = VMs sur le LAN, chaque VM consomme une IP, nécessite configuration manuelle.

  3. virbr0 est un bridge L2. Le NAT vient de <forward mode='nat'/> + règles iptables (MASQUERADE).

  4. dnsmasq fournit DHCP + DNS aux VMs en mode NAT.

  5. Netplan (Ubuntu) ou NetworkManager (Fedora/RHEL) pour créer un bridge.

  6. Toujours avoir un accès console avant de modifier le réseau.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn