/!\ 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
This commit is contained in:
salles 2006-04-24 14:14:58 +02:00
parent 486b27ad13
commit 8b33c17bd8

View file

@ -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) :