Linux Sécurité et réseaux Debian Communauté Découverte  







Introduction à la qualité de service sous Linux

vendredi 3 mai 2002, par regit


DANS LA MEME RUBRIQUE :
Ulog, les logs de firewall nouvelles générations
QOS sous Linux, optimisation d’une connexion ADSL
Paramétrer PPP avec le Speed Touch Pro
Utilisation du forwarding ssh


Ce document présente brièvement la qualité de service et son utilisation sous Linux. Il fournit des exemples de solutions à certaines problématiques courantes. Seule l’approche Diffserv de la qualité de service est étudiée ici.

Ce document est disponible dans différents formats imprimables.

Table des matières

  • Principe
  • Implémentation sur Linux
  • Mise en place et utilisation de tc
  • Exemples
  • Problèmes courants
  • Conclusion

    Principe

    Le domaine de la qualité de service est particulièrement vaste. Nous définissons uniquement ici les notions et les algorithmes disponibles sous Linux (Cette partie du document est inspirée du rapport de DEA d'Emmanuel Lochin).

    Ordonnanceurs

    FIFO

    C'est l'ordonnanceur par défaut, "First In First Out". Il n'y a pas grand chose à dire si ce n'est que c'est le plus facile à mettre en place.

    PRIO

    L'ordonnanceur PRIO permet de classer les flux selon leur priorité. Les paquets appartenant à un flux sont envoyés avant les paquets des fluxs de plus basse priorité. Tant que des paquets sont en attente dans ce flux, on ne considère pas les paquets des fluxs suivants.

    Le problème de cet ordonnanceur est qu'il est nécessaire de gérer les files d'attente et que cela peut aboutir à des dépassements de capacités et donc à des pertes. De plus, les paquets favorisés le sont de manières excessives, rien ne peut être envoyé dans une classe tant qu'une classe de priorité plus haute a des paquets à émettre.

    Stochastic Fair Queuing

    Le "Stochastic Fair Queuing" sépare un flux en plusieurs files d'attentes et chaque flux se voit attribué un poids. Le paquet devant partir le premier est alors tiré au hasard dans l'une des files et ce avec une probabilité égale au poids de sa file d'attente. Ceci permet de définir des priorités relatives, les fluxs sont équilibrés suivant leur poids respectif.

    Architecture d'ordonnancement

    Les architectures d'ordonnancements suivantes permettent de séparer le flux global en plusieurs file d'attente.

    Class Base Queuing

    L'architecture CBQ est la plus intuitive et la plus utilisée. Elle crée des files d'attente auxquelles sont attribuées une certaine bande passante. Elle permet de réaliser le traditionnel ``traffic shaping'' : le trafic est partagé en flux d'importances prédéfinies, la classification étant effectuée grâce des facteurs arbitraires.

    Clark Shenker Shang

    Cette architecture plus complexe sépare tout d'abord les fluxs en deux catégories :
    • Les services garantis : Les services sont en temps réél borné avec une contrainte de délai au pire cas.
    • Les services prédictifs : Pour ces services, on donne juste par calcul une borne pour le délai d'acheminement.

    Lissage de traffic

    Afin de gérer les services dont la demande en ressource réseau n'est pas continue, il peut être nécessaire de mettre en place une politique de lissage de trafic. En effet, si l'on se place par exemple dans le cas d'un ordinateur gérant une liste de diffusion, le trafic smtp va au moment de l'envoi d'un mail (par exemple journalier) monopolisé les ressources réseaux pendant un long moment. Si la liaison utilisée sert aussi à des serveurs webs, la disponibilité des sites s'en trouvent affecté. Il peut donc être fortement interessant de limiter l'utilisation de la bande passante par un trafic non continu.

    Token Bucket

    L'algorithme Token Bucket est un exemple d'algorithme de lissage de trafic. Des jetons sont accumulés dans un seau, de taille fini, à débit constant. Lorsque des paquets passent, ils décrémentent le nombre de jetons du seau. Les paquets passent donc à vitesse élevé tant qu'il reste des jetons. Ensuite, ils passent au débit de remplissage du seau.

    Prévention de la congestion

    Lorsqu'un lien sature, l'ensemble des machines émettant sur ce lien sont prévenues en même temps de la saturation du lien. Par conséquent elles réemettent toutes en même temps (si on a des OS homogènes) ce qui peut saturer le lien à nouveau. Il est donc nécessaire de trouver un moyen de régler ce problème. Random Early Detection est un algorithme qui consiste à rejeter aléatoirement des paquets dès que l'on approche d'une situation de congestion. La réemission des paquets est alors décalée pour certaines machines.

    Gestion des paquets entrant, Ingress

    Sur un lien entrant, il n'est pas possible de réguler le traffic puisque l'on en a pas la maitrise. Cependant, on peut jeter des paquets arrivant trop vite. Ceci permet d'obtenir une gestion de la bande passante entrante. En jetant des paquets sélectionnés, on fait croire à la machine emettante que la bande passante disponible pour nous joindre est celle que nous voulons.

    Implémentation sur Linux

    La qualité de service sous Linux prend en charge les paquets juste avant (CBQ, CSZ), ou juste après (Ingress), le driver réseau.

    Détail sur les algorithmes utilisés

    La gestion de la qualité de service fait évidemment partie du noyau, et les sources sont disponibles dans le répertoire net/sched/. Elles sont particulièrement bien commentées et la plupart des fichiers contiennent une description assez détaillé de l'algorithme implémenté.

    Hiérarchie de classes

    Il est possible de construire une hiérarchie de classes. Cependant, il faut indiquer quel est le chemin emprunté par les paquets. Chaque flux entrant dans un flux qui bifurque doit appartenir ensuite à un flux fils.

    Mise en place et utilisation de tc

    L'utilitaire tc permet de transmettre au noyau les paramètres que l'on souhaite mettre en place. C'est un utilitaire de la famille iproute2. La syntaxe utilisé par ce programme est :
    tc [ OPTIONS ] OBJECT { COMMAND | help }
    where  OBJECT := { qdisc | class | filter }
           OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -b[atch] file }
    
    Le principe est le suivant :
    1. Définition d'une architecture d'ordonnancement sur le périphérique
    2. Si l'architecture comporte des classes, définitions des classes
    3. Classification des paquets en flux.
    4. Affectation des flux de paquets aux classes

    On étudiera ici principalement l'architecture d'ordonnancement CBQ Deux implémentations de CBQ existe pour Linux, la version standard est CBQ. Une deuxième version a été développée, HTB pour Hierachical Token Bucket.

    Cette implémentation de CBQ est très performante et plus simple à mettre en place que l'implémentation standard. Cependant, elle nécessite une version du kernel supérieure à 2.4.18.

    Queueing Discipline

    L'attribution d'une architecture d'ordonnancement au périphérique est la première étape nécessaire :
    tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit \
    avpkt 1000 mpu 64
    On définit ici une architecture de type CBQ sur le périphérique eth0.
    • bandwidth : La paramètre bandwidth est utilisé par les calculs effectués par l'ordonnanceur et décrit la bande passante physique disponible sur le périphérique.
    • handle : Il nomme la ``queueing discipline'' 10: ; ceci permet ensuite de créer des flux nommés 10:xx .
    • avpkt : Cette variable définit la taille moyenne des paquets associés à cette discipline. Elle est utilisée à des fins de calculs de bande passante.

    On peut aussi définir une queueing discipline pour chaque flux feuille (voir 4.5).

    Class

    Le mot clé class est lié à la définition des files d'attentes.

    CBQ

    tc class add dev ppp0 parent 10:0 classid 10:1 cbq bandwidth \
    10Mbit rate 64Kbit weight 4 allot 1514 \
    prio 1 avpkt 200 isolated
    La commande précédente crée un flux de tpe CBQ.
    • parent : On indique la parenté du flux avec le mot parent On conserve la paramètre bandwidth.
    • rate : On spécifie la bande passante allouée à ce flux avec le mot clef rate.
    • weight : Le mot clé optionnel weight est par défaut égal à rate et définit l'importance relative des fluxs les uns par rapport aux autres.
    • allot : Variable utilisée par les calculs, elle doit être égale au MTU.

    Type de flux

    Pour qualifier le flux, on dispose de deux mots et de leur combinaison :
    • bounded : Le flux est limité en bande passante, il n'est pas possible de dépasser la bande passante indiquée. Cette notion n'est clairement pas optimale puisque les ressources réseau ne sont pas utilisées à plein. Par exemple, si un seul flux est intensif, il ne peut prendre toute la bande passante.
    • isolated : Le flux n'autorise pas que l'on emprunte de sa bande passante.
    • bounded isolated : Le flux est limité en bande passante et ne partage pas la sienne.

    Pour créer un flux fils, on peut utiliser :

    tc class add dev ppp0 parent 10:1 classid 10:3 cbq \
    bandwidth 10Mbit rate 32Kbit \
    allot 1514 prio 1 maxburst 20 \
    avpkt 200 isolated

    On spécifie en plus ici que le flux a la priorité la plus forte :

    • prio Ce mot clé permet de définir la priorité du flux, la priorité varie de 1 à 8.
    • maxburst Elle permet de spécifier le nombre de paquets maximum pouvant être envoyé en une rafale. Pour un réseau 10Mbit la valeur recommandée est 20.

    Les classificateurs et l'option filter

    Pour pouvoir ranger les paquets par classe, il est nécessaire de pouvoir les classifier. L'implémentation de la qualité de service sous linux comporte trois classificateurs.

    fw

    Les paquets sont affectés à une file suivant leur marquage par Netfilter. Par exemple :
    iptables -t mangle -A OUTPUT -o eth0 -p tcp -j mark -set-mark 1
    On appose ici la marque 1 au paquet sortant sur l'interface eth0 et étant des paquets de type TCP. Ensuite on peut assigner à ces paquets une file :
    tc filter add dev eth0 parent 1:0 \
    protocol ip handle 1 fw \
    flowid 1:1

    u32

    Le classificateur u32 permet de lire n'importe quel champ de l'entête IP et de classer les paquets dans les files suivant la valeur de ce paramètre. u32 supporte des raccourcis permettant par exemple d'attendre facilement les entêtes spécifiques à TCP.

    exemples

    La ligne de commande à utiliser commence par le classique :
    tc filter add dev eth0 parent 1:0 \
    • Filtrage d'une adresse IP source : la syntaxe est la suivante :
      tc filter add dev eth0 parent 1:0 \
      protocol ip prio 1 u32 match ip src 192.168.1.0/24 flowid 1:1

      Cette règle associe au flux 1:1 tous les paquets provenant du réseau 192.168.1.0/24 En remplaçant 192.168.1.0/24 par une IP standard, on filtre uniquement sur cette IP. En substituant dst à src on filtre sur la destination.

    • Filtrage sur l'entête TCP : La syntaxe est
      tc filter add dev eth0 parent 1:0 \
      protocol ip prio 1 u32 match tcp src 80 flowid 1:1
    • Filtrage sur un entête quelconque : La commande suivante permet d'assigner aux paquets ACK la file 1:10
      tc filter add dev eth0 parent 1: \
      protocol ip prio 12 u32 \
      match ip protocol 6 0xff \
      match u8 0x05 0x0f at 0 \
      match u16 0x0000 0xffc0 at 2 \
      match u8 0x10 0xff at 33 \
      flowid 1:10

    route

    L'utilitaire route (voir le Linux advanced routing and traffic control howto) permet de numéroter les routes. Le classificateur route classe les paquets suivant le numéro de leur route. Il est nécessaire d'utiliser ip qui permet de numéroter les routes en utilisant le mot clé realm :
    ip route add 192.168.1.0/24 via 192.168.1.1 \
    dev eth2 realm 15
    On peut ensuite mettre les paquets empruntant cette route dans une classe :
    tc filter add dev eth2 parent 1:0 protocol ip prio 1 \
    route to 15 classid 1:1

    Visualisation des classes et filtres

    Pour visualiser les informations, il faut utiliser tc avec la commande ls :
    tc -s -d filter ls dev eth0
    tc -s -d class ls dev eth0
    Les optiont -s (resp. -d) permettent d'afficher les statistiques (resp. les détails).

    Nettoyage des règles

    Pour enleveer toutes les règles, il suffit de supprimer la discipline à la racine :
    tc qdisc del dev eth0 root

    Exemples

    Le traditionnel "traffic shaping"

    CBQ permet de scinder un trafic en flux différents. Nous allons étudier le cas où l'on veut séparer les flux TCP des autres flux. Les paquets TCP doivent avoir une bande passante de 8Mbit. Il faut d'abord assigner une classe racine au périphérique concerné :
    tc qdisc add dev ppp0 root handle 10: cbq bandwidth \
    10Mbit avpkt 1000 mpu 64
    Ensuite, on définit deux classes :
    tc class add dev ppp0 parent 10:0 classid 10:1 cbq \
    rate 8Mbit allot 1514 \
    prio 1 maxburst 20 avpkt 200 isolated
    Ceci définit la première classe, puis passons à la deuxième classe :
    tc class add dev ppp0 parent 10:0 classid 10:2 cbq \
    rate 2Mbit allot 1514 \
    prio 1 maxburst 20 avpkt 200 isolated
    Puis, on marque les paquets :
    iptables -t mangle -A OUTPUT -o eth0 -p tcp -j mark -set-mark 1
    Ici, tous les paquets TCP sont marqués 1. On marque alors les paquets non TCP :
    iptables -t mangle -A OUTPUT -o eth0 ! -p tcp \
    -j mark -set-mark 2
    Enfin, il faut définir les filtres. Les paquets marqués 1 sont mis dans la classe 10:1
    tc filter add dev eth0 protocol ip \
    handle 1 fw flowid 10:1
    puis c'est le tour des autres paquets (marqué 2)
    tc filter add dev eth0 protocol ip \
    handle 2 fw flowid 10:2

    Descendre dans l'arborescence

    Pour augmenter le niveau de séparation on peut envisager de séparer par exemple le flux non TCP en UDP et autre protocoles. Pour cela, il faut rajouter deux classes, modifier le marquage, et appliquer les filtres :
    • Ajout des classes :
      tc class add dev eth0 parent 10:2 classid 10:3 cbq \
      rate 1.5Mbit allot 1514 \
      prio 1 maxburst 20 avpkt 200 isolated
      puis
      tc class add dev eth0 parent 10:2 classid 10:4 cbq \
      rate 0.5Mbit allot 1514 \
      prio 1 maxburst 20 avpkt 200 isolated
    • Modification du marquage :
      iptables -t mangle -A OUTPUT -o eth0 -p tcp \
      -j mark -set-mark 1
      iptables -t mangle -A OUTPUT -o eth0 -p udp \
      -j mark -set-mark 2
      iptables -N Reste iptables -A Reste ! -p udp -j mark -set-mark 1
      iptables -t mangle -A OUTPUT -o eth0 ! -p tcp -j Reste \
      -j mark -set-mark 3

    Privilégier les flux interactifs

    L’utilisation de prio permet de privilégier les flux interactifs. Le plus intéressant est de combiner cela à un partage de la bande passante. On peut, par exemple, réserver une bande passante de 30kbit/s pour les flux ssh et icmp.
    tc class add dev eth0 parent 10:0 classid 10:1 cbq \
    rate 30kbit allot 1514 \
    prio 1 maxburst 20 avpkt 200 bounded isolated

    La priorité prio est à 1, il est donc prioritaire par rapport au paquet de la classe 10:2  :

    tc class add dev eth0 parent 10:0 classid 10:2 cbq \
    rate 2Mbit allot 1514\
    prio 2 maxburst 20 avpkt 200 isolated

    Limitation en entrée

    Grâce à Ingress, on peut limitercertains trafics entrant en bande passante. Il faut tout d’abord attacher une "Queuing displine" au périphérique que l’on souhaite controler.
    tc qdisc add dev ppp0 handle ffff : ingress

    Il est alors possible de spécifier qu’elle doit être l’attitude à adopter face à certains flux. Ici, on limite à 200kbit le flux marqué 5 par netfilter sur l’interface ppp0.

    tc filter add dev ppp0 parent ffff : \
    protocol ip handle 5 fw \
    police rate 200kbit burst 10k drop \
    flowid :1

    La variable burst permet de réaliser un flux de type tocken bucket. Un seau de 10ko est créé avec un taux de remplissage de 200kbit/s.

    Les paquets doivent être marqués dans la chaine PREROUTING puisque le rejet des paquets est fait au plus près de l’interface.

    Utilisation de HTB

    Manuel DERA a écrit une nouvelle implémentation de CBQ. Le fonctionnement est globalement similaire à celui de CBQ (voir le manuel). La différence notable est que cette implémentation utilise la notion de Token Bucket.

    On affecte une discipline htb au périphérique eth0.

    tcqdiscadddev eth0 root handle 1 : htb default 12

    Les paquets non affectés dansles flux sont mis dans la classe 1:12 grâce au mot clé default.

    Mais, il faut aussi déclarer une classe racine sinon il n’est pas possible d’emprunter de la bande passante. Ici, on veut englober toute la bande passante afin de filtrer l’ensemble du trafic.

    tc class add dev eth0 parent 1 : classid 1:1 htb \
    rate 10mbit ceil 10mbit burst 20k

    On définit ensuite des classes :

     1:1 : Classe pour TCP, limité à 2mbit/s.
     1:2 : Classe pour UDP, limité à 6mbit/s.
     1:12 : Classe pour les autres, limité à 1mbit/s. Ce qui donne :

    tc class add dev eth0 parent 1 : classid 1:1 htb \
    rate 1mbit ceil 10mbit burst 20k
    tc class add dev eth0 parent 1 : classid 1:2 htb \
    rate 8mbit ceil 10mbit burst 20k
    tc class add dev eth0 parent 1 : classid 1:12 htb \
    rate 1mbit ceil 1mbit burst 20k

    Ensuite, on n’affecte les flux marqués 1 et 2 à leur file respective :

    tcfilter add dev eth0 protocol ip handle 1 fw flowid 1:1
    tc filter add dev eth0 protocol ip handle 2 fw flowid 1:2

    Les mots clés ont les significations suivantes :

     rate : Débit à l’équilibre
     ceil : Débit maximal autorisé pour la classe
     burst : Taille du seau, elle est égale à la quantité maximale de données pouvant être envoyée d’une classe avant de servir une autre classe.


    Prise en charge des trafics par bouffée

    Le but de la manoeuvre est de limiter le trafic utilisé par un flux donné. Dans cet exemple, on va autoriser le flux smtp à prendre une grande partie de la bande passante, mais ceux que pendant un bref moment. Le flux permanent smtp est limité à 500kbit/s pour une bande passante de 10mbit/s. On désire de plus que la bande passante maximale utilisée soit 1mbit/s. Tout d’abord, définissons une discipline HTB sur eth0 après avoir remis à zéro le périphérique :
    tc qdisc del dev eth0 root
    tc qdisc add dev eth0 root handle 1 : htb default 2

    Ensuite, on définit, outre la classe racine englobant la bande passante totale, deux sous-classes. La première est destinée à recevoir le flux soumis au tocken bucket. La seconde est mise en place pour recevoir le trafic restant (la classe 1:2 est la classe par défaut comme on l’a défini auparavant).

    tc class add dev eth0 parent 1 : classid 1:1 htb \
    rate 10mbit ceil 10mbit burst 20k
    tc class add dev eth0 parent 1:1 classid 1:2 htb \
    rate 1mbit ceil 1mbit burst 20k
    tc class add dev eth0 parent 1:1 classid 1:3 htb \
    rate 8mbit ceil 10mbit burst 20k

    La classe 10:2 se voit ensuite affectée une discipline TBF :


    tc qdisc add dev eth0 parent 1:2 handle 2:
    tbf rate 0.5mbit burst 20kb latency 70ms
    peakrate 1mbit minburst 1540

     rate : Taux de remplissage du seau
     peakrate : Bande passante utilisée maximale
     burst : Taille du seau en bytes.
     latency : Temps de conservation des paquets dans la discipline.
     minburst : Doit être égal au MTU. Ensuite, on se contente de mettre les paquets dans leur files respectives :


    tc filter add dev eth0 protocol ip handle 2 fw flowid 1:3
    tc filter add dev eth0 protocol ip handle 1 fw flowid 1:2

    Random Early Detection

    C’est une discipline sans classes qui se met sur une feuille de l’arbre des flux :

    tc qdisc add dev eth0 parent 10:2 handle 2 : \
    red limit 10kb min 2kb max 4kb avpkt 1000 \
    burst 20 ecn probability 0.02

    Lorsque la taille de la queue est en dessous de min, les paquets sont acceptés ; entre min et max, la probabilité de rejet d’un paquet croit. Au dessus de max la probabilité de rejet est maximale, égale à probability. Lorsque la taille de la queue est supérieure à limit, tous les paquets sont rejetés.

    Si le flag exn est positionné, les machines, signalant dans leurs paquets qu’elles sont respectueuses du protocole ECN, sont prévenues par un paquet ICMP et non par un rejet de leurs paquets.

    Problèmes courants

    Approximation de bande passante avec CBQ

    CBQ a parfois du mal à approximer la bande passante. C’est particulièrement le cas pour les faibles bande passante et pour les périphériques de type PPP.

    HTB semble être à l’abri de ce genre de comportement.

    Paramètre burst

    Le paramètre burst exprime une quantité de tokens disponibles. Cette notion est utilisée par TBF et HTB qui en retirent un effet de bord assez original.

    En effet, le remplissage du seau au débit rate est fait à intervalles réguliers, toutes les secondes. Par conséquent, le débit est limité à burst/s ; il y a donc une limitation implicite qui oblige à augmenter la taille du burst lorsque la bande passante maximale désirée, fixée par rate, est plus importante que burst/s.

    Environnement de tests

    L’outil bench permet de tester la répartition des flux en période de congestion.

    Conclusion

    La syntaxe et la mise en oeuvre de la qualité de service sous Linux sont complexes. La définition d’une stratégie cohérente est nécessaire avant toute chose. Il faut donc déterminer précisément les besoins et arriver à la structure la plus simple possible. Les deux versions de CBQ ont chacune leur avantage. La simplicité et la précision de HTB sont très souvent un gros avantage. On ne trouve malheureusement pas les combinaisons possibles grâce à l’utilisation des mots bounded et isolated. Cependant, l’utilisation du mot bounded est à proscrire dans la plupart des cas. Pour la mise en oeuvre, HTB représente donc le meilleur compromis, et ce, malgré le patch nécessaire du noyau et de tc.

    Répondre à cet article

regit





Il y a 3 contribution(s) au forum.

Introduction à la qualité de service sous Linux
(1/3) 16 octobre 2006, par Nicolas
> Introduction à la qualité de service sous Linux
(2/3) 24 juillet 2004, par ben essayyad
> Introduction à la qualité de service sous Linux
(3/3) 22 mai 2002, par regit




Introduction à la qualité de service sous Linux
16 octobre 2006, par Nicolas   [retour au début des forums]

Merci pour cet article tres bien expliqué qui va surement me permettre d’avoir une bonne note à mon contrôle tp, la commande tc est maintenant beaucoup plus claire pour moi.

Nicolas

[Répondre à ce message]

> Introduction à la qualité de service sous Linux
24 juillet 2004, par ben essayyad   [retour au début des forums]

Je vous remercier beaucoup pour votre article sur la qualité de service sous linux ; il est très bien fait .Je suis stagiaire ds un labo de réseau & teleocm à l’ecole Mohammadia d ingénieur ( Maroc) ; je veux savoir si vous pouvez me recommander une méthode ou une voix pour faire la qualité de service sous linux parce que je sais pas par où commencer mon e-mail est benessayyd@yahoo.fr et merci beaucoup

[Répondre à ce message]

> Introduction à la qualité de service sous Linux
22 mai 2002, par regit   [retour au début des forums]

La discipline Ingress semble être parfois touchée par un bug (jusqu’au 2.4.18 inclus, voir au-delà). Lors de la création de deux displines sur deux interfaces, le système se bloque complètement.

Il semble qu’une solution soit de compiler ingress en module.

[Répondre à ce message]

Da Trucs for Linux | PLAN DU SITE | ADMIN