14. Communications réseau en mode commutation virtuelle

Dans ce contexte, l'objectif est de faire communiquer les machines virtuelles entre elles. Pour ce faire, on utilise un commutateur virtuel lui aussi : Virtual Distributed Ethernet. Ce commutateur est un processus utilisateur appelé vde_switch. Il est fourni par le paquet vde2. Voici un exemple de topologie réseau utilisant vde_switch.

[Note] Note

C'est cette topologie de base que j'utilise systématiquement pour les travaux pratiques de la série Administration système en réseau. En effet, on retrouve une machine virtuelle de type serveur et une autre de type client. Le système hôte joue le rôle de passerelle donnant accès à l'Internet (et|ou) à un système distant.

On trouve ci-dessous les attributions d'adresses IPv4 en fonction des types d'interfaces. Bien entendu, ces adresses ne sont que des exemples utiles à l'interprétation des étapes de mise en place de la topologie illustrée.

Tableau 2. Plan d'adressage IPv4 pour les communications point à point

Système Interface Adresse IPv4
hôte eth0 192.168.1.1/24
hôte tap0 192.0.2.1/24
machine virtuelle «serveur» eth0 192.0.2.2/24
machine virtuelle «client» eth0 192.0.2.3/24
distant eth0 192.168.1.7/24

14.1. Configuration manuelle

Cette section reprend la configuration manuelle de l'interface TAP (Voir Section 11.1, « Configuration manuelle d'une interface TAP ») à partir d'un compte utilisateur normal et décrit son «brassage» sur le commutateur virtuel. La configuration du commutateur Virtual Distributed Ethernet est elle aussi réalisée avec les droits du compte utilisateur normal.

Interface TAP

La configuration de l'interface TAP sur le système hôte reprend les éléments présentés précédemment Section 11, « Fonction TUN/TAP du noyau Linux ».

$ ./scripts/tap-up.sh tap0 192.0.2.1/24
$ /sbin/ifconfig tap0
tap0      Link encap:Ethernet  HWaddr 00:ff:8c:db:de:99
          inet adr:192.0.2.1  Bcast:192.0.2.255  Masque:255.255.255.0
          adr inet6: fe80::2ff:8cff:fedb:de99/64 Scope:Lien
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:47 overruns:0 carrier:0
          collisions:0 lg file transmission:100
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
Table de routage et traduction d'adresses (NAT)

L'objectif principal des opérations présentées ici est de «préserver» la table de routage du système hôte. En effet, ce système correspond le plus souvent à un ordinateur portable utilisé pour les enseignements. La connexion au réseau public de ce portable peut utiliser plusieurs types d'interfaces différentes : Ethernet filaire, Wifi (et|ou) VPN. Il faut donc que les instances de machines virtuelles puissent avoir accès aux réseaux réels externes via n'importe laquelle de ces différentes interfaces.

On commence donc par activer le routage des paquets IPv4 au niveau du noyau Linux manuellement ou au niveau système dans le fichier de configuration de sysctl.

# echo 1 >/proc/sys/net/ipv4/ip_forward
# grep ip_forward /etc/sysctl.conf
net.ipv4.ip_forward=1

Ensuite, on utilise les fonctions de traduction d'adresses IP sources (S-NAT) dynamiques du noyau Linux à l'aide de l'outil de configuration ipatbles.

# iptables -t nat -A POSTROUTING -s 192.0.2.0/24 -j MASQUERADE

La cible MASQUERADE permet l'utilisation dynamique de n'importe quelle interface de sortie du système hôte vers le réseau public. La traduction d'adresses se fait sur la base des adresses IP sources du réseau des instances de machines virtuelles desservi par le commutateur vde_switch. Ces adresses sources sont désignées à l'aide de l'option -s 192.0.2.0/24.

On valide le fonctionnement de la traduction d'adresses en consultant l'état des chaînes correspondantes.

# iptables -vL -t nat
Chain PREROUTING (policy ACCEPT 56 packets, 4650 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 220 packets, 20710 bytes)
 pkts bytes target     prot opt in     out     source               destination
   32  1966 MASQUERADE  all  --  any    any     192.0.2.0/24       anywhere

Chain OUTPUT (policy ACCEPT 220 packets, 20710 bytes)
 pkts bytes target     prot opt in     out     source               destination

Il est aussi possible, avec des outils comme iptsate, de visualiser en temps réel l'état des tables de suivi de communication du système de filtrage netfilter/iptables.

Commutateur virtuel, Virtual Distributed Ethernet

Le commutateur virtuel vde_switch doit être lancé avant les instances de machines virtuelles pour que le brassage des connexions puisse se faire. Voici un exemple de commande de lancement du commutateur suivi de la visualisation du socket et du processus correspondant.

$ vde_switch -d --tap tap01 -s /tmp/vde.ctl2 -M /tmp/vde.mgmt3

$ ls -lAh /tmp/vde*4
srw------- 1 phil phil  0 jun  1 17:29 /tmp/vde.mgmt

/tmp/vde.ctl:
total 0
srwx------ 1 phil phil 0 jun  1 17:29 ctl

$ pgrep -l -u phil vde5
11198 vde_switch

1

Avec l'option --tap, le commutateur virtuel est «connecté» au système hôte via une interface TAP comme dans le cas des communications point à point. Cette connexion correspond au lien vers le réseau externe «réel» : le réseau local du système hôte et l'Internet. Le réseau local auquel appartient cette interface TAP apparaît dans la table de routage du système hôte.

# route -n
Table de routage IP du noyau
Destination     Passerelle      Genmask         Indic Metric Ref    Use Iface
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth1
192.0.2.0     0.0.0.0         255.255.255.0   U     0      0        0 tap0
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth1

2

L'option -s désigne le socket de communication sur lequel les machines virtuelles vont se brancher. Ce socket sert au brassage des connexions de toutes les machines virtuelles.

3

L'option -M désigne le socket de gestion de la configuration du commutateur virtuel. Ces opérations se font via ce socket dédié. Le paquet vde2 fournit un programme utilisateur pour l'utiliser : vdeterm.

4 5

On observe que l'utilisateur normal phil est propriétaire des sockets de communication et de gestion du commutateur. Comme les machines virtuelles sont elles même des processus utilisateur, il est nécessaire que ces processus aient la même identité (uid) que le socket.

Il est possible de combiner le script d'initialisation de l'interface TAP (voir Section 11, « Fonction TUN/TAP du noyau Linux ») et le lancement du commutateur virtuel à l'aide du script suivant :

#!/bin/bash

# $Id: vde-up.sh 1305 2008-06-08 20:59:09Z latu $
# Run this virtual ethernet switch setup script before KVM/QEMU launch
# These scripts should be located in ~/bin as they are run at normal user level

if [ -z $1 ] || [ -z $2 ]; then
        echo "Usage: vde-up.sh {tap interface} {IP address}"
        exit 1
fi

~/bin/tap-up.sh $1 $2
vde_switch -d --tap $1 -s /tmp/vde.ctl -M /tmp/vde.mgmt

Avec cette combinaison de scripts, la séquence d'initialisation de l'interface TAP et du commutateur virtuel associé devient :

$ vde-up.sh tap0 192.0.2.1/2
Instances de machines virtuelles

Le lancement des machines virtuelles sur le système hôte se fait en fonction du brassage à réaliser. Il faut désigner le socket de communication avec le commutateur virtuel et l'interface de réseau local utilisée par la machine virtuelle.

$ qemu \
-drive file=vm0-debian-i386.raw,if=virtio,media=disk,boot=on \
-m 128 \
-net vde,sock=/tmp/vde.ctl1 \
-net nic,vlan=0,macaddr=52:54:00:12:34:622 &

$ qemu \
-drive file=vm1-debian-i386.raw,if=virtio,media=disk,boot=on \
-m 128 \
-net vde,sock=/tmp/vde.ctl3 \
-net nic,vlan=0,macaddr=52:54:00:12:34:634 & 

1 3

Quelle que soit l'instance de machine virtuelle initialisée, on utilise le socket unique de communication avec le commutateur virtuel. Dans cet exemple, le brassage des ports du commutateur virtuel se fait dynamiquement dans l'ordre des connexions libres. Les informations de brassage sont visualisées dans la Configuration du commutateur virtuel et les deux instances lancées occupent les ports numérotés 002 et 003.

Ces informations se retrouvent aussi au niveau de l'arborescence des sockets utilisateur.

$ ls -lAh /tmp/vde*
srwx------ 1 phil phil  0 jun  1 17:53 /tmp/vde.ctl.19747-00000
srwx------ 1 phil phil  0 jun  1 18:03 /tmp/vde.ctl.21781-00000
srw------- 1 phil phil  0 jun  1 17:29 /tmp/vde.mgmt

/tmp/vde.ctl:
total 0
srwx------ 1 phil phil 0 jun  1 17:53 002
srwx------ 1 phil phil 0 jun  1 18:03 003
srwx------ 1 phil phil 0 jun  1 17:29 ctl

Dans l'exemple ci-dessus, les valeurs 19747 et 21781 correspondent aux numéros des deux processus de machine virtuelle sur le système hôte.

2 4

Si les interfaces Ethernet des machines virtuelles sont configurées au niveau du système d'exploitation invité, il est malgré tout nécessaire de préciser le paramétrage matériel de ces interfaces au lancement d'une instance. En effet, l'adresse physique MAC d'une machine virtuelle est toujours la même par défaut. C'est la raison pour laquelle chaque machine virtuelle est lancée avec une adresse MAC distincte. Il faut se rappeler que la commutation Ethernet réelle ou virtuelle est basée sur la constitution d'une table de correspondance entre adresses MAC et numéro de port. L'état de cette table de correspondance est disponible via l'interface de gestion du commutateur vde_switch. Voir Configuration du commutateur virtuel.

De plus, il est possible d'utiliser des réseaux locaux virtuels (VLANs) en constituant des groupes logiques de ports de commutateur. Ici, les deux instances de machines virtuelles appartiennent au même groupe logique : le VLAN numéro 0.

Configuration du commutateur virtuel

La visualisation et la gestion de la configuration du commutateur virtuel sur le système hôte se fait à l'aide d'un socket dédié présenté lors du lancement du processus vde_switch (Voir Commutateur virtuel).

On accède à l'interface de gestion du commutateur virtuel via vdeterm, un outil fourni avec le paquet vde2.

Le jeu des commandes disponibles sur la configuration du commutateur est disponible à partir de la commande help. Voici quelques exemples basiques de commandes de visualisation de l'état des connexions.

$ vdeterm /tmp/vde.mgmt
VDE switch V.2.2.0-pre2
(C) Virtual Square Team (coord. R. Davoli) 2005,2006,2007 - GPLv2

vde$ showinfo1
0000 DATA END WITH '.'
VDE switch V.2.2.0-pre2
(C) Virtual Square Team (coord. R. Davoli) 2005,2006,2007 - GPLv2

pid 14433 MAC 00:ff:c6:46:c5:14 uptime 2079
mgmt /tmp/vde.mgmt perm 0600
unsent_pktq_len 0
.
1000 Success

vde$ port/allprint2
0000 DATA END WITH '.'
Port 0001 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
 IN:  pkts       2218          bytes              2966493
 OUT: pkts       1480          bytes               106042
  -- endpoint ID 0008 module tuntap      : tap0
Port 0002 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
 IN:  pkts       1518          bytes               109167
 OUT: pkts       2292          bytes              2972334
  -- endpoint ID 0009 module unix prog   : vdeqemu user=phil PID=19747\
  SOCK=/tmp/vde.ctl.19747-00000
Port 0003 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
 IN:  pkts         73          bytes                 5788
 OUT: pkts         48          bytes                 3893
  -- endpoint ID 0011 module unix prog   : vdeqemu user=phil PID=21781\
  SOCK=/tmp/vde.ctl.21781-00000
.
1000 Success

vde$ hash/print3
0000 DATA END WITH '.'
Hash: 0048 Addr: 52:54:00:12:34:63 VLAN 0000 to port: 003  age 1 secs
Hash: 0088 Addr: 52:54:00:12:34:62 VLAN 0000 to port: 002  age 1 secs
.
1000 Success

1

La commande showinfo donne les paramètres du socket de gestion dédié et l'adresse MAC interne du commutateur.

2

La commande port/allprint donne la liste des ports actifs du commutateur ainsi que les paramètres associés : numéro de port et de VLAN, interface ou socket associé au port, référence du processus utilisateur de machine virtuelle.

3

La commande hash/print affiche la table de correspondance entre les adresses MAC et les numéros de ports actifs. Tout comme dans une commutateur réel, cette table est rafraîchie dynamiquement en fonction du trafic qui transite sur un port.

On retrouve ici les deux adresses MAC données lors du lancement des instances de machines virtuelles Instances de machines virtuelles.

14.2. Configuration système

Cette section reprend les mêmes étapes que la précédente, en utilisant cette fois ci les paramètres de configuration fournis avec les paquets de la distribution Debian GNU/Linux.

Interface TAP

La configuration système de l'interface TAP sur la machine hôte reprend les éléments présentés précédemment dans la Section 11.3, « Configuration Debian d'une interface TAP ».

auto tap0
iface tap0 inet static
        address 192.0.2.1
        netmask 255.255.255.0
        network 192.0.2.0
        broadcast 192.0.2.255
        vde2-switch -
Table de routage et traduction d'adresses (NAT)

Pour activer systématiquement la fonction de routage des paquets IP dans le noyau Linux, on utilise le fichier de configuration /etc/sysctl.conf dans lequel on donne la valeur 1 à la variable net.ipv4.ip_forward.

# grep ip_forward /etc/sysctl.conf
net.ipv4.ip_forward=1

Pour appliquer les règles de filtrage et de traduction d'adresses sources, il est possible de créer un script exécuté lors de l'initialisation des interfaces réseau du système. Ce script est placé dans le répertoire /etc/network/if-up.d/. Voici un exemple.

# cat /etc/network/if-up.d/iptables
#!/bin/sh

set -e

daemon="/sbin/iptables"
name="iptables"
save="/sbin/iptables-save"
restore="/sbin/iptables-restore"
active="/var/lib/iptables/active"

test -x "$daemon" -a -x "$save" -a -x "$restore" || exit 0

# Load iptables rules
test -f $active || exit 0
echo -n "Starting $name: "
$restore < "$active"
echo "loaded active state"

D'après ce script, les règles doivent êtres placées dans le fichier /var/lib/iptables/active. En sauvegardant le résultat de la commande iptables-save dans ce fichier, on obtient un jeu de règles du type suivant :

# cat /var/lib/iptables/active
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A POSTROUTING -p tcp -m tcp --syn -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu
-A POSTROUTING -o wlan0 -j MASQUERADE
-A POSTROUTING -o eth0 -j MASQUERADE
COMMIT

Bien sûr, ces règles ne répondent qu'au contexte étudié. Elles n'ont aucune valeur en dehors de ce même contexte.

Commutateur virtuel, Virtual Distributed Ethernet

Ici, le commutateur virtuel vde_switch est lancé automatiquement lors de l'initialisation de l'interface TAP. Le paramétrage est imposé comme indiqué dans la Section 11.3, « Configuration Debian d'une interface TAP ». Relativement à la section précédente sur la configuration manuelle, on remarque que l'accès au commutateur virtuel est réservé aux membres du groupe vde2-net.

$ ls -lAh /var/run/vde2/*
srw-rw---- 1 root vde2-net    0 sep  3 06:40 /var/run/vde2/tap0.mgmt
-rw-r--r-- 1 root vde2-net    5 sep  3 06:40 /var/run/vde2/tap0.pid

/var/run/vde2/tap0.ctl:
total 0
srw-rw---- 1 root vde2-net 0 sep  3 06:40 ctl
Instances de machines virtuelles

Le lancement des machines virtuelles suit le même principe que précédemment. Il faut cependant adapter les références du «brassage» des connexions virtuelles aux nouvelles références de sockets. Le chemin utilisé est correspond à la copie d'écran ci-dessus : /var/run/vde2/tap0.ctl.

Pour automatiser le lancement d'une instance de machine virtuelle on peut utiliser le script suivant. Les deux paramètres à fournir lorsque l'on utilise ce script sont le nom de l'image et le numéro du port du commutateur utilisé. Si le numéro de port n'est pas donné, le port 2 est utilisé par défaut.

#!/bin/bash
# $Id: startup.sh 1614 2011-03-17 22:41:04Z latu $

#RedOnBlack='\E[31;40m'
RedOnBlack='\E[31m'

vm=$1
shift
memory=$1
shift
port=$1
shift

if [[ -z "$vm" || -z "$memory" || -z "$port" ]]
then
        echo "ERREUR : paramètre manquant"
        echo "Utilisation : $0 <fichier image> <quantité mémoire en Mo> <port commutateur [2..32]>"
        exit 1
fi

if (( $memory < 128 ))
then
        echo "ERREUR : quantité de mémoire RAM insuffisante"
        echo "La quantité de mémoire en Mo doit être supérieure ou égale à 128"
        exit 1
fi

macaddress="52:54:00:12:34:$port"

echo -e "$RedOnBlack"
echo "~> Machine virtuelle : $vm"
echo "~> Mémoire RAM       : $memory"
echo "~> Port commutateur  : $port"
echo "~> Adresse MAC       : $macaddress"
tput sgr0

#  -vga vmware \
kvm \
  -daemonize \
  -name $vm \
  -m $memory \
  -rtc base=localtime,clock=host \
  -drive file=$vm,if=virtio,media=disk,boot=on \
  -k fr \
  -usb -usbdevice tablet \
  -soundhw es1370 \
  -net vde,vlan=1,sock=/var/run/vde2/tap0.ctl,port=$port \
  -net nic,vlan=1,model=virtio,macaddr=$macaddress \
  $*
Configuration du commutateur virtuel

La visualisation et la gestion de la configuration du commutateur virtuel sur le système hôte se fait à l'aide d'un socket dédié affecté à l'aide de l'option -M lors du lancement du processus vde_switch. Cette affectation se fait via un script fourni avec le paquet vde2 : /etc/network/if-pre-up.d/vde2.

Voici un exemple d'utilisation de l'interface de gestion et de configuration du commutateur virtuel basé sur l'utilisation de trois instances de machines virtuelles dont les interfaces réseau sont «brassées» sur des VLANs différents.

  • On commence par créer un fichier image «différentiel» pour chaque instance à partir du fichier image maître choisi.

    $ ./scripts/diff-img.sh vm0-debian-stable-amd64-base.raw r1.raw
    $ ./scripts/diff-img.sh vm0-debian-stable-amd64-base.raw r2.raw
    $ ./scripts/diff-img.sh vm0-debian-stable-amd64-base.raw r3.raw
    
  • On procède au lancement de chaque instance en affectant un numéro de port différent sur le commutateur virtuel.

    $ ./scripts/startup.sh r1.raw 512 2
    $ ./scripts/startup.sh r2.raw 512 3
    $ ./scripts/startup.sh r3.raw 512 4
    
  • Une fois les instances actives et après avoir validé les communications au niveau réseau à l'aide de la commande ping, on peut visualiser le résultats sur l'interface du commutateur. L'équivalent de la Table CAM du commutateur virtuel est donné par la commande hash/print.

    $ vdeterm /var/run/vde2/tap0.mgmt
    VDE switch V.2.2.3
    (C) Virtual Square Team (coord. R. Davoli) 2005,2006,2007 - GPLv2
    
    vde$ hash/print
    0000 DATA END WITH '.'
    Hash: 0010 Addr: 52:54:00:12:34:02 VLAN 0000 to port: 002  age 21 secs
    Hash: 0016 Addr: ca:d9:0c:3a:a2:f1 VLAN 0000 to port: 001  age 21 secs
    Hash: 0034 Addr: 52:54:00:12:34:04 VLAN 0000 to port: 004  age 13 secs
    Hash: 0094 Addr: 52:54:00:12:34:03 VLAN 0000 to port: 003  age 13 secs
    .
    1000 Success
    

    Dans la copie d'écran ci-dessus, on retrouve les valeurs choisies pour distinguer les adresses MAC et les numéros de ports.

  • Pour obtenir des informations détaillées sur la correspondance entre les ports de commutation et les sockets de communication des instances de systèmes d'exploitation, on utilise la commande port/print.

    vde$ port/print
    0000 DATA END WITH '.'
    Port 0001 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
     Current User: NONE Access Control: (User: NONE - Group: NONE)
     IN:  pkts     353870          bytes            507588924
     OUT: pkts     178720          bytes             13090480
     -- endpoint ID 0006 module tuntap      : tap0
    Port 0002 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
     Current User: phil Access Control: (User: NONE - Group: NONE)
     IN:  pkts     140180          bytes             10268912
     OUT: pkts     274615          bytes            397341431
     -- endpoint ID 0003 module unix prog   : QEMU user=phil PID=31442  SOCK=/var/run/vde2/tap0.ctl/.31442-00000
    Port 0003 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
     Current User: phil Access Control: (User: NONE - Group: NONE)
     IN:  pkts      38235          bytes              2765961
     OUT: pkts      76949          bytes            109875833
     -- endpoint ID 0008 module unix prog   : QEMU user=phil PID=31452  SOCK=/var/run/vde2/tap0.ctl/.31452-00000
    Port 0004 untagged_vlan=0000 ACTIVE - Unnamed Allocatable
     Current User: phil Access Control: (User: NONE - Group: NONE)
     IN:  pkts         70          bytes                13110
     OUT: pkts        144          bytes                20998
     -- endpoint ID 0010 module unix prog   : QEMU user=phil PID=31459  SOCK=/var/run/vde2/tap0.ctl/.31459-00000
    .
    1000 Success
    

    La copie d'écran ci-dessus illustre bien la correspondance entre les ports de commutation, l'interface TAP et les processus correspondant à chaque instance de système virtualisé. On retrouve les éléments introduits dans la Section 3, « Choix d'une solution de virtualisation ».

    En reprenant l'identifiant de processus 31442 donné ci-dessus, on retrouve les références du système virtualisé dans la liste des processus du système hôte.

    $ ps aux | grep 31442 | grep -v grep
    phil     31442  4.2  1.0 685260 85100 pts/3    Sl   22:41   1:23 \
     kvm -name r1.raw -m 512 -drive file=r1.raw,if=virtio,media=disk,boot=on \
     -k fr -usb -usbdevice tablet -rtc base=localtime,clock=host \
     -net vde,vlan=1,sock=/var/run/vde2/tap0.ctl,port=2 \
     -net nic,vlan=1,model=virtio,macaddr=52:54:00:12:34:2