/* 

                          Firewall Builder

                 Copyright (C) 2002 NetCitadel, LLC

  Author:  Vadim Kurland     vadim@vk.crocodile.org

  $Id$

  This program is free software which we release under the GNU General Public
  License. You may redistribute and/or modify this program under the terms
  of that license as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  To get a copy of the GNU General Public License, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#ifndef __POLICYCOMPILER_CISCO_HH
#define __POLICYCOMPILER_CISCO_HH

#include <fwbuilder/libfwbuilder-config.h>

#include "fwcompiler/PolicyCompiler.h"
#include "fwbuilder/RuleElement.h"

#include "Helper.h"
#include "ACL.h"

namespace libfwbuilder {
    class IPService;
    class ICMPService;
    class TCPService;
    class UDPService;
    class RuleElementSrc;
    class RuleElementDst;
    class RuleElementSrv;
    class Group;
};

namespace fwcompiler {

    class PolicyCompiler_cisco : public PolicyCompiler {

protected:

	/**
	 * our firewall policy must block everything by default even
	 * if there are no rules. In iptables we do this by setting
	 * default chain policies to DROP. Here we do this by adding
	 * this unconditional blocking rule in the end. See also comment
	 * in the code regarding "pass_all_out" option
	 */
	void addDefaultPolicyRule();

        /**
         * prints rule in some universal format (close to that visible
         * to user in the GUI). Used for debugging purposes. This method
         * calls PolicyCompiler::_internalPrintPolicyRule and then adds
         * fields specific to PIX rules at the end of the printout
         */
        virtual std::string debugPrintRule(libfwbuilder::Rule *rule);

	/**
	 * drops dynamic interface from the rule in the following
	 * cases because its address is unknown and we can not build
	 * ACL rule for it.
         *
         *  assuming interface 'INT' is dynamic
         *
         *   src.      rule bound to interface     direction     decision
         * -----------------------------------------------------------------
         *   INT       INT                        outbound      keep
         *   INT       any other                  outbound      remove
         *   INT       INT                        inbound       remove
         *   INT       any other                  inbound       remove
         *            
         *   dest.     rule bound to interface     direction     decision
         * -------------------------------------------------------------------
         *   INT       INT                        inbound       keep
         *   INT       any other                  inbound       remove
         *   INT       INT                        outbound      remove
         *   INT       any other                  outbound      remove
         *
         *
	 */
        friend class specialCaseWithDynInterface;
        class specialCaseWithDynInterface : public PolicyRuleProcessor
        {
            bool dropDynamicInterface(libfwbuilder::PolicyRule *rule,
                                      libfwbuilder::PolicyRule::Direction dir,
                                      libfwbuilder::RuleElement *re);
            public:
            specialCaseWithDynInterface(const std::string &name) : PolicyRuleProcessor(name) {}
            virtual bool processNext();
        };

	/**
         * only for rules with interface element 'all' and direction
         * 'both' or 'inbound'; for interfaces with zones that match
         * src: create rule with that interface, direction
         * inbound. 'Any' in src matches all interfaces.
         *
         * Set flag (boolean) 'interface_and_direction_set_from_src'
         * so other rule processors in the same batch can skip the
         * rule.
	 */
	DECLARE_POLICY_RULE_PROCESSOR( setInterfaceAndDirectionBySrc );

	/**
         * only for rules with interface element 'all'and direction
         * 'both' or 'outbound'; for interfaces with zones that match
         * dst: create rule with that interface, direction
         * outbound. 'Any' in dst matches all interfaces.
         *
         * Set flag (boolean) 'interface_and_direction_set_from_dst'
         * so other rule processors in the same batch can skip the
         * rule.
	 */
	DECLARE_POLICY_RULE_PROCESSOR( setInterfaceAndDirectionByDst );

        /**
         * for rules with interface element not 'all' and direction
         * 'both': create two rules with the same interface and
         * directions Inbound and Outbound
         *
         * for rules with interface element not 'all' and direction
         * 'inbound' or 'outbound': setInterfaceId to this interface
         *
         * Skip rule if flag 'interface_and_direction_set_from_src' or
         * 'interface_and_direction_set_from_dst' is set
         *
         * Set flag (boolean) 'interface_and_direction_set'
         * so other rule processors in the same batch can skip the
         * rule.
         */
	DECLARE_POLICY_RULE_PROCESSOR( setInterfaceAndDirectionIfInterfaceSet );

	/**
	 * determine acl rules should belong to
	 */
        class pickACL : public PolicyRuleProcessor
        {
            bool using_named_acl;
            public:
            pickACL(bool use_named_acl,const std::string &name) :
            PolicyRuleProcessor(name) {using_named_acl = use_named_acl;}
            virtual bool processNext();
        };
        friend class PolicyCompiler_cisco::pickACL;


        
	/**
	 * split rule if Src==any 
	 * 
	 * This is special case since we assume that "any" includes
	 * also a firewall object. Packets headed to or from the
	 * firewall must be inspected by INPUT or OUTPUT chain, while
	 * packets crossing the firewall are inspected by FORWARD
	 * chain. If we assume that "any" also includes firewall
	 * itself, then we need to generate code for both FORWARD and
	 * INPUT/OUTPUT chains from the same rule. This processor
	 * splits the rule onto two and sets chain and direction in
	 * the second copy appropriately. It preserve original src and
	 * dst in both copies, it only changes chain and direction in
	 * the second copy.
	 */
        DECLARE_POLICY_RULE_PROCESSOR(splitIfSrcAny);

	/**
	 * split rule if Dst==any. See comment in splitIfSrcAny
	 */
        DECLARE_POLICY_RULE_PROCESSOR(splitIfDstAny);

        /**
         * TODO: move this processor to class PolicyCompiler. The same
         * processor is used in ipt and in pf (although in pf there it
         * is present in two copies that have different names
         * splitIfFirewallInSrc and splitIfFirewallInDst). Move also
         * splitIfSrcMatchesFw and splitIfDstMatchesFw
         */
        friend class splitIfRuleElementMatchesFW;
        class splitIfRuleElementMatchesFW : public PolicyRuleProcessor
        {
            std::string re_type;
            public:
            splitIfRuleElementMatchesFW(const std::string &n,
                                                  std::string _type) :
                PolicyRuleProcessor(n) { re_type=_type; }
            virtual bool processNext();
        };

        friend class splitIfSrcMatchesFw;
        class splitIfSrcMatchesFw : public splitIfRuleElementMatchesFW
        {
            public:
            splitIfSrcMatchesFw (const std::string &n) :
                splitIfRuleElementMatchesFW(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        class splitIfDstMatchesFw : public splitIfRuleElementMatchesFW
        {
            public:
            splitIfDstMatchesFw (const std::string &n) :
                splitIfRuleElementMatchesFW(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };
        friend class PolicyCompiler_cisco::splitIfDstMatchesFw;


        /**
         * find redundant objects in rule element and eliminate
         * them. This only works for SRC and DST since all objects are
         * assumed to be addresses. Redundant object is such that has
         * narrower address range than some other object in the same
         * rule element.
         */
        friend class removeRedundantAddresses;
        class removeRedundantAddresses : public PolicyRuleProcessor
        {
            std::string re_type;
            public:
            removeRedundantAddresses(const std::string &n,std::string _type) :
                PolicyRuleProcessor(n) { re_type=_type; }
            virtual bool processNext();
        };

        friend class removeRedundantAddressesFromSrc;
        class removeRedundantAddressesFromSrc : public removeRedundantAddresses
        {
            public:
            removeRedundantAddressesFromSrc (const std::string &n) :
                removeRedundantAddresses(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        friend class removeRedundantAddressesFromDst;
        class removeRedundantAddressesFromDst : public removeRedundantAddresses
        {
            public:
            removeRedundantAddressesFromDst (const std::string &n) :
                removeRedundantAddresses(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };



       /**
         * this processor splits rules if it finds rule that controls
         * access for tcp service with port number "port" to the firewall
         */
        class tcpServiceToFW : public PolicyRuleProcessor
        {
            int port;
            public:
            tcpServiceToFW(int p,const std::string &name) :
                PolicyRuleProcessor(name) {port=p;}
            virtual bool processNext();
        };
        friend  class PolicyCompiler_cisco::tcpServiceToFW;

        /**
         * this processor splits rules if it finds telnet to firewall
         */
        class telnetToFirewall : public tcpServiceToFW
        {
            public:
            telnetToFirewall(const std::string &n):tcpServiceToFW(23,n) {}
        };
        friend  class telnetToFirewall;

        /**
         * this processor splits rules if it finds ssh to firewall
         */
        class sshToFirewall : public tcpServiceToFW
        {
            public:
            sshToFirewall(const std::string &n):tcpServiceToFW(22,n) {}
        };
        friend  class sshToFirewall;

        /**
         * replace fw with one of its interfaces in SRC in interface
         * policy rule
         */
        DECLARE_POLICY_RULE_PROCESSOR( replaceFWinSRCInterfacePolicy );

        /**
         * replace fw with one of its interfaces in DST in interface
         * policy rule
         */
        DECLARE_POLICY_RULE_PROCESSOR( replaceFWinDSTInterfacePolicy );

        /**
         * replace fw with one of its interfaces in DST in global
         * policy rule
         */
        DECLARE_POLICY_RULE_PROCESSOR( replaceFWinDSTPolicy );


        /**
         * this processor splits rules if objects in rule element
         * re_type belong to different network zones
         */
        class splitByNetworkZonesForRE : public PolicyRuleProcessor
        {
            std::string re_type;
            std::map<int,libfwbuilder::PolicyRule*> rules;
            void AddToInterface(int interface_id,
                                libfwbuilder::Address *addr,
                                libfwbuilder::PolicyRule *rule);
            public:
            splitByNetworkZonesForRE(const std::string &name,const std::string &_type) :
                PolicyRuleProcessor(name) {re_type=_type; }
            virtual bool processNext();
        };


        class splitByNetworkZonesForSrc : public splitByNetworkZonesForRE
        {
            public:
            splitByNetworkZonesForSrc(const std::string &n):
                splitByNetworkZonesForRE(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        class splitByNetworkZonesForDst : public splitByNetworkZonesForRE
        {
            public:
            splitByNetworkZonesForDst(const std::string &n):
                splitByNetworkZonesForRE(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };


	/**
	 * this processor deals with negation in all fields by
	 * splitting the rule and using temporary action
	 * "CONTINUE". Rules must be filtered through NegationPhase2
	 * later
	 */
	DECLARE_POLICY_RULE_PROCESSOR(  NegationPhase1 );

        /**
         *  eliminates duplicate objects in rule element
         *  're_type'. Uses special comparison function class to
         *  detect equivalent ICMP objects
         */
        class equalObjCISCO : public PolicyCompiler::equalObj
        {
            public:
            virtual bool operator()(libfwbuilder::FWObject *o);
        };

        class eliminateDuplicatesInRE_cisco : public PolicyCompiler::eliminateDuplicatesInRE
        {
            public:
            eliminateDuplicatesInRE_cisco(const std::string &n,const std::string &re_type) :
                eliminateDuplicatesInRE(n,re_type) { comparator=new equalObjCISCO(); }
        };

        /**
         *  eliminates duplicate objects in SRC.
         */
        class eliminateDuplicatesInSRC : public eliminateDuplicatesInRE_cisco
        {
            public:
            eliminateDuplicatesInSRC(const std::string &n) :
                eliminateDuplicatesInRE_cisco(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        /**
         *  eliminates duplicate objects in DST.
         */
        class eliminateDuplicatesInDST : public eliminateDuplicatesInRE_cisco
        {
            public:
            eliminateDuplicatesInDST(const std::string &n) :
                eliminateDuplicatesInRE_cisco(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };

        /**
         *  eliminates duplicate objects in SRV
         */
        class eliminateDuplicatesInSRV : public eliminateDuplicatesInRE_cisco
        {
            public:
            eliminateDuplicatesInSRV(const std::string &n) :
                eliminateDuplicatesInRE_cisco(n,libfwbuilder::RuleElementSrv::TYPENAME) {}
        };

        /**
         * Placeholders for MultiAddressRunTime objects which are not
         * supported for Cisco devices (IOS and PIX, at least)
         */
        class processMultiAddressObjectsInRE : public PolicyRuleProcessor
        {
            std::string re_type;
            public:
            processMultiAddressObjectsInRE(const std::string &name,
                      const std::string &t) : PolicyRuleProcessor(name) { re_type=t; }
            virtual bool processNext();
        };


        class processMultiAddressObjectsInSrc : public processMultiAddressObjectsInRE
        {
            public:
            processMultiAddressObjectsInSrc(const std::string &n) :
                processMultiAddressObjectsInRE(n,libfwbuilder::RuleElementSrc::TYPENAME) {}
        };

        class processMultiAddressObjectsInDst : public processMultiAddressObjectsInRE
        {
            public:
            processMultiAddressObjectsInDst(const std::string &n) :
                processMultiAddressObjectsInRE(n,libfwbuilder::RuleElementDst::TYPENAME) {}
        };


        
protected:

        Helper                             helper;
	std::map<std::string,ciscoACL*>      acls;

	virtual std::string myPlatformName();

        std::string mangleInterfaceName(const std::string &interface_name);

public:

	PolicyCompiler_cisco(libfwbuilder::FWObjectDatabase *_db,
                             libfwbuilder::Firewall *fw,
                             bool ipv6_policy,
                             fwcompiler::OSConfigurator *_oscnf);
        virtual ~PolicyCompiler_cisco() {}

        virtual std::string createRuleLabel(const std::string &txt,
                                            libfwbuilder::Interface *iface,
                                            int rule_num);

	virtual int  prolog();
	virtual void compile();
	virtual void epilog();

        /**
         * sort commands ('icmp', 'telnet', 'ssh') and access lists
         * in some kind of 'natural' order. Useful for both IOS and PIX
         */
        void regroup();
	
    };


}

#endif
