Simuler un lien WAN sous Linux

Date: 17/03/2009 | Catégories: Open-source,Reseau | Tags: ,,,,,,,

Il peut être utile, dans le cadre de tests applicatifs, de simuler sur votre réseau local (LAN), les caractéristiques d'une liaison distante (WAN). En effet, vos applications peuvent très bien fonctionner sur un réseau LAN et devenir inexploitable sur des liaisons WAN.

Nous allons utiliser le module Net:Netem des noyaux Linux 2.6 pour simuler les caractéristiques suivantes:

  • Bande passante
  • Délai de transit
  • Perte de paquet
  • Duplication de paquet
  • Re-arrangement de paquet

La configuration de ce module se fait via la commande en ligne tc.

Simuler un délai de transit constant

Le délai est le temps de transit réseau d'un paquet IP. Il dépend de pas mal de paramètres (traversé des équipements, taille des buffers et distance physique entre les deux points du réseau). Nous allons utiliser la commande delay qui va simuler un délai de transit de X ms sur tout les paquets IP sortant de l'interface réseau. On va utiliser la commande "ping" pour vérifier que tout fonctionne comme prévu.

Test du réseau avant la commande tc:

$ ping 192.168.29.1
PING 192.168.29.1 (192.168.29.1) 56(84) bytes of data.
64 bytes from 192.168.29.1: icmp_seq=1 ttl=64 time=0.290 ms
64 bytes from 192.168.29.1: icmp_seq=2 ttl=64 time=0.204 ms

On simule un délai de 40 ms sur tout les paquets sortant (soit environ le délais sur une liaison ADSL):

sudo tc qdisc add dev eth0 root netem delay 40ms

Test du réseau après la commande tc:

$ ping 192.168.29.1
PING 192.168.29.1 (192.168.29.1) 56(84) bytes of data.
64 bytes from 192.168.29.1: icmp_seq=1 ttl=64 time=40 ms
64 bytes from 192.168.29.1: icmp_seq=2 ttl=64 time=40 ms

Pour revenir à la configuration initiale (sans simulateur), on utilise la commande suivante:

sudo tc qdisc del dev eth0 root

On vérifie que l'on retombe bien sur les caractéristiques normale du réseau:

$ ping 192.168.29.1
PING 192.168.29.1 (192.168.29.1) 56(84) bytes of data.
64 bytes from 192.168.29.1: icmp_seq=1 ttl=64 time=0.218 ms
64 bytes from 192.168.29.1: icmp_seq=2 ttl=64 time=0.209 ms

Simuler un délai de transit "normal"

Sur un réseau WAN, le délai de transit n'est jamais constant à travers le temps (surtout pour des liaisons de type Internet). Nous allons donc modifier la commande précédente pour intégrer une variation de délai (gigue de +/- 10ms) sur les paquets sortant:

sudo tc qdisc add dev eth0 root netem delay 40ms 10ms distribution normal

On obtient les caractéristiques suivantes:

$ ping 192.168.29.1PING 192.168.29.1 (192.168.29.1) 56(84) bytes of data.
64 bytes from 192.168.29.1: icmp_seq=1 ttl=64 time=36.9 ms
64 bytes from 192.168.29.1: icmp_seq=2 ttl=64 time=50.5 ms
64 bytes from 192.168.29.1: icmp_seq=3 ttl=64 time=33.1 ms
64 bytes from 192.168.29.1: icmp_seq=4 ttl=64 time=43.1 ms
64 bytes from 192.168.29.1: icmp_seq=5 ttl=64 time=32.5 ms
64 bytes from 192.168.29.1: icmp_seq=6 ttl=64 time=23.6 ms
...

Pour revenir à la configuration initiale (sans simulateur), on utilise la commande suivante:

sudo tc qdisc del dev eth0 root

Simuler une bande passante limite

Cette fonction ne fait pas partie de Netem mais utilise tout de même la commande tc pour se configurer.

Avant de commencer nous allons tester la capacité de notre réseau LAN avec la commande IPerf (à lancer en mode serveur UDP sur votre machine cible, 192.168.29.1 dans mon cas):

$ iperf -c 192.168.29.1 -u -b 10M
------------------------------------------------------------
Client connecting to 192.168.29.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:   110 KByte (default)
------------------------------------------------------------
[  3] local 192.168.29.222 port 47532 connected with 192.168.29.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  11.9 MBytes  10.0 Mbits/sec
[  3] Sent 8505 datagrams
[  3] Server Report:
[ ID] Interval       Transfer     Bandwidth       Jitter   Lost/Total Datagrams
[  3]  0.0-10.0 sec  11.9 MBytes  10.0 Mbits/sec 0.008 ms    0/ 8505 (0%)

On a donc bien un débit de 10 Mbps.

On commence par créer la racine de l'arbre des classes (avec une simulation de délai "normal" de 40ms):

sudo tc qdisc add dev eth0 root handle 1:0 netem delay 40ms 10ms distribution normal

Puis on y ajoute un "tuyau" limitant le trafic sortant à 512 Kbps:

sudo tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate 512kbit buffer 3200 limit 6000

On re-teste notre réseau:

$ iperf -c 192.168.29.1 -u -b 10M
------------------------------------------------------------
Client connecting to 192.168.29.1, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:   110 KByte (default)
------------------------------------------------------------
[  3] local 192.168.29.222 port 57589 connected with 192.168.29.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  11.9 MBytes  10.0 Mbits/sec
[  3] Sent 8505 datagrams
[  3] Server Report:
[ ID] Interval       Transfer     Bandwidth       Jitter   Lost/Total Datagrams
[  3]  0.0-10.3 sec    609 KBytes    486 Kbits/sec 14.351 ms 8081/ 8505 (95%)

On arrive bien à limiter le débit réseau sortant à 500 Kbps (un peu moins pour mon test).

Pour revenir à la configuration initiale (sans simulateur), on utilise la commande suivante:

sudo tc qdisc del dev eth0 root

Simuler une perte de paquets

La perte de paquets (ou "packet loss" dans la langue de Shakespeare) peut être simulé par Netem par la commande loss. Dans l'exemple suivant, nous allons simuler un lien WAN avec une perte de 0.1% des paquets sortant (soit 1 paquet perdu sur 1000 envoyé) avec un corrélation de 25% sur la probabilité que 2 paquets soit perdu de suite.

sudo tc qdisc add dev eth0 root netem loss 0.1% 25%

Pour revenir à la configuration initiale (sans simulateur), on utilise la commande suivante:

sudo tc qdisc del dev eth0 root

Simuler d'autres paramètres réseau

Il est également possible de simuler la duplication de paquets (commande duplicate), la corruption de paquets (commande corrupt) et le re-arrangement des paquets (commande gap).

Simuler également les paquets entrants

Imaginons que l'on veuille simuler une liaison de type ADSL, cette liaison est asymétrique en terme de débit. Il faut donc pouvoir simuler de manière différente le débit entrant (DOWNLOAD) du débit sortant (UPLOAD). Pour cela, il faut passer par la déclaration d'une interface réseau virtuelle (ifb) dans laquelle nous allons re-router les paquets entrants. Nous appliquerons nos paramètres réseau de simulation sur cette nouvelle interface.

Commençons par créer cette interface virtuelle:

# modprobe ifb
# ip link set dev ifb0 up
# tc qdisc add dev eth0 ingress
# tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0

Puis appliquons un délai de 40ms comme vu dans le chapitre précédant:

# tc qdisc add dev ifb0 root netem delay 40ms 10ms distribution normal

Un exemple complet: simulation d'une liaison ADSL

Voici un script Shell (bash) permettant de mettre en place une simulation de type liaison ADSL sur votre réseau local:

#!/bin/bash
#
# limitbw.sh
# Nicolargo - 2009
#

# Nom de l'interface ou l'on doit faire la simulation
IF=eth0

# Liaison sortante (UPLOAD)

# Debit sortant
BWU=768kbit
# Délai de transit sortant
DELAYU=20ms
# % de paquets perdus sortant
LOSSU=0.01%

# Liaison entrante (DOWNLOAD)

# Debit entrant
BWD=2mbit
# Délai de transit entrant
DELAYD=20ms
# % de paquets perdus entrant
LOSSD=0.01%

start() {

# Liaison entrante

modprobe ifb
ip link set dev ifb0 up
tc qdisc add dev $IF ingress
tc filter add dev $IF parent ffff: \
protocol ip u32 match u32 0 0 flowid 1:1 \
action mirred egress redirect dev ifb0

tc qdisc add dev ifb0 root handle 1:0 \
netem delay $DELAYD 10ms distribution normal \
loss $LOSSD 25%
tc qdisc add dev ifb0 parent 1:1 handle 10: \
tbf rate $BWD buffer 3200 limit 6000

# Liaison sortante

tc qdisc add dev $IF root handle 2:0 \
netem delay $DELAYU 10ms distribution normal \
loss $LOSSU 25%
tc qdisc add dev $IF parent 2:1 handle 10: \
tbf rate $BWU buffer 3200 limit 6000

}

stop() {

tc qdisc del dev ifb0 root

tc qdisc del dev $IF root

# ip link set dev ifb0 down

}

restart() {

stop
sleep 1
start

}

show() {

echo "Liaison entrante"

tc -s qdisc ls dev ifb0

echo "Liaison sortante"

tc -s qdisc ls dev $IF

}

case "$1" in

start)

echo -n "Starting WAN simul: "
start
echo "done"
;;

stop)

echo -n "Stopping WAN simul: "
stop
echo "done"
;;

restart)

echo -n "Restarting WAN simul: "
restart
echo "done"
;;

show)

echo "WAN simul status for $IF:"
show
echo ""
;;

*)

echo "Usage: $0 {start|stop|restart|show}"
;;

esac

exit 0

Bonne simulation 😉

  • Jarjarbinz

    Salut et félicitations pour ton site très complet !
    J’ai cependant une question sur Netem : dans ma configuration j’ai installé un Ubuntu 9.04 sur un PC avec 2 interfaces réseaux. Ce PC fait pont et permet de simuler un lien WAN entre 2 réseaux.
    Je souhaiterais simuler un lien de 512kbps (en entrée et en sortie) avec 200ms de latence. J’ai donc appliqué les règles TC sur les 2 interfaces.
    Mais lorsque je créé une règle pour ajouter un temps de latence de 200ms, mon débit déscend à 320kbps.
    Sais tu pourquoi le débit baisse autant lorsque l’on augmente la latence ?
    Merci

    NB : voici la règle que je créé sur mes 2 interfaces :
    tc qdisc add dev eth0 root netem delay 100ms 10ms distribution normal
    tc qdisc add dev eth1 root netem delay 100ms 10ms distribution normal

  • Alex

    Bonjour Jarjarbinz,

    il est normal que le débit baisse quand la latence augmente (du moins pour des connexions TCP). C’est à cause du principe suivant :
    – TCP est un protocole qui fonctionne en mode connecté
    – le récepteur doit acquitter les segments TCP au fur et à mesure qu’il les reçoit.
    – or plus le temps de parcours entre l’émetteur et le récepteur est important (la fameuse latence), plus le temps d’envoi des paquets de l’émetteur vers le récepteur prend de temps. De la même façon, l’acquitement des segments par le récepteur prend également plus de temps pour revenir à l’émetteur.
    – TCP utilise des buffers pour stocker les données en attente d’acquitement (afin de pouvoir les réemettre). Par conséquent, si le buffer est plein car on attend trop d’acquittement, l’émetteur arrête d’envoyer des nouvelles données en attendant de recevoir des acquitements pour les données envoyées. Dès qu’il en reçoit il libère la place correspondant à ces données dans son buffer et peut émettre à nouveau. Ainsi lorsque la latence est importante, rapidement les buffers configurés par défaut sont insuffisants et il faut les modifier en conséquence. Tu pourras trouver pas mal d’info en cherchant « TCP bandwidth product delay » sur Google.

  • NetEM fonctionne correctement mais pour la latence, les pertes de paquets.

    Quand je cherche à simuler une bande passante limitée, j’ai systématiquement un message d’erreur : « RTNETLINK answers: Operation not supported ».
    J’utilise Ubuntu 10.10 et le noyau par défaut d’Ubuntu 2.6.35.

    Exemple en suivant scrupuleusement ton tuto :

    # sudo tc qdisc add dev eth0 root handle 1:0 netem delay 40ms 10ms distribution normal

    # sudo tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate 512kbit buffer 3200 limit 6000
    RTNETLINK answers: Operation not supported

    eth0 est mon unique interface Ethernet (le PC héberge un serveur apache2).

    Merci d’avance pour tes lumières.

  • Vivien

    J’ai réalisé des tests avec Ubuntu 32 et 64bits, 10.04 LTS et 10.10.

    Quand je cherche à simuler une bande passante limitée, j’ai systématiquement un message d’erreur : “RTNETLINK answers: Operation not supported”.

  • idem pour moi, apparemment ça vient d’un problème de configuration dans le kernel. Du coup j’ai opté pour une autre solution : wondershaper

  • hello

    il y a WanEmu qui fait ca très bien également.
    C’est sur un live CD knoppix, il y a juste à booter et a faire les routes qui vont bien 😉

    enjoy

  • Maxime

    Bonjour,

    J’ai eu le même problème en utilisant le script: « RTNETLINK answers: Operation not supported ».

    Le problème est simple tout comme la solution: Le module netem n’accepte pas de qdisc fils, d’où le message d’erreur. Par contre, tbf les accepte et c’est donc celui-ci qui doit être le parent.
    Il faut juste inverser les deux pour que ça marche, comme ceci :

    Pour la liaison entrante:
    tc qdisc add dev ifb0 root handle 1:0 tbf rate $BWD buffer 3200 limit 6000

    tc qdisc add dev ifb0 parent 1:1 handle 10: netem delay $DELAYD 10ms distribution normal loss $LOSSD 25%

    Pour la liaison sortante:
    tc qdisc add dev $IF root handle 2:0 tbf rate $BWU buffer 3200 limit 6000

    tc qdisc add dev $IF parent 2:1 handle 10: netem delay $DELAYU 10ms distribution normal loss $LOSSU 25%

  • yaap

    Bonjour,

    Tout d’abord, merci pour ce tutoriel intéressant et très complet.

    Pour approfondir cet article, je souhaite comprendre à quel « niveau » se situe les limitations mises en place.
    Par exemple, vaut-il mieux limiter le débit avec tc ou avec des règles dans iptables ?
    Avec iptables, il me semble que les paquets en « trop » seront droppé lorsque le débit limite aura été atteint. Mais avec tc, comment se fait la limitation ?
    Du coup, quel est l’avantage de tc par rapport à iptables toujours dans le cas de limiter le débit ?

    Je vous remercie par avance,

    yaap.

  • w1ldscr4tch

    Bonjour,

    C’est exactement ce qu’il me fallait et qui plus est c’est clair et net.

    Merci Nicolargo pour l’article et le blog en général!

  • Pingback: GuiGui's show » Maquetter des réseaux avec LXC()

  • rémi seidita

    Bonjour,

    Le lien vers le module Net:Netem (à jour)

    http://www.linuxfoundation.org/collaborate/workgroups/networking/netem

    Bonne journée 🙂