#!/usr/bin/env python
# -*- coding: utf-8 -*-
# The authors of this code are
# Manuel Sabban <manu@feyd-rautha.org>
# Frédéric Pauget <pauget@crans.ens-cachan.fr>
# Mathieu Segaud <matt@minas-morgul.org>
# Nicolas Salles <salles@crans.org>
#
# Rewritten as inherited classes from firewall_crans
# by Mathieu Segaud <matt@minas-morgul.org>
#
# Copyright (c) 2004 Manuel Sabban, Frédéric Pauget
# Copyright (c) 2005 Mathieu Segaud
# Copyright (c) 2006 Nicolas Salles
#
# Permission to use, copy, and modify this software with or without fee
# is hereby granted, provided that this entire notice is included in
# all source code copies of any software which is or includes a copy or
# modification of this software.
#
# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRSS OR
# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
# PURPOSE.

import sys
sys.path.append('/usr/scripts/')
sys.path.append('/usr/scripts/gestion')
sys.path.append('/usr/scripts/')

import cranslib.deprecated
cranslib.deprecated.module()

import syslog
import pwd
import commands
from lock import *
from ldap_crans import crans_ldap, ann_scol, hostname
from ldap_crans import AssociationCrans, Machine, MachineWifi, BorneWifi
from affich_tools import *
from commands import getstatusoutput
from iptools import AddrInNet, NetSubnets, IpSubnet
from config import NETs, mac_komaz, mac_wifi, mac_titanic, p2p, vlans, debit_max_radin, adm_users, accueil_route, blacklist_sanctions, blacklist_sanctions_soft, periode_transitoire
import config.firewall
from ipset import IpsetError, Ipset
from ipt import gethostbyname
syslog.openlog('firewall')

debug = 1
db = crans_ldap()

class IptablesError(Exception):
    """ Gestion des erreurs d'iptables """
    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)

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)
    status,output=getstatusoutput("/sbin/iptables "+cmd)
    if status:
        raise IptablesError(cmd,status,output)
    return output
    
def iptables_restore(path):
    #~ syslog.syslog(syslog.LOG_INFO,cmd)
    cmd = "cat %s | /sbin/iptables-restore -n" % path
    status,output=getstatusoutput(cmd)
    if status:
        raise IptablesError(cmd,status,output)
    return output

def iptables_save():
    """ Sauvegarde d'ipatbles """
    status,output=getstatusoutput("mkdir -p /var/log/firewall/")
    if status:
        raise IptablesError(cmd,status,output)
    status,output=getstatusoutput("/sbin/iptables-save > /var/log/firewall/backup-firewall_`date +%Y-%m-%d_%H:%M:%S`.log" )
    if status:
        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)
    if status:
        raise TcError(cmd, status, output)
    return output

def redirect_chain(table, chain_in, chain_out, ip) :
    try:
        iptables("-t %s -N %s" % (table, chain_out))
    except IptablesError:
        iptables("-t %s -F %s" % (table, chain_out))
    # On redirige les paquets de la chaîne in dans la chaîne out
    iptables("-t %s -A %s -o ens   -s %s -j %s" % (table, chain_in, ip, chain_out))
    iptables("-t %s -A %s -o crans -d %s -j %s" % (table, chain_in, ip, chain_out))


class firewall_crans :
    """
    Classe parente pour les firewalls du crans
        Implémentée directement à partir du firewall de komaz, initialement
        écrit par Manuel Sabban et Frédéric Pauget.

        * les méthodes à surcharger pour l'implémentation eme des firewall
          sont mangle_table, nat_table, filter_table, pour la préparation
          du fw, start_fw_funcs pour la mise en place du filtrage.

        * serveurs_maj, adh_maj_list_to_do et serveurs_maj_list_to_do,
          pour la mise en place de la MAC-IP.
          en particulier, adh_maj_list_to_do et serveurs_maj_list_to_do
          sont factorisées pour la simple et bonne raison que les sources
          de la liste to_do originale ne seront pas forcément identiques
          (c'est un peu sale...)

        * la classe parente contient à peu de choses prés tout ce qu'il
          faut pour mettre en place un fw basique n'effectuant que la
          verif MAC-IP.
    """
    zone_serveur = NETs['serveurs'][0]
    vlan_adm = NETs['adm'][0]

    limit = " -m limit --limit 10/s --limit-burst 10 "
    log_template = '-m limit --limit 1/s --limit-burst 1 -j LOG --log-level notice --log-prefix '
    filtre_flood =  '-m hashlimit --hashlimit 200/second --hashlimit-mode srcip,dstip,dstport --hashlimit-name flood'

    machines = []
    debug = 1

    def exception_catcher(self,tache) :
        """ Exécute la tache founie en gérant les diverses exceptions
        pouvant survenir
        Retourne 1 en cas d'erreur et 0 sinon """
        try :
            tache()
            return 0
        except IptablesError, c :
            self.anim.reinit()
            print ERREUR
            if self.debug : print c
        except :
            self.anim.reinit()
            print ERREUR
            import traceback
            if self.debug : traceback.print_exc()
        return 1

    def __machines(self) :
        """ Liste des machines du crans """
        return db.all_machines(graphic=True)

    def __init__(self) :
        """ Pose un lock """
        make_lock('firewall')
        self.mac_ip_set = Ipset("MAC-IP","macipmap","--from 138.231.136.0 --to 138.231.151.255")
        self.mac_ip_set_wifi = Ipset("MAC-IP-WIFI","macipmap","--from 138.231.144.0 --to 138.231.151.255")
        self.mac_ip_adm_set = Ipset("MAC-IP-ADM","macipmap","--from 10.231.136.0 --to 10.231.136.255")
        try:
            self.mac_ip_set.list()
        except IpsetError:
            self.mac_ip_set.create()
        try:
            self.mac_ip_set_wifi.list()
        except IpsetError:
            self.mac_ip_set_wifi.create()
        try:
            self.mac_ip_adm_set.list()
        except IpsetError:
            self.mac_ip_adm_set.create()

    def __del__(self) :
        """ Destruction du lock """
        # Comprend pas pourquoi il faut réimporter ici -- Fred
        from lock import remove_lock
        remove_lock('firewall')
        
    def reload_qos(self):
        """Recherche la QoS"""
        return

    def mangle_table(self) :
        """ Remplit la table mangle """
        return

    def nat_table(self) :
        """ Remplit la table nat """
        return

    def filter_table(self) :
        """ Remplit la table filter """
        self.anim = anim('\tStructure de la table filter')
        print OK

    def filter_table_tweaks(self) :
        """ Complete la table filter """
        return

    def start_fw_funcs(self) :
        """ Ordonnance la construction du firewall """
        self.exception_catcher(self.test_mac_ip)

    def post_start_hook(self) :
        """ Hook de fin de demarrage """
        return

    def pre_stop_hook(self) :
        """ Hook de debut d'arret """
        return

    def restart(self):
        """ Rédémarrage du firewall """
        cprint(u'Redémarrage firewall', 'gras')
        self.start(False)

    def start(self,aff_txt_intro=True) :
        """ Construction du firewall
        aff_txt_intro s'occupe uniquement de l'esthétisme
        """
        if aff_txt_intro: cprint(u'Démarrage firewall', 'gras')
        # Préliminaires
        if not self.__machines() or self.exception_catcher(self.__stop) :
            cprint(u"Abandon", 'rouge')
            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)

        # On peux router, on a besoin d'internet pour la suite
        self.post_start_hook()

        # Remplissage
        self.start_fw_funcs()
        cprint(u"\t -> fin de la procédure de démarrage",'vert')

    def stop(self):
        """ Arrête le firewall """
        cprint(u"Arrêt du firewall", 'gras')
        self.pre_stop_hook()
        self.exception_catcher(self.__stop)
        cprint(u"\t -> fin de la procédure d'arrêt",'vert')

    def __stop(self) :
        iptables_save()
        self.anim  = anim("\tSuppression des règles")
        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):
        iptables('-t filter -F TEST_MAC-IP')
        self.mac_ip_gen()
        iptables('-t filter -A TEST_MAC-IP -m set --match-set %s src -j RETURN' % self.mac_ip_set.set)
        iptables('-t filter -A TEST_MAC-IP -m set --match-set %s src -j RETURN' % self.mac_ip_set_wifi.set)
        iptables('-t filter -A TEST_MAC-IP -m set --match-set %s src -j RETURN' % self.mac_ip_adm_set.set)
        
        # Proxy ARP de Komaz et Titanic pour OVH
        ip_ovh = db.search('host=ovh.adm.crans.org')['machineCrans'][0].ip()
        iptables('-t filter -A TEST_MAC-IP -m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, mac_komaz))
        iptables('-t filter -A TEST_MAC-IP -m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, mac_titanic))

        iptables('-t filter -A TEST_MAC-IP -j DROP')

    def __test_mac_ip(self, machine, flushed = False):
        ip = machine.ip()
        if ip.startswith("138.231.1"):
            if not flushed:
                try:
                    self.mac_ip_set.delete(ip)
                    self.mac_ip_set_wifi.delete(ip)
                except IpsetError:
                    pass
            if machine.__class__.__name__ == "MachineWifi" and hostname != 'gordon':
                # Machine Wifi, c'est la mac de gordon
                self.mac_ip_set.add("%s,%s" % (ip,mac_wifi))
            else:
                # Machine fixe
                self.mac_ip_set.add("%s,%s" % (ip,machine.mac()))
            if machine.__class__.__name__ == "MachineWifi" and hostname == 'komaz':
                self.mac_ip_set_wifi.add("%s,%s" % (ip,machine.mac()))
            elif machine.__class__.__name__ == "MachineWifi" and hostname != 'komaz':
                self.mac_ip_set_wifi.add("%s,%s" % (ip,mac_komaz))
        elif ip.startswith("10.231.136."):
            if not flushed:
                try:
                    self.mac_ip_adm_set.delete(ip)
                except IpsetError:
                    pass
            self.mac_ip_adm_set.add("%s,%s" % (ip,machine.mac()))

    def __test_mac_ip_delete(self, ip):
        if ip.startswith("138.231.1"):
            try:
                self.mac_ip_set.delete(ip)
                self.mac_ip_set_wifi.delete(ip)
            except IpsetError:
                pass
        elif ip.startswith("10.231.136."):
            try:
                self.mac_ip_adm_set.delete(ip)
            except IpsetError:
                pass


    def mac_ip_maj(self, ip_list):
        for ip in ip_list:
            machines=db.search("ip=%s" % ip)['machine']
            if not machines:
                self.__test_mac_ip_delete(ip)
            else:
                try:
                    #~ self.mac_ip_set.add("%s,%s" % (ip, machines[0].mac()))
                    self.__test_mac_ip(machines[0])
                except IpsetError:
                    pass


    def __mac_ip_gen(self,machine,rules):
        ip = machine.ip()
        if ip.startswith("138.231.1"):
            if machine.__class__.__name__ == "MachineWifi" and hostname != 'gordon':
                # Machine Wifi, c'est la mac de gordon
                rules[self.mac_ip_set].append("%s,%s" %  (ip,mac_wifi))
            else:
                # Machine fixe
                rules[self.mac_ip_set].append("%s,%s" %  (ip,machine.mac()))
            if machine.__class__.__name__ == "MachineWifi" and hostname == 'komaz':
                rules[self.mac_ip_set_wifi].append("%s,%s" % (ip,machine.mac()))
            elif machine.__class__.__name__ == "MachineWifi" and hostname != 'komaz':
                rules[self.mac_ip_set_wifi].append("%s,%s" % (ip,mac_komaz))
        elif ip.startswith("10.231.136."):
            rules[self.mac_ip_adm_set].append("%s,%s" % (ip,machine.mac()))
    def mac_ip_gen(self):
        self.anim = anim('\tChaîne TEST_MAC-IP', len(self.__machines()))
        self.anim.reinit()
        rules={
            self.mac_ip_set:[],
            self.mac_ip_set_wifi:[],
            self.mac_ip_adm_set:[],
        }
        for machine in self.__machines():
            self.anim.cycle()
            self.__mac_ip_gen(machine,rules)
        for set,rules in rules.items():
            set.restore(rules)
        self.anim.reinit()
        print OK


    def serveurs_maj(self):
        pass

    def serveurs_maj_list_to_do(self) :
        pass

    def adh_maj_list_to_do(self) :
        pass

    def port_maj(self,ip_list) :
        """ Mise à jour des ports pour les ip fournies """
        # Note : système bourrin (on efface les chaînes et on refait)
        # mais rapide et efficace (si qqn veut se casser le cul à
        # un système aussi délicat que pour la correspondance MAC-IP...)
        # -- Fred
        serveur_maj = False
        adh_maj = False
        for ip in ip_list :
            if AddrInNet(ip,self.zone_serveur) :
                serveur_maj = True
            else :
                adh_maj = True
            if serveur_maj and adh_maj :
                break
        to_do=[]
        if serveur_maj :
            self.exception_catcher(self.serveurs_maj_list_to_do)

        if adh_maj :
            self.exception_catcher(self.adh_maj_list_to_do)

    def build_chaine_adherent(self,chaine,methode) :
        # On construit d'abord les autorisations particulières
        if not self.build_chaine(chaine, methode) :
        # Puis si pas de problèmes les autorisations par défaut
            self.anim.reinit()
            for proto in [ 'tcp' , 'udp' ] :
                for port in self.ports_default["%s_%s" % ( proto, chaine) ] :
                    self.anim.cycle()
                    iptables("-I %s -p %s --dport %s -j ACCEPT" % (chaine, proto,port) )
            self.anim.reinit()
            print OK

    def build_chaine(self,chaine, methode) :
        self.anim = anim('\tChaîne %s' % chaine,len(self.__machines())+1)
        iptables("-F %s" % chaine)
        self.anim.cycle()
        def procedure() :
            for machine in self.__machines() :
                methode(machine)
                self.anim.cycle()
            iptables("-A %s -j REJECT" % chaine)
        return self.exception_catcher(procedure)


"""
Komaz
"""

class firewall_komaz(firewall_crans) :
    """
    Structure du firewall :
        table mangle :
            PREROUTING (policy par défaut : ACCEPT)
               1) proxy transparent
               2) marquage des paquets bittorrent

            POSTROUTING (policy par défaut : ACCEPT)
               1) passage dans un sous-réseau de l'ip crans :  SUBNET

            SUBNET classe pour chaque ip de son réseau dans la classe htb correspondante
        table nat :
            PREROUTING (policy par défaut : ACCEPT)
               1) passage par TEST_VIRUS_FLOOD pour tout ce qui n'est pas dans zone_serveur
               2) passage dans RESEAUX_NON_ROUTABLES_DST
               3) passage est paquets venant de l'extérieur dans RESEAUX_NON_ROUTABLES_SRC
               4) on laisse passer vers filter les paquets suivants :
                   source ou destination les serveurs de serveurs_crans
                   ce qui vient de l'extérieur
               5) passage par TEST_MAC-IP

            TEST_VIRUS_FLOOD droppe les paquets contenant des virus ou les paquets de flood
            RESEAUX_NON_ROUTABLES_DST droppe les paquets dont la destination est non routable
            RESEAUX_NON_ROUTABLES_SRC droppe les paquets dont la source est non routable
            TEST_MAC-IP envoi les bon paquets vers CRANS_VERS_EXT

        table filter :
            FORWARD (policy par défaut : ACCEPT)
               1) passage par BLACKLIST
               2) passage par FILTRE_P2P (ACCEPT sur le trafic de filtres_p2p, REJECT sur le trafic de
                    filtres_p2p_bloq, sanctions gérées par déconnexion.py)
               3) ce qui a pour source les serveurs de serveurs_crans est dirigé vers SERVEURS_VERS_EXT
               4) ce qui a pour destination les serveurs de serveurs_crans est dirigé EXT_VERS_SERVEURS
               5) tout ce qui vient de l'interface externe est dirigé vers EXT_VERS_CRANS
               6) ce qui a pour source les serveurs de serveurs_crans est dirigé vers EXT_VERS_CRANS

            BLACKLIST fitre des ip blacklistées (REJECT)
            FILTRE_P2P filtre le traffic de p2p : logging des paquets matchés par les protocoles de filtres_p2p
                                                  rejet des paquets matchés par les protocoles de filtres_p2p_bloq au dessus de la limite
            EXT_VERS_CRANS et CRANS_VERS_EXT
                ACCEPT pour les paquets vers les machines du crans (test port-ip)
                REJECT pour le reste
            EXT_VERS_SERVEURS et SERVEURS_VERS_EXT
                ACCEPT pour bon mac-ip-port
                REJECT pour le reste
            INGRESS_FILTERING : ne laisse sortir que les paquets dont l'adresse IP source appartient au crans
    """

    # interfaces physiques
    eth_ext = "ens"
    eth_int  = "crans"
    eth_wifi = "crans.3"
    eth_adm = "crans.2"

    # Ports ouverts
    ports_default = { 'tcp_EXT_VERS_CRANS' : config.firewall.ports_default['tcp']['input'],
                      'tcp_CRANS_VERS_EXT': config.firewall.ports_default['tcp']['output'],
                      'udp_EXT_VERS_CRANS' : config.firewall.ports_default['udp']['input'],
                      'udp_CRANS_VERS_EXT': config.firewall.ports_default['udp']['output'] }


    # on retire 445 et 135 en tcp car plein de mac se font deconnecter
    ports_virus = { 'tcp' :  [ ] , 'udp' : [] }

    # Filtrage du peer to peer
    # Apple et WinMX desactives car possibilite de fausse detection par ipp2p
    filtres_p2p = [ #('apple', 'AppleJuice'),
                    ('soul', 'SoulSeek'),
                    #('winmx', 'WinMX'),
                    ('edk', 'eDonkey'),
                    ('dc', 'DirectConnect'),
                    ('kazaa', 'KaZaa'),
                    ('ares', 'Ares'),
                    ('bit', 'Bittorrent'),
                    ('gnu', 'GNUtella') ]

    filtres_p2p_bloq = [
            ]
            

    ports_p2p = [ '412', '1214', '4662:4665' , '6346:6347', '6699', '6881:6889' ]

    liste_reseaux_non_routables = config.firewall.reseaux_non_routables

    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))
        iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d 10.231.136.0/24 -j RETURN")
        iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -d 10.231.136.0/24 -j RETURN")
        iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d %s -j RETURN" % NETs['personnel-ens'][0])
        iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -s %s -j RETURN" % NETs['personnel-ens'][0])
        for reseau in self.liste_reseaux_non_routables :
            iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d %s -j DROP" % reseau)
            iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -s %s -j DROP" % reseau)
            self.anim.cycle()
        self.anim.reinit()
        print OK


    def reload_qos(self):
        self.mangle_table()
        self.qos()
        
    def mangle_table(self):

        self.anim = anim('\tStructure de la table mangle')

        # On vide complètement la table
        iptables("-t mangle -F")
        iptables("-t mangle -X")
        
        #Log de paquets
        iptables('-t mangle -A PREROUTING  -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % self.eth_int)
        iptables('-t mangle -A PREROUTING  -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % self.eth_wifi)
        iptables('-t mangle -A PREROUTING  -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % self.eth_ext)

        # Proxy transparent
        iptables("-t mangle -A PREROUTING -s %s -j RETURN" % self.zone_serveur)
        #~ iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 "
                 #~ "-s %s -d ! %s -j MARK --set-mark %s" %
                 #~ (NETs['fil'][0], NETs['wifi'][0], config.firewall.mark['proxy']))
        #~ iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" %
                 #~ config.firewall.mark['proxy'])
        iptables("-t mangle -N BLACKLIST_SOFT")
        for ip_fil in NETs['fil']:
            iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 "
                 "-s %s -d ! %s -j BLACKLIST_SOFT" %
                 (ip_fil, NETs['wifi'][0]))
        iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 "
                 "-s %s -d ! %s -j BLACKLIST_SOFT" %
                 (NETs['wifi'][0], '138.231.136.0/21'))
        iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % config.firewall.mark['proxy'])
        
        
        #connection de secours
        iptables("-t mangle -A PREROUTING -p tcp -s 138.231.136.0/16 ! -d 138.231.136.0/16 --destination-port 80 -m condition --condition secours -j MARK --set-mark %s" % (config.firewall.mark['secours']))
        iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % config.firewall.mark['secours'])

        # Parametres pour iptables/tc
        mark = config.firewall.mark['bittorrent']
        debit_max = config.firewall.debit_max
        debit_max_semi=debit_max/2
        eth_ext = self.eth_ext
        eth_int = self.eth_int
        eth_wifi = self.eth_wifi

        # Classification du traffic : extérieur <-> ftp
        iptables("-t mangle -A POSTROUTING -o %(eth_int)s -d 138.231.136.98 "
                 "-j CLASSIFY --set-class 1:9997" % locals())
        iptables("-t mangle -A POSTROUTING -o %(eth_ext)s -s 138.231.136.98 "
                 "-j CLASSIFY --set-class 1:9997" % locals())

        # On marque les paquets bittorrent uniquement
        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 %s" % mark)
        #iptables("-t mangle -A PREROUTING -p tcp -m mark --mark %s "
        #         "-j CONNMARK --save-mark" % mark)

        warn = ''
        
        # pas de QoS pour la zone ens
        iptables("-t mangle -A POSTROUTING -d 138.231.0.0/16 -s 138.231.0.0/16 -j RETURN")
        # Par défaut, on envoit les paquets dans la classe 9998
        for net in NETs['all']:
            iptables("-t mangle -A POSTROUTING -o %(eth_int)s -d %(net)s "
                     "-j CLASSIFY --set-class 1:9998" % locals())
            iptables("-t mangle -A POSTROUTING -o %(eth_wifi)s -d %(net)s "
                     "-j CLASSIFY --set-class 1:9998" % locals())
            
            iptables("-t mangle -A POSTROUTING -o %(eth_ext)s -s %(net)s "
                     "-j CLASSIFY --set-class 1:9998" % locals())
                     
        for net in NETs['personnel-ens']:
            # pas de limitation en download
            #iptables("-t mangle -A POSTROUTING -d %(net)s "
            #        "-j CLASSIFY --set-class 1:9998" % locals())
            iptables("-t mangle -A POSTROUTING -s %(net)s "
                     "-j CLASSIFY --set-class 1:9998" % locals())

        # On crée les chaînes de sous-réseaux
        for net in NETs['all']:
            for mask in config.firewall.mask:
                for subnet in NetSubnets(net, mask):
                    index = config.firewall.mask.index(mask)
                    if index == 0:
                        prev_chain = "POSTROUTING"
                    else:
                        ip = subnet.split('/')[0]
                        prev_subnet = IpSubnet(ip, config.firewall.mask[index-1])
                        prev_chain = "SUBNET-%s" % prev_subnet
                    next_chain = "SUBNET-%s" % subnet
                    redirect_chain('mangle', prev_chain, next_chain, subnet)
        print OK


        self.anim = anim('\tLimitation du debit')
        adherents = db.search('paiement=ok')['adherent']
        self.adherents=adherents
        debit_adh = int(debit_max / float(len(adherents)))

        # Création des classes et qdisc
        for interface in [eth_ext, eth_int, eth_wifi]:
            # 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 %(interface)s root handle 1: htb r2q 1" % locals())
            tc("class add dev %(interface)s parent 1: classid 1:1 "
               "htb rate %(debit_max)skbps ceil %(debit_max)skbps" % locals())
            tc("class add dev %(interface)s parent 1:1 classid 1:9999 "
               "htb rate %(debit_adh)skbps ceil %(debit_adh)skbps" % locals())
            tc("qdisc add dev %(interface)s parent 1:9999 "
               "handle 9999: sfq perturb 10" % locals())
            debit_ftp = 1000 # kbps
            tc("class add dev %(interface)s parent 1:1 classid 1:9997 "
               "htb rate %(debit_ftp)skbps ceil %(debit_max_semi)skbps prio 1" % locals())
            tc("qdisc add dev %(interface)s parent 1:9997 "
               "handle 9997: sfq perturb 10" % locals())
               
            tc("class add dev %(interface)s parent 1:1 classid 1:9998 "
                   "htb rate %(debit_max_semi)skbps ceil %(debit_max)skbps prio 1" % locals())
            tc("qdisc add dev %(interface)s parent 1:9998 "
                   "handle 9998: sfq perturb 10" % locals())
  
        for interface in ["crans.21"]:
            # 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 %(interface)s root handle 1: htb r2q 1" % locals())
            tc("class add dev %(interface)s parent 1: classid 1:1 "
               "htb rate 128kbps ceil 128kbps" % locals())
            tc("class add dev %(interface)s parent 1:1 classid 1:9998 "
                   "htb rate 128kbps ceil 128kbps prio 1" % locals())
            tc("qdisc add dev %(interface)s parent 1:9998 "
                   "handle 9998: sfq perturb 10" % locals())
        print OK
                   
    def qos(self):
        
        if len(self.adherents) == 0 :
            self.mangle_table()
        
        # On desactive la QoS lorsque le debit augmente
        if not config.firewall.debit_jour:
            return

        # Parametres pour iptables/tc
        mark = config.firewall.mark['bittorrent']
        debit_max = config.firewall.debit_max
        debit_max_semi=debit_max/2
        eth_ext = self.eth_ext
        eth_int = self.eth_int
        eth_wifi = self.eth_wifi
        
        adherents =  self.adherents
        debit_adh = int(debit_max / float(len(adherents)))


        self.anim = anim('\tGénération des classes de QoS', len(adherents))
        # On construit ensuite les classes et qdisc pour chaque adhérent
        for adherent in adherents:
            self.anim.cycle()
            # On ne peut pas reprendre le numéro 1
            class_id = int(adherent.id()) + 1
            # Il nous faut un n° inférieur à 9999 unique
            qdisc_id = class_id
            for interface in [self.eth_ext, self.eth_int, self.eth_wifi]:
                tc("class add dev %(interface)s parent 1:1 classid 1:%(class_id)d "
                   "htb rate %(debit_adh)skbps ceil %(debit_max)skbps prio 0" % locals())
                tc("qdisc add dev %(interface)s parent 1:%(class_id)d "
                   "handle %(qdisc_id)d: sfq perturb 10" % locals())

            # Classification des adhérents dans leur classe respective
            for machine in adherent.machines():
                ip = machine.ip()
                if not AddrInNet(ip, NETs['all']):
                    # Cas particulier d'une machine ayant une IP non CRANS
                    continue
                subnet = IpSubnet(machine.ip(), config.firewall.mask[-1])
                iptables("-t mangle -A SUBNET-%(subnet)s -o crans -d %(ip)s "
                         "-j CLASSIFY --set-class 1:%(class_id)s" % locals())
                iptables("-t mangle -A SUBNET-%(subnet)s -o ens   -s %(ip)s "
                         "-j CLASSIFY --set-class 1:%(class_id)s" % locals())

        # +-----------------+
        # | QOS pour le ftp |
        # +-----------------+

        # On ne veut pas que les gens à l'éxtérieur bouffe toute la
        # bande passante.

        # Classification des paquets à destination du ftp
        iptables("-t mangle -A POSTROUTING -o %(eth_int)s -p tcp -d 138.231.136.98 --dport 21 "
                 "-j CLASSIFY --set-class 1:9997" % locals())

        self.anim.reinit()
        print OK

    def nat_table(self) :
        self.anim = anim('\tStructure de la table nat')

        iptables("-t nat -P PREROUTING ACCEPT")
        iptables("-t nat -P OUTPUT ACCEPT")
        iptables("-t nat -A PREROUTING -p tcp -d 138.231.136.2 --dport 22 -j DNAT --to-destination 138.231.136.1:22")    # redirection du ssh vers zamok
        iptables("-t nat -A PREROUTING -p tcp -d 138.231.136.2 --dport 443 -j DNAT --to-destination 138.231.136.1:22")   # redirection du ssh vers zamok (pour passer dans un proxy, avec corkscrew)
        iptables("-t nat -A PREROUTING -i %s -j ACCEPT" % self.eth_ext )
        iptables("-t nat -A PREROUTING -s %s -j ACCEPT" % self.zone_serveur )
        iptables("-t nat -A PREROUTING -d %s -j ACCEPT" % self.zone_serveur )
        # iptables("-t nat -A PREROUTING -i %s -p tcp --dport 80 -s ! %s -j DNAT --to-destination 138.231.136.3:81" % (self.eth_int, self.zone_serveur) )
        # iptables("-t nat -A POSTROUTING -o %s -p tcp --dport 81 -s 138.231.136.0/21 -d 138.231.136.3 -j SNAT --to-source 138.231.136.4" % self.eth_int )

        # Proxy transparent pour deconnexion soft
        iptables("-t nat -A PREROUTING -p tcp -m mark --mark %s " % config.firewall.mark['proxy'] +
                 "-j DNAT --to-destination 10.231.136.4:3128")
                 
        # Appartement ENS
        iptables("-t nat -A POSTROUTING -o ens -s %s -j SNAT --to 138.231.136.44" % NETs['personnel-ens'][0])
        iptables("-t nat -A POSTROUTING -o crans -s %s -j SNAT --to 138.231.136.44" % NETs['personnel-ens'][0])
                 
        #Connection de secours
        iptables("-t nat -A PREROUTING -p tcp -m mark --mark %s " % config.firewall.mark['secours'] +
                 "-j DNAT --to-destination 10.231.136.4:3129")
        print OK

    def filter_table(self) :
        self.anim = anim('\tStructure de la table filter')
        for chaine in [ 'TEST_MAC-IP', 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST' ] :
            iptables('-t filter -N %s' % chaine)

        
        iptables("-A FORWARD -i lo -j ACCEPT")
        iptables("-A FORWARD -p icmp -j ACCEPT")
        iptables("-A FORWARD -i tun-ovh -j ACCEPT")
        iptables("-A FORWARD -d 224.0.0.0/4 -j DROP")
        # Proxy transparent, pour les deconnexion soft
        iptables("-A FORWARD -m mark --mark %s -j ACCEPT" % config.firewall.mark['proxy'])
        #iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
        for net in  NETs['fil'] + NETs['wifi'] + NETs['serveurs']:
            iptables("-A FORWARD -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_int))
        for net in NETs['wifi']:
             iptables("-A FORWARD -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_wifi))
        for net in  NETs['adm']:
            iptables("-A FORWARD -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))

        iptables("-A FORWARD -j RESEAUX_NON_ROUTABLES_DST")
        iptables("-A FORWARD -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.eth_ext )
        
        #Connection de secours
        # on ne peut pas faire passer https dans un proxy transparent sans faire de man in the middle et sans recompiler squid
        iptables("-A FORWARD -p tcp -s 138.231.136.0/16 ! -d 138.231.136.0/16 --destination-port 443 -m condition --condition secours -j REJECT") 
        iptables("-A FORWARD -m mark --mark %s -j ACCEPT" % config.firewall.mark['secours'])

        iptables("-P FORWARD ACCEPT")

        for net in  NETs['fil'] + NETs['wifi'] + NETs['serveurs']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_int))
        for net in  NETs['wifi']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_wifi))
        for net in  NETs['adm']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
        iptables("-P INPUT ACCEPT")

        print OK

    def filter_table_tweaks(self) :
        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', 'INGRESS_FILTERING',
                'TEST_VIRUS_FLOOD', 'LOG_VIRUS', 'LOG_FLOOD','LOG_TRACKER','TRACKER_FILTER' ] :
            iptables('-N %s' % chaine)
        iptables("-A FORWARD -i ens -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu")
        iptables("-A FORWARD -i crans -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu")
        iptables("-A FORWARD -i crans.3 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu")
        iptables("-A FORWARD -i %s -j BLACKLIST_DST" % self.eth_ext )
        iptables("-A FORWARD -o %s -j BLACKLIST_SRC" % self.eth_ext )
        #iptables("-A FORWARD -o %s -s ! %s -d ! %s -j FILTRE_P2P" % (self.eth_ext,self.zone_serveur, self.zone_serveur) )

        # Appartement ENS
        iptables("-A FORWARD -i crans.21 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu")
        iptables("-A FORWARD -s %s -j ACCEPT" % NETs['personnel-ens'][0])
        iptables("-A FORWARD -d %s -j ACCEPT" % NETs['personnel-ens'][0])

        iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
        iptables("-A FORWARD -j INGRESS_FILTERING")
        # on ne route pas les paquets n'appartenant pas à notre plage ip -- xhub
        for net in NETs['all']:
            iptables("-A INGRESS_FILTERING -o ens -s %s -j RETURN" % net)
        iptables("-A INGRESS_FILTERING -o ens -j LOG --log-prefix BAD_ROUTE ")
        iptables("-A INGRESS_FILTERING -o ens -j DROP")
        # protection contre un certain type de spoof
        # cf http://travaux.ovh.net/?do=details&id=5183 -- xhub
        for net_d in NETs['all']:
            for net in NETs['all']:
                iptables("-A INGRESS_FILTERING -i ens -s ! %s -d %s -j RETURN" % (net,net_d))
        iptables("-A INGRESS_FILTERING -i ens -j LOG --log-prefix BAD_SRC ")
        iptables("-A INGRESS_FILTERING -i ens -j DROP")
        iptables("-A FORWARD -i %s -d %s -j ADMIN_VLAN" % (self.eth_int, self.vlan_adm) )
        iptables("-A FORWARD -i %s -d %s -j ADMIN_VLAN" % (self.eth_wifi, self.vlan_adm) )
        iptables("-A FORWARD -i %s -d %s -j REJECT" % (self.eth_ext, self.vlan_adm) )
        iptables("-A FORWARD -s ! %s -o %s -j TEST_VIRUS_FLOOD" % (self.zone_serveur,self.eth_ext))
        iptables("-A FORWARD -i %s -d %s -j EXT_VERS_SERVEURS" % (self.eth_ext, self.zone_serveur) )
        iptables("-A FORWARD -o %s -s %s -j SERVEURS_VERS_EXT" % (self.eth_ext, self.zone_serveur) )
        iptables("-A FORWARD -i %s -j EXT_VERS_CRANS" % self.eth_ext )
        iptables("-A FORWARD -o %s -j CRANS_VERS_EXT" % self.eth_ext )

        
        print OK

    def classes_p2p_maj(self, ip_list):
        """ 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

        print OK

        ## Traitement
        # MAJ des règles de classification de l'IP
        def procedure():
            self.anim = anim('\tMise à jour des classes p2p')

            # Liste des classes candidates a la suppression
            scheduled_del = []
            warn = ''

            # Parametres pour iptables/tc
            mark = config.firewall.mark['bittorrent']
            debit_max = config.firewall.debit_max
            debit_adh = int(config.firewall.debit_max / 1200.) # XXX: guesstimate 
            eth_ext = self.eth_ext
            eth_int = self.eth_int
            eth_wifi = self.eth_wifi

            try:
                for ip in ip_list:
                    recherche = db.search('ip=%s&paiement=ok' % ip)
                    # Si l'ip n'appartient pas à un adhérent,
                    # on ne cherche pas plus loin
                    if not recherche['adherent']:
                        continue
                    machines = recherche['machine']
                    if not machines:
                        # Il faut supprimer cette entrée
                        iptables_option = '-D'
                        subnet = IpSubnet(ip, config.firewall.mask[-1])
                        all_regles = iptables("-t mangle -L SUBNET-%(subnet)s -n" % locals()).split('\n')
                        regles = [line for line in all_regles if ip in line]
                        # On sélectionne la première qui doit contenir ce que l'on veut
                        regle = regles[0].split()
                        class_id = int(regle[7].split(':')[1])
                        # On marque la classe comme candidate a la suppression
                        scheduled_del.append(class_id)
                    elif len(machines) == 1:
                        # Il faut ajouter cette entrée
                        iptables_option = '-A'
                        machine = machines[0]
                        adherent = machine.proprietaire()
                        ip = machine.ip()
                        subnet = IpSubnet(ip, config.firewall.mask[-1])
                        # On ne peut pas reprendre le numéro 1
                        class_id = int(adherent.id()) + 1
                        # On cree la classe et la qdisc s'il elles n'existent pas deja
                        qdisc_id = class_id
                        try:
                            for interface in [eth_ext, eth_int, eth_wifi]:
                                tc("class add dev %(interface)s "
                                   "parent 1:1 classid 1:%(class_id)d htb "
                                   "rate %(debit_adh)s ceil %(debit_max)s" % locals())
                                tc("qdisc add dev %(interface)s "
                                   "parent 1:%(class_id)d handle %(qdisc_id)d: "
                                   "sfq perturb 10" % locals())
                        except TcError, e:
                            if "File exists" in e.output:
                                # La classe existe deja
                                pass
                            else:
                                raise e
                    else:
                        warn += "Plusieurs machines avec l'IP %s\n" % ip
                    # Il nous faut un n° inférieur à 9999 unique
                    iptables("-t mangle %(iptables_option)s SUBNET-%(subnet)s "
                             "-o %(eth_int)s -d %(ip)s "
                             "-j CLASSIFY --set-class 1:%(class_id)s" % locals())
                    iptables("-t mangle %(iptables_option)s SUBNET-%(subnet)s "
                             "-o %(eth_wifi)s -d %(ip)s "
                             "-j CLASSIFY --set-class 1:%(class_id)s" % locals())
                    iptables("-t mangle %(iptables_option)s SUBNET-%(subnet)s "
                             "-o %(eth_ext)s -s %(ip)s "
                             "-j CLASSIFY --set-class 1:%(class_id)s" % locals())
            except IptablesError, c:
                warn += str(c) + '\n'

            for class_id in scheduled_del:
                # TODO: supprimer les classes qui sont vraiment vides
                pass

            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 du routage")
        warn = ''
        for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' ,
            'echo 65536 > /proc/sys/net/ipv4/netfilter/ip_conntrack_max' ,
            'modprobe ip_conntrack_ftp' ,
            'modprobe ip_conntrack_irc' ] :
            status,output=getstatusoutput(cmd)
            if status :
                warn += output + '\n'
        if warn :
            print WARNING
            if self.debug :
                print warn
        else :
            print OK

    def pre_stop_hook(self) :
        self.anim = anim("\tArrêt du routage")
        status,output=getstatusoutput('echo 0 > /proc/sys/net/ipv4/ip_forward')
        if status :
            print ERREUR
        else :
            print OK

    def start_fw_funcs(self) :
        self.exception_catcher(self.log_chaines)
        self.exception_catcher(self.test_virus_flood)
        self.exception_catcher(self.reseaux_non_routables)
        self.exception_catcher(self.blacklist)
        self.exception_catcher(self.admin_vlan)
        self.exception_catcher(self.serveurs_vers_ext)
        self.exception_catcher(self.ext_vers_serveurs)
        self.exception_catcher(self.crans_vers_ext)
        self.exception_catcher(self.ext_vers_crans)
        self.exception_catcher(self.test_mac_ip)
        #self.exception_catcher(self.filtre_p2p)
        self.exception_catcher(self.qos)

    def serveurs_maj_list_to_do(self) :
        self.exception_catcher(self.serveurs_vers_ext)
        self.exception_catcher(self.ext_vers_serveurs)

    def adh_maj_list_to_do(self) :
        self.exception_catcher(self.crans_vers_ext)
        self.exception_catcher(self.ext_vers_crans)

    def log_chaines(self) :
        """ Construction des chaînes de log (LOG_VIRUS et LOG_FLOOD) """
        self.anim = anim('\tCréation des chaînes de log')
        for filtre in [ 'VIRUS', 'FLOOD'] :
            # Vidage de la chaîne
            iptables('-F LOG_%s' % filtre)
            iptables('-A LOG_%s %s %s:' % (filtre, self.log_template, filtre.capitalize()) )
            iptables('-A LOG_%s -j DROP' % filtre )
            self.anim.cycle()
        self.anim.reinit()
        print OK

    def test_virus_flood(self) :
        """ Construction de la chaîne TEST_VIRUS """
        iptables('-F TEST_VIRUS_FLOOD')
        self.anim = anim('\tFiltrage virus et floods')

        ip_vers = ('65.19.154.90', '208.72.168.52', '66.109.25.121', '193.37.152.88', '89.149.202.101')
        for ip in ip_vers:
            iptables('-A TEST_VIRUS_FLOOD -s %s -j LOG_VIRUS' % ip)
            iptables('-A TEST_VIRUS_FLOOD -d %s -j LOG_VIRUS' % ip)

        for proto, ports in self.ports_virus.items() :
            for port in ports :
                iptables('-A TEST_VIRUS_FLOOD -p %s --dport %s -j LOG_VIRUS' % (proto, port) )
                self.anim.cycle()

        iptables('-A TEST_VIRUS_FLOOD %s -j RETURN' % self.filtre_flood) # Les limites en négatif ca ne marche pas.
        self.anim.cycle()
        iptables('-A TEST_VIRUS_FLOOD -j LOG_FLOOD')
        self.anim.reinit()
        print OK

    def serveurs_vers_ext(self) :
        """ Reconstruit la chaîne SERVEURS_VERS_EXT """
        if not self.build_chaine('SERVEURS_VERS_EXT', self.__serveurs_vers_ext) :
            self.anim.reinit()
            print OK

    def ext_vers_serveurs(self) :
        """ Reconstruit la chaîne EXT_VERS_SERVEURS """
        if not self.build_chaine('EXT_VERS_SERVEURS', self.__ext_vers_serveurs) :
            self.anim.reinit()
            print OK
        # Attention les règles sont à l'envers. Hint '-I'
        iptables("-I EXT_VERS_SERVEURS -p tcp --dport ssh -m state --state NEW\
        -m recent --name SSH --update --seconds 30 --hitcount 10 --rttl -j DROP")
        iptables("-I EXT_VERS_SERVEURS -p tcp --dport ssh -m state --state NEW\
                -m recent --name SSH --set")

    def crans_vers_ext(self) :
        """ Reconstruit la chaîne CRANS_VERS_EXT """
        self.build_chaine_adherent('CRANS_VERS_EXT',self.__crans_vers_ext)
        # Protocole GRE pour les VPN
        iptables("-I CRANS_VERS_EXT -p gre -j ACCEPT")

    def ext_vers_crans(self) :
        """ Reconstruit la chaîne EXT_VERS_CRANS """
        self.build_chaine_adherent('EXT_VERS_CRANS',self.__ext_vers_crans)
        # Attention les règles sont à l'envers. Hint '-I'
        iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW -j ACCEPT")
        iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW\
        -m recent --name SSH --update --seconds 30 --hitcount 10 --rttl -j DROP")
        iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW\
                -m recent --name SSH --set")

    def admin_vlan(self) :
        """ Reconstruit la chaîne ADMIN_VLAN """
        iptables("-F ADMIN_VLAN")
        #nounou_machines = []
        #for adherent in db.search('droits=Nounou')['adherent'] :
        #    for machine in adherent.machines() :
        #        nounou_machines.append(machine.ip())
        iptables("-A ADMIN_VLAN -j REJECT")
        #self.anim = anim('\tChaîne ADMIN_VLAN', len(nounou_machines))
        #for machine in nounou_machines :
        #    self.anim.cycle()
        #    iptables("-I ADMIN_VLAN -p tcp -s %s --dport ssh -j ACCEPT" % machine)
        #    iptables("-I ADMIN_VLAN -p tcp -s %s --dport https -j ACCEPT" % machine)
        #self.anim.reinit()
        #print OK

    def __serveurs_vers_ext(self,machine):
        ip=machine.ip()
        if not AddrInNet(ip,self.zone_serveur):
            # C'est une machine adhérent, rien à faire ici
            return

        mac = machine.mac()
        ports = { 'tcp' : machine.portTCPout(),
                  'udp' : machine.portUDPout() }

        for proto in [ 'tcp', 'udp' ] :
            for port in ports[proto]:
                iptables("-I SERVEURS_VERS_EXT -s %s -p %s --dport %s -m mac --mac-source %s -j ACCEPT" \
                %(ip,proto,port,mac))

    def __ext_vers_serveurs(self,machine):
        ip=machine.ip()
        if not AddrInNet(ip,self.zone_serveur):
            # C'est une machine adhérent, rien à faire ici
            return

        ports = { 'tcp' : machine.portTCPin(),
                  'udp' : machine.portUDPin() }

        for proto in [ 'tcp', 'udp' ] :
            for port in ports[proto]:
                iptables("-I EXT_VERS_SERVEURS -d %s -p %s --dport %s -j ACCEPT"\
                %(ip,proto,port))

    def __crans_vers_ext(self,machine):
        ip=machine.ip()
        if AddrInNet(ip,self.zone_serveur):
            # C'est un serveur, rien à faire ici
            return

        ports = { 'tcp' : machine.portTCPout(),
                  'udp' : machine.portUDPout() }

        for proto in [ 'tcp', 'udp' ] :
            for port in ports[proto]:
                iptables("-I CRANS_VERS_EXT -s %s -p %s --dport %s -j ACCEPT" \
                %(ip,proto,port))

    def __ext_vers_crans(self,machine):
        ip=machine.ip()
        if AddrInNet(ip,self.zone_serveur):
            # C'est un serveur, rien à faire ici
            return

        ports = { 'tcp' : machine.portTCPin(),
                  'udp' : machine.portUDPin() }

        for proto in [ 'tcp', 'udp' ] :
            for port in ports[proto]:
                iptables("-I EXT_VERS_CRANS -d %s -p %s --dport %s -j ACCEPT" \
                %(ip,proto,port))

    def blacklist(self):
        """ Construit les chaînes de blackliste (BLACKLIST_{DST,SRC}) """

        blacklist = []
        blacklist_soft = []
        rules=['*filter',':BLACKLIST_DST - [0:0]',':BLACKLIST_SRC - [0:0]']
            

        # Recherche sur le champ paiement seulement (clubs compris) et plus ablacklist pour capturer aussi les deconnection pour chambre invalide et carte étudiant
        search = db.search('paiement=%s' % ann_scol)
        if periode_transitoire:
            tmp=db.search('paiement=%s' % ann_scol-1)
            search['adherent'].entend(tmp['adherent'])
            search['club'].entend(tmp['club'])
            del tmp
        self.anim = anim("\tBlackliste adhérents+clubs", 2*len(search['adherent']+search['club']))
        for entite in search['adherent'] + search['club']:
            self.anim.cycle()
            sanctions = entite.blacklist_actif()
            for s in blacklist_sanctions:
                if s in sanctions:
                    blacklist.extend(entite.machines())
                    break
        for entite in search['adherent'] + search['club']:
            self.anim.cycle()
            sanctions = entite.blacklist_actif()
            for s in blacklist_sanctions_soft:
                if s in sanctions:
                    blacklist_soft.extend(entite.machines())
                    break
        self.anim.reinit()
        print OK

        # Recherche sur le champ mblacklist
        search = db.search('mblacklist=*&paiement=%s' % ann_scol)
        if periode_transitoire:
            search['machine'].entend(db.search('mblacklist=*&paiement=%s' % ann_scol-1)['machine'])
        self.anim = anim("\tBlackliste machines", 2*len(search['machine']))
        for entite in search['machine']:
            self.anim.cycle()
            sanctions = entite.blacklist_actif()
            for s in blacklist_sanctions:
                if s in sanctions:
                    blacklist.append(entite)
                    break
        for entite in search['machine']:
            self.anim.cycle()
            sanctions = entite.blacklist_actif()
            for s in blacklist_sanctions_soft:
                if s in sanctions:
                    blacklist_soft.append(entite)
                    break
        self.anim.reinit()
        print OK

        self.anim = anim("\tChaînes BLACKLIST", len(blacklist))
        for machine in blacklist:
            self.anim.cycle()
            rules.append("-A BLACKLIST_DST -d %s -j REJECT" % machine.ip())
            rules.append("-A BLACKLIST_SRC -s %s -j REJECT" % machine.ip())
        self.anim.reinit()
        print OK
        rules.append('COMMIT')
        rules.append('*mangle')
        rules.append(':BLACKLIST_SOFT - [0:0]')
        self.anim = anim("\tMarquage des machines pour blacklist soft", len(blacklist_soft))
        for machine in blacklist_soft:
            self.anim.cycle()
            #~ iptables("-t mangle -A BLACKLIST_SOFT -s %s -j MARK --set-mark %s" % (machine.ip(),  config.firewall.mark['proxy']))
            rules.append("-A BLACKLIST_SOFT -s %s -j MARK --set-mark %s" % (machine.ip(),  config.firewall.mark['proxy']))
        self.anim.reinit()
        rules.append('COMMIT\n')
        f = open('/tmp/ipt_blacklist', 'w')
        f.write("\n".join(rules))
        f.close()
        iptables('-F BLACKLIST_DST')
        iptables('-F BLACKLIST_SRC')
        iptables('-t mangle -F BLACKLIST_SOFT')
        iptables_restore('/tmp/ipt_blacklist')
        print OK

    def filtre_p2p(self):
        """ Construit la chaînes de filtrage du p2p (FILTRE_P2P) """
        self.anim = anim("\tFiltrage p2p")
        iptables('-F FILTRE_P2P')
        iptables('-F TRACKER_FILTER')

        # On ne filtre que ce qui passe sur l'interface externe
        iptables('-A FILTRE_P2P -i %s -o %s -j RETURN' % (self.eth_int, self.eth_int) )


        for filtre in self.filtres_p2p :
            iptables('-A FILTRE_P2P -m ipp2p --%s -j LOG --log-prefix "IPP2P=%s "' % (filtre[0],
                                                                                      filtre[1]))
            iptables('-A FILTRE_P2P -m ipp2p --%s -j REJECT --reject-with icmp-admin-prohibited' % filtre[0])
            #iptables('-A FILTRE_P2P -m ipp2p --%s -j RETURN' % filtre[0])
            self.anim.cycle()

        for filtre in self.filtres_p2p_bloq:
            iptables('-A FILTRE_P2P -m ipp2p --%s -j LOG --log-prefix "IPP2P=%s "' % (filtre[0],
                                                                                      filtre[1]))
            iptables('-A FILTRE_P2P -m ipp2p -m limit --%s --limit=%d/hour -j RETURN' % (filtre[0], p2p.limite[filtre[1]]/2))
            iptables('-A FILTRE_P2P -m ipp2p --%s -j REJECT --reject-with icmp-admin-prohibited' % filtre[0])
            self.anim.cycle()
        #on rejetes les trackeur udp les plus connus
        for tracker in p2p.udp_torrent_tracker:
            for dest in gethostbyname(tracker)[0]:
                iptables('-A FILTRE_P2P -p udp -d %s -j LOG --log-level notice --log-prefix "TRACKER:%s "' % (dest,(tracker[:20]) if len(tracker) > 20 else tracker))
                iptables('-A FILTRE_P2P -p udp -d %s -j REJECT --reject-with icmp-admin-prohibited' % dest)
                 
        #On log les requetes a des trackers torrents puis on les rejetes
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"info_hash=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER  -m string --algo kmp --string \"/scrape?\" -j LOG_TRACKER")
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"peer_id=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"port=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"uploaded=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"downloaded=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER  -m string --algo kmp ! --string \"left=\" -j ACCEPT")
        iptables("-A TRACKER_FILTER -j  LOG_TRACKER")
        
        #On analyse que les requetes http
        iptables("-A FILTRE_P2P -p tcp -m string --algo kmp --string \"GET \" -j TRACKER_FILTER")
        iptables("-A FILTRE_P2P -p tcp -m string --algo kmp --string \"get \" -j TRACKER_FILTER")
        
        # fait bcp de faux positif, peux servir a detecter de nouveau trackers
        iptables("-A FILTRE_P2P -p udp -m string --from 0 --to 65 --algo kmp --hex-string \"|4500002c00004000|\" -j LOG --log-level notice --log-prefix \"TRACKER_TORRENT: \"")
        
        iptables('-A LOG_TRACKER -j LOG --log-level notice --log-prefix "TRACKER_TORRENT: "')
        iptables('-A LOG_TRACKER -j REJECT --reject-with icmp-admin-prohibited')
        self.anim.reinit()
        print OK

    def serveurs_maj(self) :
        self.exception_catcher(self.serveurs_vers_ext)

"""
Zamok
"""

class firewall_zamok(firewall_crans) :
    """
    Structure du firewall :
        table nat :
            SERV_OUT_ADM
            TEST_MAC-IP
        table filter :
            FORWARD (policy par défaut : DROP)
               rien ne passe pas la chaîne FORWARD
            INPUT (policy par défaut : ACCEPT)

    """

    # interfaces physiques
    eth_pub = "crans"
    eth_adm = "crans.2"
    nfs_ports = ["111", "2049", "32765:32769"]

    def serv_out_adm(self) :
        self.anim = anim('\tOutput vers VLAN adm', len(adm_users))
        # Supression des éventuelles règles
        iptables("-t filter -F SERV_OUT_ADM")

        for user in adm_users :
            self.anim.cycle()
            try:
                iptables("-A SERV_OUT_ADM -m owner --uid-owner %d -j ACCEPT" % pwd.getpwnam(user)[2])
            except KeyError:
                continue

        # LDAP et DNS toujours joignable
        iptables("-A SERV_OUT_ADM -p tcp --dport ldap -j ACCEPT")
        iptables("-A SERV_OUT_ADM -p tcp --dport domain -j ACCEPT")
        iptables("-A SERV_OUT_ADM -p udp --dport domain -j ACCEPT")

        # Pour le nfs (le paquet à laisser passer n'a pas d'owner)
        iptables("-A SERV_OUT_ADM -d fx.adm.crans.org -j ACCEPT")
        iptables("-A SERV_OUT_ADM -d nfs.adm.crans.org -j ACCEPT")

        # Rien d'autre ne passe
        iptables("-A SERV_OUT_ADM -j REJECT --reject-with icmp-net-prohibited")

        self.anim.reinit()
        print OK

    def nat_table(self) :
        self.anim = anim('\tStructure de la table nat')
       

        iptables("-t nat -P PREROUTING ACCEPT")
        iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
        
        iptables("-t nat -P PREROUTING ACCEPT")
        print OK
        
    def filter_table(self):
        self.anim = anim('\tStructure de la table filter')
        try:
            iptables('-t filter -N SERV_OUT_ADM')
            iptables('-t filter -N TEST_MAC-IP')
        except:
            pass
        iptables("-t filter -A OUTPUT -d 224.0.0.0/4 -j DROP")

        # <!> à placer dans filter
        #for net in NETs['fil'] + NETs['adm'] + NETs['wifi'] :
        #    iptables("-t nat -A PREROUTING -s %s -j TEST_MAC-IP" % net)
        iptables("-t filter -A OUTPUT -d 10.231.136.1 -j SERV_OUT_ADM") # moche mais visiblement ont avait acces a l'interface adm de zamok depuis zamok
        iptables("-t filter -A OUTPUT -o lo -j ACCEPT")

        # pour une connection entrante venant du VLAN adm, il faut que le ACK
        # puisse passer (interaction avec pam, uid de l'utilisateur en OUTPUT)
        # on accepte donc les paquets si la connection est
        # en RELATED et ESTABLISHED
        iptables("-t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT")
        iptables("-t filter -A OUTPUT -o %s -j SERV_OUT_ADM" % self.eth_adm)

        
        iptables("-t filter -P OUTPUT ACCEPT")
        print OK

    def start_fw_funcs(self) :
        self.exception_catcher(self.test_mac_ip)
        self.serv_out_adm()

    def blacklist(self):
        """Fondamentalement, bloque l'accès internet sur zamok aux
        adhérents sanctionnés"""
        iptables("-F OUTPUT")

        # Règles OUTPUT de nat_table() à remettre en place
        iptables("-t filter -A OUTPUT -o lo -j ACCEPT")
        iptables("-t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT")
        iptables("-t filter -A OUTPUT -o %s -j SERV_OUT_ADM" % self.eth_adm)

        self.filter_table()

        # Recherche des adherent blacklisté (club compris)
        search = db.search('paiement=%s' % ann_scol)
        if periode_transitoire:
            tmp=db.search('paiement=%s' % ann_scol-1)
            search['adherent'].entend(tmp['adherent'])
            search['club'].entend(tmp['club'])
            del tmp
        self.anim = anim("\tBlackliste des comptes Crans", len(search['adherent']))
        for adh in search['adherent']:
            self.anim.cycle()
            sanctions = adh.blacklist_actif()
            for s in blacklist_sanctions:
                if s in sanctions:
                    try:
                        uid = adh.uidNumber()
                        iptables("-A OUTPUT -m owner --uid-owner %s -d 127.0.0.1/8 -j ACCEPT" % uid)
                        iptables("-A OUTPUT -m owner --uid-owner %s -d 138.231.136.1/21 -j ACCEPT" % uid)
                        iptables("-A OUTPUT -m owner --uid-owner %s -d 138.231.144.1/21 -j ACCEPT" % uid)
                        iptables("-A OUTPUT -m owner --uid-owner %s -j REJECT" % uid)
                    finally:
                        break
        self.anim.reinit()
        print OK

    def filter_table_tweaks(self) :
        self.anim = anim('\tRègles spécifiques à zamok')
        iptables("-P INPUT ACCEPT")
        iptables("-P FORWARD DROP")
        print OK

"""
Rouge
"""

class firewall_redisdead(firewall_crans) :
    """
    Structure du firewall :
        table filter :
            TEST_MAC-IP pour les paquets en INPUT
            FORWARD (policy par défaut : DROP)
               rien ne passe pas la chaîne FORWARD
            INPUT (policy par défaut : ACCEPT)

    """

    # interfaces physiques
    eth_pub = "eth0"
    eth_adm = "eth1"

    def filter_table(self) :
        self.anim = anim('\tStructure de la table filter')
        iptables('-N TEST_MAC-IP')
        for net in NETs['fil'] + NETs['wifi'] + NETs['serveurs']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_pub))
        for net in NETs['adm']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
        iptables("-P INPUT ACCEPT")
        iptables("-P OUTPUT ACCEPT")
        print OK

    def filter_table_tweaks(self) :
        self.anim = anim('\tRègles spécifiques à redisdead')
        iptables("-P INPUT ACCEPT")
        iptables("-P FORWARD DROP")
        print OK


class firewall_sable(firewall_redisdead):
    """Comme pour rouge, avec le proxy transparent en plus"""

    def filter_table_tweaks(self) :
        iptables("-P INPUT ACCEPT")
        iptables("-P FORWARD DROP")

    def mangle_table(self):
        iptables("-t mangle -F PREROUTING")

        # Pour le    transparent
        iptables("-t mangle -i eth0.2 -A PREROUTING -p tcp --destination-port 3128 " +
                 "--destination 10.231.136.9 " +
                 "-m mac --mac-source %s " % mac_komaz +
                 "-j MARK --set-mark %s" % config.firewall.mark['proxy'])
        iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % config.firewall.mark['proxy'])


    def nat_table(self):
        firewall_redisdead.nat_table(self)
        # Proxy transparent pour le filiaire
        iptables("-t nat -A PREROUTING -i eth0.2 -m mark --mark %s -j ACCEPT" % config.firewall.mark['proxy'])
        if_defaut = "eth0"
        if_radin = "eth0.%d" % vlans["radin"]
        if_accueil = "eth0.%d" % vlans["accueil"]
        if_isolement = "eth0.%d" % vlans["isolement"]

         # Proxy transparent pour le wifi
        iptables("-t nat -A PREROUTING -i %s -p tcp --dport 80" %if_defaut +
                 " -d ! 138.231.136.0/24 -j DNAT --to-destination" +
                 " 138.231.136.9:3128")

        # Proxy transparent pour les vlans radin et accueil
        for interface in [if_accueil, if_isolement]:
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 80 -j DNAT --to-destination 10.51.0.1:3128" % interface)
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 3128 -j ACCEPT" % interface)
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 443 -j ACCEPT" % interface)

    def post_start_hook(self) :
        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/netfilter/ip_conntrack_max' ] :
            status,output=getstatusoutput(cmd)
            if status :
                warn += output + '\n'
        if warn :
            print WARNING
            if self.debug :
                print warn
        else :
            print OK

firewall_bleu = firewall_zamok

"""
Gordon
"""

class firewall_gordon(firewall_crans) :
    """
    Structure du firewall :
        table nat :
        table filter :
            MAC-IP
            FORWARD (policy par défaut : ACCEPT)
            INPUT (policy par défaut : ACCEPT)
        table mangle :
            POSTROUTING : le proxy transparent

    """

    # interfaces physiques
    eth_crans = "eth0"
    eth_wifi = "eth0.3"

    def filter_table(self) :
        self.anim = anim('\tStructure de la table filter')
        iptables('-t filter -N TEST_MAC-IP')

        iptables("-t filter -P FORWARD ACCEPT")
        iptables("-t filter -A FORWARD -i lo -j ACCEPT")
        iptables("-t filter -A FORWARD -d 224.0.0.0/4 -j DROP")

        for net in NETs['fil'] + NETs['adm'] + NETs['wifi'] + NETs['serveurs']:
            iptables("-t filter -A FORWARD -s %s -j TEST_MAC-IP" % net)

        iptables("-t filter -P FORWARD ACCEPT")
        iptables("-t filter -P OUTPUT ACCEPT")
        print OK

    def filter_table_tweaks(self) :
        self.anim = anim('\tRègles spécifiques à gordon')
        iptables("-P INPUT ACCEPT")
        iptables("-P FORWARD ACCEPT")
        print OK

    def mangle_table(self):
        self.anim = anim('\tRègles spécifiques à gordon')
        #~ iptables("-t mangle -A PREROUTING " +
                 #~ "-d ! 138.231.136.0/21 " +
                 #~ ("-i %s " % self.eth_wifi) +
                 #~ "-p tcp -m tcp --dport 80 " +
                 #~ "-j MARK --set-xmark %s/0xffffffff" % config.firewall.mark['proxy'])
        print OK

    def post_start_hook(self) :
        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/netfilter/ip_conntrack_max' ] :
            status,output=getstatusoutput(cmd)
            if status :
                warn += output + '\n'
        if warn :
            print WARNING
            if self.debug :
                print warn
        else :
            print OK


"""
Routeur
"""

class firewall_routeur(firewall_crans):
    
        # interfaces physiques
    eth_pub = "eth0"
    eth_adm = "eth1"
    eth_accueil = "eth2"
    eth_isolement = "eth3"
    eth_appart = "eth4"

    def filter_table(self) :
        self.anim = anim('\tStructure de la table filter')
        iptables('-N TEST_MAC-IP')
        for net in NETs['fil'] + NETs['wifi'] + NETs['serveurs']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_pub))
        for net in NETs['adm']:
            iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
        iptables("-P INPUT ACCEPT")
        iptables("-P OUTPUT ACCEPT")
        print OK

    def filter_table_tweaks(self) :
        iptables("-P INPUT ACCEPT")
        iptables("-P FORWARD DROP")
        for ip in accueil_route.keys():
            for type in accueil_route[ip].keys():
                if type in ['udp', 'tcp']:
                    for port in accueil_route[ip][type]:
                        iptables("-A FORWARD -p %s -d %s --dport %s -j ACCEPT" % (type,ip,port))
                        iptables("-A FORWARD -p %s -s %s --sport %s -j ACCEPT" % (type,ip,port))

    def mangle_table(self):
        iptables("-t mangle -F PREROUTING")


    def nat_table(self):
        #intranet et wiki pour le vlan accueil
        for ip in accueil_route.keys():
            for type in accueil_route[ip].keys():
                if type in ['udp', 'tcp']:
                    for port in accueil_route[ip][type]:
                        iptables("-t nat -A PREROUTING -i %s -p %s -d %s --dport %s -j ACCEPT" % (self.eth_accueil ,type,ip,port))
                        iptables("-t nat -A PREROUTING -i %s -p %s -d %s --dport %s -j ACCEPT" % (self.eth_isolement ,type,ip,port))
                        iptables("-t nat -A PREROUTING -i %s -p %s -d %s --dport %s -j ACCEPT" % (self.eth_appart ,type,ip,port))
                        iptables("-t nat -A POSTROUTING -p %s -s %s -d %s --dport %s -j MASQUERADE" % (type,NETs['accueil'][0],ip,port))
                        iptables("-t nat -A POSTROUTING -p %s -s %s -d %s --dport %s -j MASQUERADE" % (type,NETs['isolement'][0],ip,port))
                        iptables("-t nat -A POSTROUTING -p %s -s %s -d %s --dport %s -j MASQUERADE" % (type,NETs['personnel-ens'][0],ip,port))

        # Proxy transparent pour les vlans isolement et accueil
        i=1
        for interface in [self.eth_accueil, self.eth_isolement]:
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 80 -j DNAT --to-destination 10.5%s.0.10" % (interface,i))
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 80 -j ACCEPT" % interface)
            iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 443 -j ACCEPT" % interface)
            i+=1

        # dns menteur pour accueil
        iptables("-t nat -A  PREROUTING -p udp -s %s --dport 53 -j DNAT --to 10.51.0.10" % NETs['accueil'][0])
        iptables("-t nat -A  PREROUTING -p tcp -s %s --dport 53 -j DNAT --to 10.51.0.10" % NETs['accueil'][0])
    def post_start_hook(self) :
        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/netfilter/ip_conntrack_max' ] :
            status,output=getstatusoutput(cmd)
            if status :
                warn += output + '\n'
        if warn :
            print WARNING
            if self.debug :
                print warn
        else :
            print OK


if __name__ == '__main__' :
    # 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', 'mangle_table', 'classes_p2p_maj','reload_qos' ] :
            chaines.append(nom)

    def __usage(txt=None) :
        if txt!=None : cprint(txt,'gras')

        print """Usage:
   %(p)s start   : Construction du firewall.
   %(p)s restart : Reconstruction du firewall.
   %(p)s stop    : Arrêt du firewall.
   %(p)s <noms de chaînes> : reconstruit les chaînes
Les chaînes pouvant être reconstruites sont :
   %(chaines)s
Pour reconfiguration d'IPs particulières, utiliser generate. """ % \
{ 'p' : sys.argv[0].split('/')[-1] , 'chaines' : '\n   '.join(chaines) }
        sys.exit(-1)

    # Bons arguments ?
    if len(sys.argv) == 1 :
        __usage()
    for arg in sys.argv[1:] :
        if arg in [ 'stop', 'restart', 'start' ] and len(sys.argv) != 2 :
            __usage("L'argument %s ne peut être employé que seul." % arg)

        if arg not in [ 'stop', 'restart', 'start' ] + chaines :
            __usage("L'argument %s est inconnu." % arg)

    for arg in sys.argv[1:] :
        eval('fw.%s()' % arg )
