From 8b33c17bd800bce5e76fb68d889c82904a233796 Mon Sep 17 00:00:00 2001 From: salles Date: Mon, 24 Apr 2006 14:14:58 +0200 Subject: [PATCH] /!\ Cette solution n'a pas encore reu l'aval du CA et n'est donc pas officiellement utilisable. /!\ Cration des classes ncessaires au filtrage du p2p avec bande passante limite par adhrent. On cre une classe pour le p2p avec un debit max On cre une classe htb avec un dbit garanti de max/nb_adherent par adherent + 1 pour les paquets non associs un adhrent (club, ...) chaque classe on associ un qdisc avec sfq pour garantir de l'galit des flux malgr la diminution de dbit. On cre ensuite des rgles iptables qui classent les paquets marqus 1 dans la classe de l'adhrent correspondant. Les paquets appartenant un flux de bittorrent sont marqus 1 (pas encore dans le script de firewall) darcs-hash:20060424121458-72cb0-7288a90bfde1cd51abd4fa8b5d0d2481c259f326.gz --- gestion/gen_confs/firewall.py | 165 +++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 10 deletions(-) diff --git a/gestion/gen_confs/firewall.py b/gestion/gen_confs/firewall.py index a9768b8b..32bc0872 100755 --- a/gestion/gen_confs/firewall.py +++ b/gestion/gen_confs/firewall.py @@ -49,6 +49,16 @@ class IptablesError(Exception): def __str__(self): return "%s\n status : %s\n %s" % (self.cmd,self.err_code,self.output) +class TcError(Exception): + """ Gestion des erreurs de tc """ + def __init__(self,cmd,err_code,output): + self.cmd=cmd + self.err_code=err_code + self.output=output + syslog.syslog(syslog.LOG_ERR,"%s : status %s,%s" % (cmd,err_code,output)) + def __str__(self): + return "%s\n status : %s\n %s" % (self.cmd,self.err_code,self.output) + def iptables(cmd): """ Interface d'iptables """ syslog.syslog(syslog.LOG_INFO,cmd) @@ -57,6 +67,15 @@ def iptables(cmd): raise IptablesError(cmd,status,output) return output +def tc(cmd): + """ Interface de tc """ + syslog.syslog(syslog.LOG_INFO,cmd) + status,output=getstatusoutput("/sbin/tc "+cmd) + #print cmd + if status: + raise TcError(cmd,status,output) + return output + class firewall_crans : """ @@ -125,6 +144,10 @@ class firewall_crans : from lock import remove_lock remove_lock('firewall') + def mangle_table(self) : + """ Remplit la table mangle """ + return + def nat_table(self) : """ Remplit la table nat """ return @@ -166,6 +189,7 @@ class firewall_crans : return # Initialisation + #self.exception_catcher(self.mangle_table) self.exception_catcher(self.nat_table) self.exception_catcher(self.filter_table) self.exception_catcher(self.filter_table_tweaks) @@ -189,14 +213,16 @@ class firewall_crans : iptables("-t nat -P PREROUTING ACCEPT") iptables("-F") iptables("-t nat -F") + iptables("-t mangle -F") iptables("-X") iptables("-t nat -X") + iptables("-t mangle -X") print OK def test_mac_ip(self) : """ Reconstruit la correspondance MAC-IP des machines des adhérents """ - self.anim = anim('\tChaine TEST_MAC-IP',len(self.__machines())+1) + self.anim = anim('\tChaîne TEST_MAC-IP',len(self.__machines())+1) iptables("-t nat -P PREROUTING ACCEPT") iptables("-t nat -F TEST_MAC-IP") self.anim.cycle() @@ -261,7 +287,7 @@ class firewall_crans : def mac_ip_maj(self,ip_list) : """ Mise à jour de la correspondance MAC-IP pour les ip fournies """ ## Que faut-il faire ? - self.anim = anim('\tAnalyse travail à effectuer') + self.anim = anim('\tAnalyse du travail à effectuer') if ip_list == [''] : print OK + ' (rien à faire)' return @@ -436,6 +462,12 @@ class firewall_komaz(firewall_crans) : liste_reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12', '169.254.0.0/16', '192.168.0.0/16', '224.0.0.0/4'] + # Valeur des débits autorisés pour le p2p + machines = db.search('*')['machine'] + debit_p2p_max = 1000000 #"1mbit" + debit_p2p_adh = debit_p2p_max/(len(machines)+1) + + def reseaux_non_routables(self) : """ Construction de RESEAUX_NON_ROUTABLES_{DST,SRC} """ self.anim = anim('\tFiltrage ip non routables',len(self.liste_reseaux_non_routables)) @@ -446,6 +478,59 @@ class firewall_komaz(firewall_crans) : self.anim.reinit() print OK + def mangle_table(self) : + from affich_tools import anim, cprint, OK + self.anim = anim('\tStructure de la table mangle') + iptables("-t mangle -F POSTROUTING") + # On crée les chaines de sous-réseaux + for subnet in range(136,145)+range(148,152) : + try : + iptables("-t mangle -N SUBNET-%s_24" % subnet) + except IptablesError : + iptables("-t mangle -F SUBNET-%s_24" % subnet) + # On redirige les paquets dans la chaine appropriée + iptables("-t mangle -A POSTROUTING -o ens -s 138.231.%s.0/24 -j SUBNET-%s_24" % (subnet, subnet)) + iptables("-t mangle -A POSTROUTING -o crans -d 138.231.%s.0/24 -j SUBNET-%s_24" % (subnet, subnet)) + print OK + + adherents = db.search('paiement=ok')['adherent'] + + a = anim('\tGénération des classes de filtrage p2p', len(adherents)) + warn = '' + # Création des classes et qdisc + for interface in [self.eth_ext, self.eth_int] : + # On vide les classes et qdisc + try : + tc("qdisc del dev %s root" % interface) + except TcError, c: + warn += str(c) + '\n' + # On construit les classes et qdisc de base + # La partie principale qui définit le comportement par défaut + tc("qdisc add dev %s root handle 1: htb" % interface) + tc("class add dev %s parent 1: classid 1:1 htb rate %s ceil %s" % (interface, self.debit_p2p_max, self.debit_p2p_max)) + tc("class add dev %s parent 1:1 classid 1:9998 htb rate %s ceil %s" % (interface, self.debit_p2p_adh, self.debit_p2p_max)) + tc("qdisc add dev %s parent 1:9998 handle 9999: sfq perturb 10" % interface) + # On construit ensuite les classes et qdisc pour chaque adhérent + for adherent in adherents : + a.cycle() + class_id = int(adherent.id())+1 # On ne peut pas reprendre le numéro 1 + qdisc_id = class_id+5000 # Il nous faut un n° inférieur à 9998 unique + for interface in [self.eth_ext, self.eth_int] : + tc("class add dev %s parent 1:1 classid 1:%d htb rate %s ceil %s" % (interface, class_id, self.debit_p2p_adh, self.debit_p2p_max)) + tc("qdisc add dev %s parent 1:%d handle %d: sfq perturb 10" % (interface, class_id, qdisc_id)) + + # Classification des adhérents dans leur classe respective + for machine in adherent.machines() : + ip = machine.ip() + subnet = machine.ip().split('.')[2] + iptables("-t mangle -A SUBNET-%s_24 -o crans -d %s -m mark --mark 1 -j CLASSIFY --set-class 1:%s" % (subnet, ip, class_id)) + iptables("-t mangle -A SUBNET-%s_24 -o ens -s %s -m mark --mark 1 -j CLASSIFY --set-class 1:%s" % (subnet, ip, class_id)) + # Par défaut on envoit les paquets dans la classe 9998 + iptables("-t mangle -A POSTROUTING -o crans -m mark --mark 1 -j CLASSIFY --set-class 1:9998") + iptables("-t mangle -A POSTROUTING -o ens -m mark --mark 1 -j CLASSIFY --set-class 1:9998") + + print OK + def nat_table(self) : self.anim = anim('\tStructure de la table nat') for chaine in [ 'TEST_MAC-IP', 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST', 'TEST_VIRUS_FLOOD', 'LOG_VIRUS', 'LOG_FLOOD' ] : @@ -477,7 +562,7 @@ class firewall_komaz(firewall_crans) : print OK def filter_table_tweaks(self) : - self.anim = anim('\trègles spécifiques à komaz') + self.anim = anim('\tRègles spécifiques à komaz') for chaine in [ 'ADMIN_VLAN', 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS', 'CRANS_VERS_EXT', 'BLACKLIST_SRC', 'BLACKLIST_DST' , 'FILTRE_P2P' ] : iptables('-N %s' % chaine) iptables("-A FORWARD -i lo -j ACCEPT") @@ -497,8 +582,67 @@ class firewall_komaz(firewall_crans) : iptables("-I FORWARD -m mark --mark 2 -j ACCEPT") print OK + def classe_p2p_maj(self,ip_list=['138.231.137.170']) : + """ Mise à jour de la classification pour les ip fournies + On ne crée que les règles iptables pour classer les paquets, les + classes correspondantes ne sont à créer que toutes à la fois """ + ## Que faut-il faire ? + self.anim = anim('\tAnalyse du travail à effectuer') + if ip_list == [''] : + print OK + ' (rien à faire)' + return + + maj = {} + for ip in ip_list : + machine = db.search('ip=%s'% ip)['machine'] + if not machine : + # Suppression de la règle associée à la machine + maj[machine[0]] = '-' + + elif len(machine) == 1 : + # Ajout de la machine + maj[machine[0]] = '+' + else : + print WARNING + if debug : + sys.stderr.write("Plusieurs machines avec l'IP %s\n" % ip) + + print OK + + ## Traitement + # MAJ des règles de classification de l'IP + def procedure() : + self.anim = anim('\tMise à jour des classes p2p') + warn = '' + try : + for machine in maj.keys() : + adherent = machine.proprietaire() + ip = machine.ip() + subnet = ip.split('.')[2] + class_id = int(adherent.id())+1 # On ne peut pas reprendre le numéro 1 + if maj[machine] == '+' : + # Il faut ajouter cette entrée + option = '-A' + else : + # Il faut supprimer cette entrée + option = '-D' + iptables("-t mangle %s SUBNET-%s_24 -o ens -s %s -m mark --mark 1 -j CLASSIFY --set-class 1:%s" % (option, subnet, ip, class_id)) + iptables("-t mangle %s SUBNET-%s_24 -o crans -d %s -m mark --mark 1 -j CLASSIFY --set-class 1:%s" % (option, subnet, ip, class_id)) + except IptablesError, c: + warn += str(c) + '\n' + except KeyError : + print OK + + if warn : + print WARNING + sys.stdout.write(warn) + else : + print OK + + self.exception_catcher(procedure) + def post_start_hook(self) : - self.anim = anim("\tMise en place routage") + self.anim = anim("\tMise en place du routage") warn = '' for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' , 'echo 65536 > /proc/sys/net/ipv4/ip_conntrack_max' , @@ -515,7 +659,7 @@ class firewall_komaz(firewall_crans) : print OK def pre_stop_hook(self) : - self.anim = anim("\tArret routage") + self.anim = anim("\tArrêt du routage") status,output=getstatusoutput('echo 0 > /proc/sys/net/ipv4/ip_forward') if status : print ERREUR @@ -523,6 +667,7 @@ class firewall_komaz(firewall_crans) : print OK def start_fw_funcs(self) : + self.exception_catcher(self.classe_p2p_maj) self.exception_catcher(self.log_chaines) self.exception_catcher(self.test_virus_flood) self.exception_catcher(self.reseaux_non_routables) @@ -803,7 +948,7 @@ class firewall_zamok(firewall_crans) : self.serv_out_adm() def filter_table_tweaks(self) : - self.anim = anim('\tRegles specifiques a zamok') + self.anim = anim('\tRègles spécifiques à zamok') iptables("-P INPUT ACCEPT") iptables("-P FORWARD DROP") print OK @@ -846,7 +991,7 @@ class firewall_rouge(firewall_crans) : print OK def filter_table_tweaks(self) : - self.anim = anim('\tRegles specifiques a rouge') + self.anim = anim('\tRègles spécifiques à rouge') iptables("-P INPUT ACCEPT") iptables("-P FORWARD DROP") print OK @@ -888,7 +1033,7 @@ class firewall_vert(firewall_crans) : print OK def filter_table_tweaks(self) : - self.anim = anim('\tRegles specifiques a vert') + self.anim = anim('\tRègles spécifiques à vert') iptables("-P INPUT ACCEPT") iptables("-P FORWARD DROP") print OK @@ -908,11 +1053,11 @@ class firewall_sila(firewall_rouge): firewall_bleu = firewall_zamok if __name__ == '__main__' : - # Chaines pouvant être recontruites + # Chaînes pouvant être recontruites fw = eval('firewall_%s()' % hostname) chaines = [] for nom in dir(fw) : - if nom in [ 'log_chaines' , 'test_virus_flood', 'reseaux_non_routables', 'test_mac_ip' , 'blacklist' , 'ext_vers_serveurs' , 'serveurs_vers_ext', 'ext_vers_crans', 'crans_vers_ext' , 'filtre_p2p', 'admin_vlan' , 'serv_out_adm'] : + if nom in [ 'log_chaines' , 'test_virus_flood', 'reseaux_non_routables', 'test_mac_ip' , 'blacklist' , 'ext_vers_serveurs' , 'serveurs_vers_ext', 'ext_vers_crans', 'crans_vers_ext' , 'filtre_p2p', 'admin_vlan' , 'serv_out_adm', 'mangle_table', 'classe_p2p_maj'] : chaines.append(nom) def __usage(txt=None) :