** Mise en place d'un service de bande passante garantie avec linux et HTB. ** Auteur: Clément "nodens" Hermann Modifié le : 11 déc 2003 Bon, ce document est probablement sous GPL v2... Mais j'ai eu la flemme d'ajouter les notices légales pour le moment. La version la plus récente de ce docemnt se trouve sur http://clement.hermann.free.fr/QoS/QoS.txt Je suis bien entendu ouvert à tout ajout, correction, suggestion, etc. Références: - le Linux Advanced Routing Howto: http://www.lartc.org - la homepage de htb: http://luxik.cdi.cz/~devik/qos/htb/ et les manpages de tc, bien sûr... INTRODUCTION Ce document décrit l'utilisation des fonctionnalités QoS de linux pour faire de la séparation de bande passante, en utilisant la queuing discipline HTB. Qu'est-ce que la séparation de bande passante ? c'est "découper" artificiellement la bande passante sur un lien donné. Dans notre exemple, on veut fournir au client un service de bande passante garantie, tout en continuant à utiliser la bande passante qu'il n'utilise pas pour le datacenter. Ca coûte moins cher à effectuer que la mise en place d'une LS dédiée, c'est plus souple et ça permet d'utiliser la bande passante quand il n'en a pas besoin. Qu'est-ce qu'une Queuing discipline ? C'est la méthode qui consiste à modifier la manière dont les paquets sont traité dans la queue du noyau. Autrement dit, c'est une des facettes du QoS (Quality of Service). Normalement, la discipline FIFO (First In, First Out) est utilisée. Dans notre cas selon certains paramètres, l'ordre dans lequel les paquets vont passer sera modifié, et certains pourront même être mis en attente ou droppés pour obtenir l'illusion de plusieurs liens distinct. Qu'est-ce que HTB ? HTB signifie Hierarchy Tocken Bucket (seau à jetons hierarchique). Ce nom fait référence au fonctionnement théorique de la qdisc HTB, que nous ne développerons pas ici. Cette qdisc a l'avantage d'être "classful", c'est à dire qu'elle offre la possibilité de créer des classes en arbre qui sont autant de "tranches" de bande passante. Le traffic est ensuite classifié à l'aide de filtres dans ces différentes classes en fonction de ce qui les différencie (par exemple, selon le port source, le port destination, l'ip source, l'ip de destination, le bit ToS...). Pour en savoir plus sur le QoS en général, voir le Linux Advanced Routing and Traffic Control HOWTO : http://www.lartc.org Pour des détails sur HTB, voir la homepage du projet: http://luxik.cdi.cz/~devik/qos/htb/ PREREQUIS Noyau >= 2.4.20 Tout d'abord, il faut un noyau qui supporte les fonctionalités de QoS et HTB en particulier. HTB est intégré dans les noyaux linux depuis la version 2.4.20, pour les version précédentes il existe des patches. Il est possible de compiler le support du QoS sous forme de modules. Je pars du principe que le lecteur sait comment recompiler un noyau pour obtenir ce dont il a besoin. Iproute Il faut les outils en user space permettant de controller les fonctionnalités du noyau en question. Cela nécessite une version récente d'iproute2 pour le support HTB, ou encore l'utilisation des patches que l'on trouvera sur la page du projet HTB. La version disponible en Debian Sid (Unstable) supporte le HTB sans problème. Avoir lu le LARTC HOWTO ne peut pas faire de mal... DECOR: Pour faciliter la compréhension, nous partirons d'un exemple simple: L'hébergeur isp.com possède un datacentre doté d'un lien à 10Mbit/s vers internet. Il veut pouvoir garantir à son client Grossbouf une bande passante d'1Mbit en toutes circonstances, même si la bande passante utilisée par le reste du datacenter est saturée. Par contre, il connait bien Grossbouf et sait qu'il a souvent les yeux plus gros que le ventre. Il estime que la bande passante de Grossbouf aura un taux d'utilisation qui dépassera rarement les 60%. Comme il n'a pas tant de bande passante que ça, il veut pouvoir utiliser la bande passante non utilisée par les sites de Grossbouf (donc sans aucune pénalisation pour ces derniers). LET'S GO ! pour commencer à utiliser le QoS sous linux, il faut d'abord créer une qdisc racine sur l'interface dont on veut mettre en forme le traffic. (NB: il est inutile et peu efficace d'essayer de mettre en forme le traffic entrant sur une interface. Cela peut servir dans quelques cas très précis, mais ils ne seront pas traités ici. Donc toutes les classes se mettent en place sur l'interface externe par rapport au datacenter. Si l'on désire réellement limiter l'upload, il faut le faire sur l'interface pour laquelle le traffic est sortant dans ce cas-là. Par exemple, si l'interface du datacenter est eth0 et l'interface côté internet est eth1, on limitera le traffic pour le download sur eth1 et pour l'upload sur eth0 (en utilisant les mêmes méthodes). Dans la pratique, il est rare que l'on ait besoin de limiter l'upload pour un serveur web, donc on ne traite pas ce cas ici pour l'instant. Peut-être dans la prochaine version ;) ). pour cela (et pour tout ce qui touche à la manipulation des fonctionnalités QoS du noyau), on utilise l'outil tc: bridge:~# tc qdisc add dev eth0 root handle 1: htb default 10 traduction: on crée une _qdisc_ _racine_ sur le périphérique _eth0_, qui a pour identifiant (handle) 1:0 (ou 1:) et qui utilise htb. Tout le traffic non explicitement classifié ira vers la classe ayant le numéro mineur 10 (donc 1:10). Note: les qdisc et les classes ont un identificateur de la forme x:y où x est un numéro majeur (celui de la qdisc racine) et y est un numéro mineur. Ensuite, on crée une première classe qui nous sert à contenir les autres: bridge:~# tc class add dev eth0 parent 1: classid 1:1 htb rate 10Mbit on a donc créé une classe sur le périphérique eth0, qui a pour parent l'objet portant le handle 1: (notre qdisc racine), et pour identifiant 1:1. elle utilise htb et a pour bande passante 10Mbit (voir le man tc-htb et les url citées en intro pour plus de détails). maintenant, on peut découper notre bp en tranche: notre datacentre d'abord, avec une bande passante réservée arbitrairement fixée à 2Mbit, et 2 nouveaux paramètres: - "ceil". ceil vaut par défaut le rate, et permet d'indiquer combien de bande passante la classe peut emprunter si elle est disponible (dans une autre classe ou la classe mère). Notre Datacentre peut emprunter autant de bande passante qu'il y en a (soit au maximum 10Mbit). - "prio". prio permet de déterminer la priorité d'une classe sur une autre quand il s'agit d'emprunter de la bande passante inutilisée. Plus la valeur est faible, plus la classe est prioritaire. On veut que le datacentre soit prioritaire quand il s'agit d'emprunter de la bp, prio vaut donc 0. bridge:~# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 2Mbit ceil 10Mbit prio 0 Ensuite, la tranche de Grossbouf: bridge:~# tc class add dev eth0 parent 1:1 classid 1:11 htb rate 1Mbit prio 1 On n'a pas défini de ceil, qui vaut donc la meme chose que rate. Autrement dit, Grossbouf ne peut utiliser plus de bande passante que son rate, meme si il y en a de disponible. De plus, il n'a qu'une priorité de 1. Cela n'influe pas puisqu'il ne peut pas consommer plus que son rate, mais cela pourrait s'avérer utile à l'avenir. Evidemment, Si Grossbouf utilise toute sa bande passante, il est hors de question que le datacentre lui emprunte quoi que ce soit. Maintenant, on va définir des filtres qui vont nous permettre de classifier le traffic en fonction de l'ip source. le site de Grossbouf a pour ip 1.2.3.4. Pour cela, on utilise le filtre u32: bridge:~# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.4/32 flowid 1:11 ce filtre s'applique sur le périphérique eth0, sur le protocole "ip". il a pour parent la qdisc racine 1:0 et une priorité de 1. il utilise u32, qui regarde l'ip source (match ip src) du paquet, et classe les paquets qui matchent dans la classe 1:11. Il est inutile de créer un filtre pour le datacentre, puisque c'est la classe par défaut. imaginons que Grossbouf ait un site sur une autre machine, mais ne veuille pas de bande passante supplémentaire: il suffit de rajouter un filtre pour classifier un autre traffic dans sa classe. Par exemple, pour un site ayant l'ip 1.2.3.5: bridge:~# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.5/32 flowid 1:11 les 2 sites se partageront la bande passante. Plusieurs classes client: Imaginons que le client concurrent de Grossbouf, Baffre, voyant cela, décide d'acheter le meme service. Lui décide d'investir et de prendre 2Mbit de bande passante. Il nous suffit de rajouter une classe 1:12 ayant pour parent 1:1 : bridge:~# tc class add dev eth0 parent 1:1 classid 1:12 htb rate 2Mbit prio 1 et les filtres associés: bridge:~# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.10/32 flowid 1:12 bridge:~# tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 1.2.3.11/32 flowid 1:12 Inutile de modifier quoi que ce soit dans la configuration en place. Bien sur, pour se simplifier la vie, il faut mettre tout ça dans un script à lancer au démarrage de la machine. Un script très simple reprenant les exemples cités ici est disponible sur http://clement.hermann.free.fr/QoS/qos.sh.txt Dans le cas d'un grand nombre de règles, il est intéressant d'avoir un format plus lisible. Alors, il est intéressant d'utiliser un wrapper comme htb.init : http://sourceforge.net/projects/htbinit ANNEXE: cas du bridge: Notre hébergeur, isp.com, décide de faire le QoS par une machine dédiée, pour éviter de se compliquer la tache. De plus, il veut mettre cela sur un bridge transparent pour ne pas avoir à changer la configuration de son firewall et de ses routeurs. La mise en place du bridge est très simple sous debian, il suffit d'installer le package bridge-utils et de suivre les instructions contenues dans /usr/share/doc/bridge-utils-/README.Debian.gz (en substance, la définition d'une interface br0 dans /etc/network/interfaces). Par contre, le bridge n'a qu'une seule interface logique ! Où faut-il donc mettre nos qdisc et autres classes ? La réponse est simple: au meme endroit qu'avant (donc eth0 eth1 etc). il suffit que eth0 et eth1 soient up et en mode promiscuous (le script de démarrage du réseau s'en occupe).