Projet

Général

Profil

Projet agregation

Projet universitaire UTC (Compiègne) Yanick & Jocelyn (Automne 2011)

Prototype v1

http://lists.tetaneutral.net/pipermail/projet-agregation/2011-November/000023.html

Test de tunproxy.py

On utilise tunproxy.py. Entre 2 machines
  • client-adsl (une machine chez nous)
  • gateway (la VM)

Sur la gateway (= VM ttn)

Démarrer le tunnel, il crée lui-même une interface toto0 (détruite à la sortie).

 ./tunproxy.py -s 6000
 ifconfig toto0 10.0.0.1/24 mtu 1468

La MTU est calculée comme suit :

MTU de l'iface virtuelle  = MTU  de l'iface physique - taille_max(header IP) - taille(header UDP)
MTU de l'iface virtuelle = 1500 - 24 - 8

http://www.commentcamarche.net/faq/7185-introduction-au-mtu

Sur le client

 ./tunproxy.py -c rhizome-fai.tetaneutral.net:6000
 ifconfig toto0 10.0.0.2/24 mtu 1468

Tout le trafic vers les adresses en 10.0.0.x passera par le tunnel.

Un test de perf sur un téléchargement d'un fichier de 40Mio donne :

  • avec tunnel : 909kb/s
  • sans tunnel : 942kb/s

Détection de la saturation d'un lien / bufferbloat

Un des points sur lesquels nous nous penchons est la détection de la capacité d'un lien et de son évolution, ceci 1) pour utiliser au mieux des liens de capacité différente et éventuellement changeante.

Tout le challenge est de détecter (passivement) plutôt que de mesurer (activement) la capacité du lien, sans induire de trafic supplémentaire.

Nous avons fait des mesures sur deux liens :

Mesure ADSL Free (Freebox)

Il semble que de la QoS soit appliquée… l'effet de bufferbloat n'est pas vraiment visible : on passe d'un ping de 40 à 70/80ms… (TODO: prendre le temps de collecter des résultats des deux côtés du tunnel un peu plus sérieusement que juste l'impression donnée ceci-dit). Tous les paquets de ping arrivent, même lorsque le lien est saturé.

Lien ADSL (OVH)

L'effet de la saturation se fait clairement ressentir sur le ping : on passe de 70 à plus de 300ms de ping lorsque le lien est saturé.

Données du test et graphiques : saturation_et_ping___uplink_seulement__udp_sctp.ods

Conditions du test :

  • Tunnel tap basé sur tunproxy.py (cf dépôt git), qui est le seul à utiliser la connexion
  • Données collectées toutes les secondes, chaque peer enregistre :
    • timestamp
    • stats des paquets entrants
    • ping
  • On teste la connection au repos en la saturant par moments avec iperf
    • en UDP : en metant l'option -b à une valeur supérieure à la capacité d'uplink
    • en TCP
  • Les données des 2 peers sont fusionnées à posteriori (script merge_tunproxy_csv.py) en fonction des timestamp.
  • Les données sont graphées sur un tableur (ouais je sais, beurk ;-) ).

Le comptage du volume sortant n'est pas pertinent puisque la moitié des paquets peuvent-être dropés en route…

Analyse des résultats

(voir document joint)
  • On note systématiquement une corrélation forte entre lien saturé et augmentation du ping. Que le lien soit saturé en UDP ou TCP.
  • En UDP, on peut saturer le lien complètement. Il en résulte qu'une part des pings se perd -> Prendre non seulement en compte le RTT mais également le taux de pings perdus.
  • En TCP, on observe aussi une montée du ping significative, mais jamais de pings perdus. On constate d'ailleurs que TCP se rend compte qu'il sature le lien et divise sa fenêtre (trou dans le graphe).
  • -> Quid de la réalité de la saturation d'un lien par rapport à ces deux exemples simples ?

Demi-délai

Nous pouvons donc corréler une saturation du lien (qu'elle soit effectuée par un protocole qui gère la congestion ou non) avec une augmentation du ping. Reste un autre problème, nous voulons détecter dans quel sens a lieu la saturation. Or un ping nous donne le temps d'aller retour (RTT, Round-Trip-time). Il n'est en outre pas possible de mesure la durée absolue d'une trame entre deux sites, les horloges n'étant pas synchronisées. Deux approches sont envisagées

Synchronisation par NTP

NTP est un protocole permetant de synchroniser via le réseau les horloges de machines distantes. Si NTP fournit une précision suffisante, il serait intéressant pour pouvoir effectuer des demi-ping : 

  • On maintient les horloges synchronisées grace à NTP entre machine 1 et machine 2
  • Machine1 envoie un paquet à machine2 contenant un timestamp
  • Machine 2 peut connaître le temps de trajet machine1->machine2 en comparant ce timestamp avec sa propre horloge.

On mesure des ping entre 20 et 100ms en général, soit des demi-ping entre 10 et 50ms. Or, les études sur NTP (ex: http://www.eecis.udel.edu/~mills/database/brief/perf/perf.pdf) montrent qu'à travers un réseau WAN (ex: l'ADSL que nous utilisons), l'erreur de NTP est autour de 10ms. Soit une erreur relative entre 10% et 50%, ce qui n'est pas acceptable. La seule solution viable, selon l'étude mentionnée, pour synchroniser réellement des équipements serait d'avoir une source GPS qui permet d'avoir une erreur en-dessous de la milliseconde. Cela nécessite de l'équipement supplémentaire et n'est souhaitable.

Voir aussi http://www.frameip.com/ntp/

Par évolution du délai relatif

Une autre approche discutée est de mesurer non pas le délai absolu mais la variation de celui-ci.
On mesure timestamp_envoi_site1 - timestamp_reception_site2 pour chaque paquet, la valeur absolue n'a aucun sens (on utilise deux horloges différentes).

Un autre problème est alors la dérive relative des horloges des deux machines qu'il ne faut pas négliger (exemple donné dans l'article sur UTP de 17ms de dérive en 10 minutes)

Cette idée est d'ailleurs reprise dans le protocole UTP de bittorrent : http://www.rasterbar.com/products/libtorrent/utp.html

Un outil faisant ce type de mesure a été implémenté dans le dépôt : delta_half_trip_time.py

Côté serveur :

./delta_half_trip_time.py -s 2244

Côté client:

./delta_half_trip_time.py -s <ip_serv>:2244

Le script mesure en permanence les délais toutes les secondes. Il ne prend pas en compte la dérive d'horloge pour l'heure. La sortie est du CSV contenant les délais dans les deux sens (de chaque côté). Le format est :

pkt_type,sequence number,delay

pkt_type vaut 't' (comme timer) pour les mesures entrantes (download) et 'd' (comme delay) pour les réponses aux paquets sortants (upload).

mesures

Résultats : one-way_delay.ods

Saturation TCP (iperf) dans un sens puis dans l'autre
Note sur ces mesures (iperf TCP) : correspond peut-être au cas le plus difficile à détecter (une unique connection TCP qui sature le lien) étant donné que le backoff de TCP va essayer d'éviter de saturer le lien en permanence.
Saturation UDP progressive

Ces mesures sont effectuées à l'aide de load_uplink.py (dans le git)

Pour charger l'upload d'une connecxion qui monte en pratique à ~109kB/s (résultat iperf TCP).
On passe par paliers de 10kB/s de 0 à 120kB/s (5s par palier) :

python load_uplink.py 91.224.149.199 10 5 120

Par ailleurs, on observe à l'aide de delta_half_trip_time.py l'évolution du ping (cf document de résultats).

On observe qu'il n'y a pas de saturation progressive. Ou le lien est saturé et en quelques secondes, le delay s'envole, ou il ne l'est pas et le ping reste stable.

Plusieurs tests ont été réalisés, l'idée étant de trouver la formule qui à partir des n derniers delays et du delai minimum est capable de dire si oui ou non on a saturation.

On arrive à une première solution, elle ne fait pas de faux positifs mais peine à détecter les saturations < 10 secondes.

Soit "l_derniers" les 6 derniers échantillons de délai
Si max(l_derniers) > TRIGGER et 4 échantillons de l_derniers au moins sont supérieurs à 1/3*max(l_derniers), alors SATURATION
Mesure d'un scénario d'usage

Le but est ici de mesurer un scénario d'usage « classique » de la connexion, en upload et download pour voir si une formule nous permet de détecter les pics.
(cf sat. usage normaux dans le document attaché).

Le scénario est le suivant :

  • vidéo en 1080p sur youtube (téléchargement par sacade « à la youtube »)
  • scp d'un fichier vers un serveur (3MiO)
  • scp d'un fichier depuis un serveur (3MiO)
Observations
On observe que, particulièrement en download, il n'est pas possible de détecter les saturations courtes. Ne saturant pas les buffers du modem-routeur, elles ne font pas grimper le délai. (cf SCP en up). On doit ceci-dit être proche de la limite avec notre transfert de 3Mio car sur les deux essais, il y a une fois ou on a une réponse en augmentation de délai et une fois sans.

Il est curieux de noter que les saturation en download entrainent également une augmentation du délai en upload. Nous n'avons jamais observé ça jusqu'alors… Je n'en comprend pas le sens -> à éclaircir/reproduire.

dérive

Le fichier one-way_delay.ods présente également une mesure de la dérive sur 40 minutes entre 2 machines. L'enjeu est de savoir si il est nécessaire de mettre en place un mécanisme pour détecter et prendre en compte la dérive des horloges qui rendraient la comparaison de deux délais relatifs peu pertinents si elle était trop importante.

Bien que l'expérience ne porte que sur un cas et ne fasse pas loi, elle nous expose une dérive de 0.5ms sur 40 minutes d'observation (dérive relative de ~1.4%). Ne souhaitant garder pour nos mesures de capacité de lien qu'une fenêtre glissante que de quelques minutes ou dizaines de minutes tout au plus, il n'apparaît pas nécessaire de prendre en compte cette dérive.

Influence du réseau radio

On considère que le réseau radio entre les différents points de raccordement à internet n'est pas un goulot d'étranglement.
En revanche, il faut prendre garde à la qualité du lien, qui peut fausser les mesures Le transmit CCQ (paquets valides/paquets transmis) est un bon indicateur de la qualité du lien. Un lien de mauvaise qualité impliquera que certains pings soient plus longs (retransmissions). cf document ping_et_bande_passante.ods

Petits points techniques…

Que mesure iperf et comment (en UDP) ?

Iperf mesure le débit du client vers le serveur (dans un seul sens). En UDP, il envoie à une vitesse nominale (par défait 1M). Le résultat donné par le client n'est pas une mesure mais correspond à cette vitesse nominale. Seul le server repport correspond à la "vraie" mesure.

La saturation d'un lien générant des pertes, pour mesurer les pertes liées à la qualité du lien (et non à sa capacité), il faut demander au client d'émettre un peu en-dessous de la vitesse à laquelle peut recevoir le serveur.

Quelques outils réseaux bien pratique

  • tcpdump | http://openmaniak.com/fr/tcpdump.php
    tcpdump -D #Interfaces réseaux disponibles pour la capture
    tcpdump port 80 -i eth0 -w capture.log #Enregistre le trafic Web vers le fichier capture.log pouvant être ouvert avec Wireshark
    tcpdump icmp #Affiche tout le trafic associé au protocole icmp
    
  • ping | http://www.bortzmeyer.org/ping-taille-compte.html
    • Permet de tester un problème de MTU grâce à l'option -s de ping permettant de fixer une taille de paquet
  • hping3
    hping --syn -p 80 --data 1200 10.0.0.1 #Envoie de paquet tcp syn sur le port 80 de taille 1200
    

tracepath pour découvrir le PMTU

multi.py

peer_: Liste des sockets du clients basés sur ses interfaces physiques
peer_d: sous forme de dictionnaire
peer_l: sous forme de liste

La structure:
Liste < . . . > Dictionnaire
0->A < . . . > <A,None>
1->B < . . . > <B,None>
2->C < . . . > <C,None>

Routage

Pour qu'un script comme multi.py fonctionne, il faut faire du routage selon la source.

En effet, il n'y a qu'une IP de serveur, mais N IPs de client. Or, les décisions de routage d'une table gérée avec la commande route sont de la forme suivante (simplifié):

&lt;Destination&gt; &lt;passerelle&gt; &lt;interface&gt;

Le routage est fait seulement en fonction de la destination.

On utilise donc l'outil ip route. Quelques exemples.
Montrer la table de routage:

ip route show

Montrer la route pour une IP source et dest données :

ip route get 0.0.0.0 from 192.168.1.71

Initialement, on a deux interfaces (eth0 et wlan0) avec une passerelle vers internet sur chaque (2 lignes ADSL), on est NATé sur les deux. Mais une seule est déclarée comme route par défaut.

jocelyn@sensitive:~$ ip route get 8.8.8.8 from 192.168.2.33
8.8.8.8 from 192.168.2.33 via 192.168.1.254 dev wlan0 
    cache 
jocelyn@sensitive:~$ ip route get 8.8.8.8 from 192.168.1.71
8.8.8.8 from 192.168.1.71 via 192.168.1.254 dev wlan0 
    cache 

Par défaut, on n'a que la table « main » et « default ». (on peut voir les tables avec ip rule).
Nos deux ip locales sont 192.168.1.71 (wlan0) et 192.168.2.33 (eth0)
On y ajoute notre table : fdn_rhizome avec nos règles :

  1. Ajout d'une nouvelle table
    echo "1000 rhizome_fdn" >> /etc/iproute2/rt_tables
  2. et sa route par défaut
    ip route add default via 192.168.2.1 dev eth0 table rhizome_fdn
  3. On dit au système de ne regarder notre table que pour les requêtes venant de 192.168.2.33
    ip rule add from 192.168.2.33 lookup rhizome_fdn prio 1000

On vérifie qu'on passe par une interface différente en fonction de l'IP source :

jocelyn@sensitive:~$ ip route get 8.8.8.8 from 192.168.2.33
8.8.8.8 from 192.168.2.33 via 192.168.2.1 dev eth0
8.8.8.8 from 192.168.1.71 via 192.168.1.254 dev wlan0

Pour un test pratique, on peut utiliser ping avec l'option -I pour spécifier l'interface de sortie, puis vérifier dans wireshark que les trames sortent bien par une interface différente (header MAC, il suffit de regarder l'addr. MAC source).

Script pour permettre de sortir sur deux interfaces WAN différentes, dans le git : dual-wan-routing.sh
Le script met en place les règles de routage pour les deux interfaces puis les test.

Journal (à partir du 28 oct)

Activités du projet de Yanick & Jocelyn (TX) *

du 23 au 30 Janvier

  • Implémentation de l'agrégation avec ajustement dynamique des pondérations
  • Mesures sur une vraies agrégation de deux liens (latence, download, upload…)
  • Analyse des résultats
  • Mesures de l'influence du réseau radio
  • création d'un script basique d'initialisation
  • Rapport

22 Janvier

  • Configuration du routage avec IProute2
    • Script dual-wan.sh de configuration du routage (attention, flush les tables de routage…)

16 Janvier

  • Détection de saturation
    • Le tableur détecte à la fois les saturations en upload et download
    • Le tableur prend maintenant des paramètres au lieu de valeurs en dur pour ajuster la formule…
    • Bugfixé le script qui nettoie les CSV.
      TODO: reproduire et vérifier l'histoire de délais dans les 2 sens, appliquer le tableau paramétré détectant l'UP et down aux mesures précédentes

8 janvier.

  • Détection de saturation:
    • Évolution de delta_half_trip_time.py pour enregistrer un historique des délais (dans les 2 sens)
    • Ajout d'une détection de saturation (… Mais à améliorer, trop de faux positifs)
    • Création de l'outil de test load_uplink.py pour charger progressivement un lien jusqu'à la saturation et pouvoir ainsi observer le comportement du ping.
  • Compréhension du script multy.py de Laurent Guerby
    • Commentaire du script multy.py
    • Schéma graphique du fonctionnement de multy.py

28/29 déc.

  • Détection de saturation : nouvel outil pour mesurer les délais dans un sens
    • Création de l'outil, qui fonctionne de manière bidirectionelle et rapporte les informations aux deux pairs
    • Première mesure rapide sur un iperf en TCP, dans un sens puis dans l'autre, simplement pour valider la détection.

5 déc.

  • Détection de saturation :
  • Output CSV en direct vers le fichier plutôt que statiquement au bout de 3 minutes…
  • Écriture d'un outil de script de logs CSV
  • Collecte de mesures sur l'effet sur le ping de la saturation d'un lien en UDP et TCP
  • Analyse basique des résultats

27 nov.

  • Lecture et utilisation de linkagreg (outil d'agrégation de Fernando)
  • Faire fonctionner linkagreg sur une architecture 64bits
  • Faire fonctionner linkagreg avec une connection sur le client //Fonctionnel
  • Faire fonctionner linkagre avec n connection sur le client //Non fonctionnel
    • Test avec une connection filaire et WiFi //Non fonctionnel car perte (important) de paquet sur le lien WiFi
    • Test avec des connections virtuelles //Non fonctionnel car QoS inapplicable sur des interfaces virtuelles
    • Test avec deux interfaces physiques //Non fonctionnel car QoS déficiente
  • Ajout de la collecte de données sur les temps de réponse (ping) périodiquement.
  • Export des données en CSV (pour exploitation/graphe… etc.)
  • Premier jeu de mesure (mauvais) sur une ligne adsl. *

11 nov.

  • Debuggage du problème de MTU (c'est honteux mais c'est bêtement la taille des buffers qui n'était pas assez grande dans le programme. Notamment dû aux pseudo en-têtes, cf plus bas).
  • Configuration auto des adresses IP de chaque côté du tunnel (plus besoin d'ifconfig à la main)
  • Ajout sur tunproxy.py de compteurs de débit * mémorise le traffic sur les x dernières tranches de n secondes (défaut 10 tranches de 1 seconde) * Affiche les moyennes et les max.
  • Compréhension de ce qui passe dans TUN : bien qu'étant un tunnel de niveau 3, il y a une pseudo-en-tête de L2, cf doc officielle (merci Laurent!)
  • discussion avec Laurent sur les intérêts de faire un tunnel L2 (qui rajoute pourtant l'overhead de l'en-tête L2), en bref : * évite de gérer les soucis spécifiques du niveau IP * TUN ne supporte pas IPV6 par exemple …

5 nov.

  • Mise en place d'un dépôt git (gitolite) pour partager du code avec Fernando Alves de Sames Wireless :
    1. Dépot public : (lecture-seule)
      git clone git://rhizome-fai.tetaneutral.net/agregation.git

2 nov

  • Modification de la MTU pour éviter la fragmentation de paquet

28 oct.

  • Initiation python (découverte pour Yanick)
  • Commentaire intégral du tunproxy.py et premiers tests de ce dernier
    • ping ok (+1ms)
    • iperf à travers le tunnel : BP ~= celle de l'uplink ADSL. Le dernier datagrame ne reçoit pas d'ACK
[  3] local 10.0.0.2 port 50191 connected with 10.0.0.1 port 5001                        
[ ID] Interval       Transfer     Bandwidth                                              
[  3]  0.0-10.0 sec  1.25 MBytes  1.05 Mbits/sec                                         
[  3] Sent 893 datagrams                                                                 
[  3] WARNING: did not receive ack of last datagram after 10 tries.

Fonctionnalité

  • Ajouter plusieurs sockets sur le tunnel pour éviter le traffic shaping de la part d'un opérateur

Annexes

Rappel de base de python

@classmethod: Decorater python: la méthode s'applique à la définition de la classe. Il s'agit d'une méthode statique.
#cls: La classe Python et non l'objet instancié: tous les objets instanciés sont modifiés, il s'agit d'un attribut statique.
#self: L'objet instancié, équivalent du this.