Préambule
Suite à une discussion avec la DSI, il est envisagé d'ouvrir partiellement et de manière contrôlée une partie du P2P. Cette ouverture du firewall a pour vocation d'offrir l'accès aux adhérents à des services légaux jusqu'alors interdits.
Pour des raisons idéologiques et de ressources limitées, cette solution est limitée au seul protocole Bittorrent. Celui-ci est libre et donc parfaitement analysable. D'autre part, identifier d'autres protocoles engendrerait une augmentation proportionnelle des ressources disponibles et limitées. Bittorrent est, de plus, le protocole le plus répandu et le plus utilisé pour diffuser des contenus libres (distributions Linux, œuvres sous licence Creative Commons, ...).
Pour ce faire, il faut mettre en place un système de limitation de la bande passante pour les paquets identifiés, dans ce cas, comme étant du Bittorrent. On opère alors en 2 étapes :
- identifier et marquer les paquets que l'on veut limiter
- leur appliquer une qualité de service limitant la bande passante de ce traffic.
Marquer les paquets
Identification
Ce qui est décrit dans cette partie s'attache à une identification particulière : un traffic de pair à pair de type Bittorrent. On peut très aisément remplacer cette partie par un autre procédé d'identification.
Dans notre cas, pour identifier du Bittorrent, on utilise iptables avec le module ipp2p. L'exemple suivant montre comment supprimer les paquets comportant des commandes Bittorrent :
iptables -A PREROUTING -p tcp -m ipp2p --bit -j DROP
Marquage
L'exemple précédent nous montre comment identifier à l'aide de ipp2p les paquets contenant des commandes utilisées par le protocole Bittorrent.
Mais cela nous limite donc aux seules commandes clairement identifiables. Quid des transferts de données qui passent alors inaperçus ?
Chaque paquet individuel a une marque, qui est nulle (0x0) par défaut. Cette marque est une valeur interne au noyau qui n'a pas d'existence physique dans le paquet. Les modifications de marques s'opèrent dans la table mangle. Par exemple, pour mettre la marque 0x1 à tous les paquets Bittorrent, on procède comme suit :
iptables -t mangle -A PREROUTING -p tcp -m ipp2p --bit -j MARK --set-mark 0x1
Cependant, cela ne marque pas tous les paquets de la même connexion. Chaque connexion (flux de paquets) a aussi une marque. Par exemple, pour marquer tous les paquets d'une connexion dès que l'un d'entre eux contient une commande Bittorrent, on procède comme suit :
iptables -t mangle -A PREROUTING -p tcp -j CONNMARK --restore-mark iptables -t mangle -A PREROUTING -p tcp -m mark ! --mark 0x0 -j ACCEPT iptables -t mangle -A PREROUTING -p tcp -m ipp2p --bit -j MARK --set-mark 0x1 iptables -t mangle -A PREROUTING -p tcp -m mark --mark 0x1 -j CONNMARK --save-mark
- La marque du paquet prend la valeur de celle de la connexion.
- Tous les paquets qui ont une marque non nulle sont acceptés (on ne va donc pas plus loin si le paquet est déjà identifié comme appartenant à une connexion Bittorrent).
Tous les paquets qui sont identifiés comme du Bittorrent reçoivent la marque 0x1.
- La marque du paquet est sauvegardée dans la marque de connexion.
Gestion de classes
Pour appliquer une qualité de service aux paquets marqués, il faut procéder en deux étapes :
- créer des classes auxquelles est affectée une qualité de service particulière,
- classer les paquets marqués dans ces classes.
Gestionnaire de classes
On s'occupe ici de configurer les classes qui accueilleront les flux à limiter.
Les classes sont organisées sous forme d'un arbre. Chaque classe a un identifiant de la forme M:m (M et m veulent dire respectivement majeur et mineur). Chaque gestionnaire de mise en file d'attente (ou qdisc) se voit attribuer un identifiant M: qui indique le majeur de ses classes filles. Par exemple :
1: Gestionnaire de mise en file d'attente racine
|
1:1 classe enfant
/ | \
/ | \
/ | \
/ | \
1:10 1:11 1:12 classes enfants
| | |
| 11:1 | classe terminale
| |
10: 12: Gestionnaire de mise en file d'attente
/ \ / \
10:1 10:2 12:1 12:2 classes terminales
Gestionnaire de mise en file d'attente racine
On définit ici le gestionnaire de mise en file d'attente racine qui fait la liaison avec le noyau pour réémettre les paquets classés dans les classes filles.
On le crée de la manière suivante :
tc qdisc add dev eth0 root handle 1: htb
Classe par défaut
On crée ensuite une classe enfant qui aura pour tâche de limiter le débit de tous ses enfants à 1Mbit/s.
tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbit ceil 1mbit
on rappelle donc l'identifiant du gestionnaire parent : parent 1:
on applique une qualité de service de type : htb
on définit la valeur nominale du débit autorisé : rate 1mbit
on définit la valeur maximale à ne pas dépasser : ceil 1mbit
On choisit une qualité de service htb pour sa simplicité de mise en œuvre. On pourrait aussi choisir un type cbq qui a d'autres avantages (comme donner des poids différents aux enfants), mais qui est plus difficile d'accès.
Une classe par adhérent
Suite à la classe définie ci-dessus pour l'ensemble du crans, il nous faut définir une classe pour chacun des adhérents. La commande est la même que précédemment avec quelques nuances quant aux valeurs indiquées :
tc class add dev eth0 parent 1:1 classid 1:2 htb rate 660bit ceil 1mbit
on rappelle l'identifiant du parent, ici la classe 1:1
on donne un identifiant unique pour cette nouvelle classe : classid 1:2
on définit une valeur nominale pour l'adhérent : rate 660bit
on définit une valeur maximale qu'il pourra atteindre : ceil 1mbit
La valeur rate garantit à l'adhérent de pouvoir disposer de ce débit (il est égal au total divisé par le nombre de classes). Dans le cas où d'autres n'utiliseraient pas leur bande passante, un adhérent a le droit d'emprunter chez les autres dans la limite de ceil, de façon équitable, quelque soit le nombre de machines de l'adhérent.
Ainsi, le nomble de classes sera égal au nombre d'adhérents + 1 (classe par défaut). La classe par défaut est utilisée par exemple dans le cas d'un adhérent dont la classe ne serait pas encore construite, ou encore dans le cas des clubs (qui ne sont pas des adhérents).
Qdisc
Une dernière étape consiste à ajouter aux classes des adhérents un qdisc qui s'assurera de la répartition des paquets qui lui seront attribués.
Le plus connu est le mode FIFO (First In First Out). Mais celui-ci peut poser des problèmes dans le cas où on limite la bande passante. En effet, statistiquement, c'est le traffic le plus gros qui passe. On applique donc une autre méthode, SFQ (Stochastic Fairness Queueing), qui donne une chance équitable à tous les flux de passer, en rendant aléatoire le choix du flux qui va être autorisé à passer (grâce à un tirage aléatoire). Par exemple :
tc qdisc add dev eth0 parent 1:2 handle 2: sfq
on rappelle l'identifiant de la classe parente : parent 1:2
on indique un identifiant unique (<9999) pour le qdisc : handle 2:
on précise la méthode retenue : sfq
Classification des paquets
Il ne reste plus alors qu'à indiquer au firewall de rediriger les paquets précédemment marqués vers la classe de l'adhérent. On crée pour cela des règles de la forme :
iptables -t mangle -A POSTROUTING -o eth0 -s 138.231.137.10 -m mark --mark 0x1 -j CLASSIFY --set-class 1:2 iptables -t mangle -A POSTROUTING -o eth0 -d 138.231.137.10 -m mark --mark 0x1 -j CLASSIFY --set-class 1:2
on indique l'interface considérée : eth0 (ici on considère la même dans les deux cas entrée/sortie, ce qui n'est pas le cas au crans)
on indique l'adresse ip source/destination : 138.231.137.10
on indique la marque que l'on cherche à rediriger : 0x1
on indique la classe du gestionnaire qui va se charger de gérer la mise en attente : 1:2
On pense aussi à créer les règles par défaut dans le cas où l'ip n'est pas celle d'un adhérent ou pas encore connue pour être celle d'un adhérent :
iptables -t mangle -A POSTROUTING -o eth0 -m mark --mark 0x1 -j CLASSIFY --set-class 1:9999
Au crans, la valeur retenue pour la classe par défaut est 1:9999.
Commandes utiles
Diagnostic de la classe associée à un adhérent
Si l'on souhaite vérifier le fonctionnement du lissage de trafic dans le cas d'un adhérent.
Il faut pour avoir les statistiques de la classe d'un adhérent, connaître au préalable son aid.
Dans le cas où aid = 1790, on obtient les statistiques de sa classe à l'aide de la commande :
tc -s -d qdisc ls | grep -A 1 1:1791
On notera à nouveau, que le numéro de la classe est égale à aid+1.
Documentation
Solution à base de connmark et de traffic control (htb) comme utilisé ici








