Si vous avez déjà joué avec un BSD, vous devez certainement connaître (et apprécier) Packet Filter (PF), le firewall développé sur OpenBSD. Si ce n'est pas le cas, on a déjà sûrement dû vous en vanter la syntaxe claire par rapport à celle incompréhensible d'iptables (certes, au bout de la 15ème fois qu'on utilise une commande, on commence à se rappeler à quoi sert l'obscure option -machin). Mais quid du queueing avec PF ?
Avant de commencer, je tiens à préciser que bien que PF existe sur NetBSD, FreeBSD et DragonFly BSD, cet article ne concerne qu'OpenBSD, car à l'heure où sont écrites ces lignes, le code n'a pas été porté sur les autres BSD.
Le queueing permet de faire de la Qualité de Service (QoS). Entendons-nous bien, la QoS est à utiliser en dernier recours, quand on ne peut pas augmenter le tuyau. Les opérateurs réseau qui veulent vous proposer des services de meilleures qualités (et donc, ne pas respecter la neutralité du net) sont simplement des entreprises qui, au lieu de souhaiter investir (et dépenser de l'argent), préfèrent vous vendre un service sans rien faire (à part configurer des routeurs, mais c'est simple, comme vous allez le voir dans cet article qui a pour but de le démontrer). Comme généralement les gens ne veulent pas déplacer leur maison pour améliorer la qualité de leur ADSL (ou mieux, avoir de la fibre), il est habituel d'avoir recours à la QoS.
1. La QoS ?
D'après Wikipédia, « La qualité de service (QDS) ou quality of service (QoS) est la capacité à véhiculer dans de bonnes conditions un type de trafic donné, en termes de disponibilité, débit, délais de transmission, gigue, taux de perte de paquets. ». Sur ma ligne ADSL, quand j'utilise une ligne SIP en plus d'un client torrent, mes interlocuteurs se plaignent toujours de la qualité de la communication jusqu'à ce que je coupe le client torrent, pour qu'au final ils disent « Ah, je t'entends comme si tu étais dans la pièce à côté ». Néanmoins, la QoS n'est pas magique, elle ne fera pas revenir l'être aim^WWWWaugmenter la capacité de la ligne, elle va juste agencer les paquets de manière intelligente plutôt que le traditionnel First In First Out (FIFO).
D'autre part, la QoS agit uniquement sur les paquets sortants. Parce que, par définition, la QoS agit uniquement quand il y a un manque de ressources, donc elle ne pourra pas agir sur les paquets entrant sur une machine. En effet, soit le manque de ressources est de la puissance de calcul, et alors ajouter un traitement CPU sur chaque paquet ne va pas magiquement améliorer les choses si votre système n'arrive pas à gérer les interruptions (IRQ), soit le manque de ressources est dû à la taille du tuyau et alors, une fois que le paquet est arrivé sur l'interface, il est trop tard pour prendre la peine de lui appliquer une politique quelconque.
Là où la QoS est magique, c'est quand il n'y a pas suffisamment de bande passante ou de timeslots pour émettre un paquet immédiatement. Si l'interface est au repos (idle), un paquet avec une haute priorité ne partira pas plus vite qu'un paquet avec une faible priorité.
Le système gérant le choix du paquet qui sera émis, s'appelle un scheduler.
2. Sous OpenBSD
Par défaut, le scheduler est du FIFO, mais depuis quelques années jusqu'à maintenant, (i.e. OpenBSD 5.5), le système comportait un scheduler nommé ALTQ. Lorsqu'il a été mis en place, ça avait été une révolution, mais comme toute révolution en informatique, 5 ans plus tard, c'est désuet. Pour dire quelques mots là-dessus, ce scheduler permettait de faire du queueing par classe (CBQ), ou alors juste de la priorité (PRIQ) et pour les chanceux qui ont su se dépatouiller avec, un système Hierarchical Fair-Service Curve (HFSC).
Depuis OpenBSD 5.5, PF intègre un nouveau système de queueing avec deux modes : le premier permettant de faire de la priorisation (comme ALTQ le permettait, sauf que là, c'est encore plus simple) et le deuxième qui permet de gérer les queues. Ce système se base sur HFSC, puisque c'est devenu l'état de l'art entretemps (néanmoins, je n'en parlerai pas plus, mais j'invite les lecteurs curieux à lire le papier à propos : http://conferences.sigcomm.org/sigcomm/1997/papers/p011.pdf) tout en utilisant une syntaxe claire le rendant abordable pour quelqu'un qui a passé 10 minutes à lire la page man de pf.conf (oui oui, OpenBSD fournit des pages man des fichiers de conf... Des assistés ces admins, j'vous jure !).
Toutes les règles ont lieu dans le fichier contenant les règles pour PF (généralement /etc/pf.conf), appelé avec l'option -f de pfctl.
3. Le système de priorisation
Alors que sous ALTQ, pour la priorisation (comme pour le reste), il fallait déclarer les queues puis assigner les paquets à ces dernières, à partir de maintenant, plus besoin des les assigner. Tu veux que tes paquets SSH passent avant ceux des animes de ta copine ? Il suffit de transformer la règle :
pass out proto tcp to port ssh
en :
pass out proto tcp to port ssh set prio 6
Tout simplement. Pour ceux qui n'auraient pas compris, il suffit d'ajouter les mots set prio suivis d'un chiffre compris entre 0 et 7, avec comme valeur par défaut 3.
Bon, quand je dis que le scheduler prend la valeur 3 par défaut, ce n'est pas tout à fait vrai ; en fait, les paquets liés aux annonces de CARP et aux Spanning Tree Protocols sont priorisés par défaut, donc ce n'est pas la peine de le faire manuellement.
Et pour mes ACK ? Oui, je comprends que tu souhaites vouloir prioriser les paquets avec un Type of Service (ToS) de lowdelay. Pour faire cela, c'est comme avant, c'est le deuxième chiffre qui va s'appliquer. Par exemple :
pass out proto tcp to port www set prio (4,5)
permet de prioriser les paquets petits et nécessitant de passer rapidement, plutôt que ceux grassouillets remplis à ras de contenus.
4. Mais c'est trop facile... Il y a plus compliqué ?
La grosse nouveauté dans ce système de queueing, c'est un scheduler utilisant HFSC avec une UI simple. Néanmoins, cela reste quand même un peu compliqué, donc il va falloir voir plusieurs choses avant de goûter aux règles de déclaration des queues (car elles, contrairement à la priorisation, ont besoin d'être déclarées).
4.1 Comment les déclare-t-on ?
Les queues sont de trois types :
- queue root de l'interface, pas de queue ascendante ;
- queue fille et n'ayant pas de descendance ;
- les autres, ayant à la fois une dépendance et une ascendance.
La queue root doit obligatoirement spécifier une interface et on peut assigner des paquets uniquement aux queues filles. Les queues dépendent de l'interface de la queue root qui leur est ascendante (cette phrase est compliquée, dans les faits c'est logique, vous allez le voir tout à l'heure).
Pour chaque queue (sauf la root), on doit indiquer la queue parente (mot-clé parent) et au moins une des queues filles doit être définie comme celle par défaut (mot-clé default).
Pour chaque queue, il y a trois types de bande passante (j'espère que tu aimes la filiation) :
- bande passante cible (mot-clé bandwidth) obligatoire,
- bande passante minimale (mot-clé min),
- bande passante maximale (mot-clé max).
La bande passante cible est quelque part entre le minimum et le maximum. Une queue peut recevoir plus de bande passante tant que la queue ascendante en a de disponible. La bande passante minimum sera réservée et la bande passante maximum sera ... la valeur maximale qui lui est attribuée.
Pour attribuer les valeurs de bande passante, on peut utiliser les suffixes K, M et G qui représentent kilobits, mégabits et gigabits par seconde respectivement. Bien évidemment, la bande passante attribuée à une queue ne peut pas dépasser la quantité de bande passante attribuée à l'interface. Par exemple :
queue rootq on em0 bandwidth 10M
queue mail parent rootq bandwidth 1M
queue ssh parent rootq bandwidth 2M min 500K
queue www parent rootq bandwidth 7M max 9M default
On crée la queue root sur l'interface em0 (donc, toutes les queues filles seront sur em0), on crée une queue mail, une queue ssh et une queue www qui sera la queue par défaut.
Pour chacune des bandes passantes, on peut spécifier une bande passante « burst ». Cela se fait en utilisant le mot-clé burst suivi d'une passante en précisant une durée (avec le mot-clé for). En reprenant l'exemple précédant :
queue rootq on em0 bandwidth 10M
queue mail parent rootq bandwidth 1M burst 3M for 250ms
queue ssh parent rootq bandwidth 2M min 500K
queue www parent rootq bandwidth 7M max 9M default
Donc, si pour une raison ou pour une autre, la queue mail a besoin de plus de bande passante, pendant 250ms elle pourra accéder à 3Mbps. Par contre, elle devra retourner en idle pour qu'elle puisse à nouveau se voir attribuer le trafic burst.
On peut aussi changer la taille de la queue, c'est-à-dire le nombre de paquets qui seront gardés avant d'être émis avec le mot-clé qlimit. Par défaut, la valeur est de 50 paquets. Cela donne :
queue rootq on em0 bandwidth 10M
queue mail parent rootq bandwidth 1M burst 3M for 250ms
queue ssh parent rootq bandwidth 2M min 500K
queue www parent rootq bandwidth 7M max 9M default qlimit 40
Dans ALTQ, il y avait le mot-clé borrow pour permettre à une queue de laisser sa bande passante inutilisée accessible aux autres ; il n'existe plus dans ce nouveau système, car c'est automatiquement le cas.
4.2 Et... c'est tout ?
Maintenant que les queues sont déclarées, il faut assigner les paquets via les règles pass ou match (voire block si on a une block-policymise à return). Un jeu de règles complet sera :
queue rootq on em0 bandwidth 10M
queue mail parent rootq bandwidth 1M burst 3M for 250ms
queue ssh parent rootq bandwidth 1M min 500K
queue ssh_interactive parent ssh bandwidth 500K min 400K
queue ssh_bulk parent ssh bandwidth 500K
queue std parent rootq bandwidth 7M max 9M default qlimit 40
queue tcp_ack parent rootq bandwidth 1M min 500K
block on em0 set queue std
pass out on em0 proto tcp to port 25 set queue mail
pass out on em0 proto tcp to port ssh set queue (ssh_bulk, ssh_interactive)
pass out on em0 proto tcp to port www set queue (std, tcp_ack)
De la même manière que pour la priorisation, on peut mettre deux queues afin que les paquets avec un ToS de lowdelay soient assignés à la queue listée en deuxième.
Volontairement, je ne détaille pas 50 jeux de règles car généralement, les gens ont déjà le leur et donc, il ne vous reste plus qu'à déclarer vos queues et à leur attribuer du trafic maintenant que vous savez comment faire.
Conclusion
Vous vous demandez sans doute si l'on peut combiner les deux systèmes... Eh bien non, si on utilise le système de bandwidth shaping, le système de priorisation est ignoré. En effet, la priorisation a un effet uniquement quand on a une bande passante limitée. Avec le système de bandwidth shaping, on priorise en regardant quel trafic appartient à quelle queue et quels sont les paramètres de la queue. D'après henning@ (le développeur qui développe actuellement PF) utiliser les deux systèmes n'aurait pratiquement aucun effet.
Néanmoins, PF ne permet pas de couvrir tous les scénarios, je vous invite à lire l'article de tedu@ (http://www.tedunangst.com/flak/post/userland-traffic-shaping) qui répondra à d'autres besoins.