1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-03-21 10:47:16 +01:00
fwbuilder/src/cisco_lib/CompilerDriver_pix_run.cpp
Vadim Kurland f6e1886d4f * PolicyCompiler_PrintRuleIptRst.cpp (PrintRuleIptRst::_printRuleLabel):
really fixed #869 "compile rule" should also print the comment.
Printing rule comment in the compiler output in the single rule
compile mode when firewall object is configured to use
iptables-restore. Code that prints rule label and comment has been
unified for compilers for all firewall platforms.
2010-01-13 21:21:07 +00:00

851 lines
32 KiB
C++

/*
Firewall Builder
Copyright (C) 2009 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
*/
#include "../../config.h"
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>
#include <stdexcept>
#include <memory>
#include <assert.h>
#include <cstring>
#include <iomanip>
#include "CompilerDriver_pix.h"
#include "PolicyCompiler_pix.h"
#include "NATCompiler_pix.h"
#include "RoutingCompiler_pix.h"
#include "OSConfigurator_pix_os.h"
#include "Helper.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/XMLTools.h"
#include "fwbuilder/FWException.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/NAT.h"
#include "fwbuilder/Routing.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/IPv6.h"
#include "fwcompiler/Preprocessor.h"
#include "fwbuilder/Cluster.h"
#include "fwbuilder/ClusterGroup.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/StateSyncClusterGroup.h"
#include "fwbuilder/FailoverClusterGroup.h"
#include <QStringList>
#include <QFileInfo>
#include <QFile>
#include <QTextStream>
#include <QtDebug>
using namespace std;
using namespace libfwbuilder;
using namespace fwcompiler;
class sort_by_net_zone {
string any_address_id;
public:
explicit sort_by_net_zone()
{
any_address_id = FWObjectDatabase::getStringId(
FWObjectDatabase::ANY_ADDRESS_ID);
}
bool operator()(const FWObject *a, const FWObject *b)
{
if (Interface::constcast(a) && Interface::constcast(b))
{
string netzone_a=a->getStr("network_zone");
string netzone_b=b->getStr("network_zone");
if ( netzone_a==any_address_id) return false;
if ( netzone_b==any_address_id) return true;
}
return false;
}
};
QString CompilerDriver_pix::assembleManifest(Cluster *cluster, Firewall* fw, bool cluster_member)
{
QString script_buffer;
QTextStream script(&script_buffer, QIODevice::WriteOnly);
QString ofname = determineOutputFileName(cluster, fw, cluster_member, ".fw");
script << "!" << MANIFEST_MARKER << "* " << ofname << endl;
return script_buffer;
}
QString CompilerDriver_pix::printActivationCommands(Firewall*)
{
return "";
}
QString CompilerDriver_pix::assembleFwScript(Cluster *cluster,
Firewall* fw,
bool cluster_member,
OSConfigurator *oscnf)
{
Configlet script_skeleton(fw, "cisco", "script_skeleton");
Configlet top_comment(fw, "cisco", "top_comment");
FWOptions* options = fw->getOptionsObject();
options->setStr("prolog_script", options->getStr("pix_prolog_script"));
options->setStr("epilog_script", options->getStr("pix_epilog_script"));
string vers = fw->getStr("version");
string platform = fw->getStr("platform");
bool outbound_acl_supported = Resources::platform_res[platform]->getResourceBool(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+
"/pix_outbound_acl_supported");
bool afpa = options->getBool("pix_assume_fw_part_of_any");
bool emulate_outb_acls = options->getBool("pix_emulate_out_acl");
bool generate_outb_acls = options->getBool("pix_generate_out_acl");
top_comment.setVariable("outbound_acl_supported", QString((outbound_acl_supported)?"supported":"not supported"));
top_comment.setVariable("emulate_outb_acls", QString((emulate_outb_acls)?"yes":"no"));
top_comment.setVariable("generate_outb_acls", QString((generate_outb_acls)?"yes":"no"));
top_comment.setVariable("afpa", QString((afpa)?"yes":"no"));
script_skeleton.setVariable("short_script", options->getBool("short_script"));
script_skeleton.setVariable("not_short_script", ! options->getBool("short_script"));
script_skeleton.setVariable("system_configuration_script", system_configuration_script.c_str());
script_skeleton.setVariable("policy_script", policy_script.c_str());
script_skeleton.setVariable("nat_script", nat_script.c_str());
script_skeleton.setVariable("routing_script", routing_script.c_str());
assembleFwScriptInternal(cluster, fw, cluster_member, oscnf, &script_skeleton, &top_comment, "!");
return script_skeleton.expand();
}
string CompilerDriver_pix::run(const std::string &cluster_id,
const std::string &firewall_id,
const std::string &single_rule_id)
{
Cluster *cluster = NULL;
if (!cluster_id.empty())
cluster = Cluster::cast(objdb->findInIndex(objdb->getIntId(cluster_id)));
Firewall *fw = Firewall::cast(objdb->findInIndex(objdb->getIntId(firewall_id)));
assert(fw);
// Copy rules from the cluster object
populateClusterElements(cluster, fw);
if (cluster)
{
// PIX failover is dfferent from VRRP and other failover protocols
// in that it does not create new virtual address. Instead, each
// unit is configured with two ip addresses, one for the active
// unit and another for standby one. When active unit fails, the
// other one assumes its address.
//
// This matters because when we use cluster object or one of its
// interfaces in rules, compiler should expand it to the set of
// addresses that includes addresses of the corresponding
// interface of both member firewalls. Method
// CompilerDriver::copyFailoverInterface adds a copy of firewall
// interface to the cluster object. This works for all firewalls,
// but for PIX we need to add copies of interfaces from both
// members.
//
FWObjectTypedChildIterator cl_iface = cluster->findByType(Interface::TYPENAME);
for (; cl_iface != cl_iface.end(); ++cl_iface)
{
FailoverClusterGroup *failover_group =
FailoverClusterGroup::cast(
(*cl_iface)->getFirstByType(FailoverClusterGroup::TYPENAME));
if (failover_group)
{
FWObject *this_member_interface = NULL;
list<FWObject*> other_member_interfaces;
for (FWObjectTypedChildIterator it =
failover_group->findByType(FWObjectReference::TYPENAME);
it != it.end(); ++it)
{
FWObject *intf = FWObjectReference::getObject(*it);
assert(intf);
if (intf->isChildOf(fw)) this_member_interface = intf;
else other_member_interfaces.push_back(intf);
}
if (!other_member_interfaces.empty())
{
for (list<FWObject*>::iterator it=other_member_interfaces.begin();
it!=other_member_interfaces.end(); ++it)
{
cluster->addCopyOf(*it, true);
}
}
}
}
}
#if 0
FWObjectTypedChildIterator iface = fw->findByType(Interface::TYPENAME);
for (; iface != iface.end(); ++iface)
{
(*iface)->dump(true, true);
}
#endif
QString ofname = determineOutputFileName(cluster, fw, !cluster_id.empty(), ".fw");
FWOptions* options = fw->getOptionsObject();
QString script_buffer;
try
{
commonChecks2(cluster, fw);
pixClusterConfigurationChecks(cluster, fw);
// Note that fwobjectname may be different from the name of the
// firewall fw This happens when we compile a member of a cluster
current_firewall_name = fw->getName().c_str();
bool pix_acl_basic = options->getBool("pix_acl_basic");
bool pix_acl_no_clear = options->getBool("pix_acl_no_clear");
bool pix_acl_substitution = options->getBool("pix_acl_substitution");
bool pix_add_clear_statements = options->getBool("pix_add_clear_statements");
if (!pix_acl_basic && !pix_acl_no_clear && !pix_acl_substitution)
{
if ( pix_add_clear_statements ) options->setBool("pix_acl_basic",true);
else options->setBool("pix_acl_no_clear",true);
}
Helper helper(NULL);
multimap<string, FWObject*> netzone_objects;
std::list<FWObject*> all_interfaces = fw->getByTypeDeep(Interface::TYPENAME);
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
{
Interface *iface = dynamic_cast<Interface*>(*i);
assert(iface);
if (iface->getOptionsObject()->getBool("cluster_interface")) continue;
if ((iface->getOptionsObject()->getStr("type") == "" ||
iface->getOptionsObject()->getStr("type") == "ethernet") &&
iface->getByType(Interface::TYPENAME).size() > 0)
{
// Parent vlan interface (i.e. trunk)
if (!iface->isUnprotected())
{
QString err(
"Interface %1 has vlan subinterfaces, it can not "
"be used for ACL. Marking this interface \"unprotected\" "
"to exclude it."
);
warning(fw, NULL, NULL,
err.arg(iface->getName().c_str())
.toStdString());
iface->setUnprotected(true);
}
}
// Tests for label, security level and network zone make sense
// only for interfaces that can be used in ACLs or to bind
// ACLs to. Unnumbered interfaces can't, so we do not need to
// run these checks. One example of unnumbered interface is
// parent interface for vlan subinterfaces.
if (iface->isUnnumbered()) continue;
if (iface->isUnprotected()) continue;
/*
* there shouldn't be two interfaces with the same security level and same label
*
*/
for (std::list<FWObject*>::iterator j=all_interfaces.begin(); j!=all_interfaces.end(); ++j)
{
Interface *iface2 = dynamic_cast<Interface*>(*j);
assert(iface2);
if (iface2->isUnnumbered()) continue;
if (iface2->isUnprotected()) continue;
if (iface->getId()==iface2->getId()) continue;
if (iface->getOptionsObject()->getBool("cluster_interface") ||
iface2->getOptionsObject()->getBool("cluster_interface"))
continue;
if (iface->getSecurityLevel()==iface2->getSecurityLevel())
{
QString err(
"Security level of each interface should be unique, "
"however interfaces %1 (%2) and %3 (%4)"
" have the same security level."
);
abort(fw, NULL, NULL,
err.arg(iface->getName().c_str())
.arg(iface->getLabel().c_str())
.arg(iface2->getName().c_str())
.arg(iface2->getLabel().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
if (iface->getLabel()==iface2->getLabel())
{
QString err(
"Label of each interface should be unique, "
"however interfaces %1 (%2) and %3 (%4)"
" have the same."
);
abort(fw, NULL, NULL,
err.arg(iface->getName().c_str())
.arg(iface->getLabel().c_str())
.arg(iface2->getName().c_str())
.arg(iface2->getLabel().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
}
// We only do limited checks for dedicated failover
// interfaces because they are not used in ACLs or
// anywhere else in configuration, except in "failover"
// commands.
if (iface->isDedicatedFailover()) continue;
/*
* in PIX, we need network zones to be defined for all interfaces
*/
string netzone_id = iface->getStr("network_zone");
if (netzone_id=="")
{
QString err("Network zone definition is missing for interface %1 (%2)");
abort(fw, NULL, NULL,
err.arg(iface->getName().c_str())
.arg(iface->getLabel().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
FWObject *netzone = objdb->findInIndex(
FWObjectDatabase::getIntId(netzone_id));
if (netzone==NULL)
{
QString err("Network zone points at nonexisting object for interface %1 (%2)");
abort(fw, NULL, NULL,
err.arg(iface->getName().c_str())
.arg(iface->getLabel().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
/*
* netzone may be a group, in which case we need to expand it
* (recursively).
*
* 1. We create new temporary object (type Group).
*
* 2. put it in the database somewhere
*
* 3. add all objects that belong to the network zone to this
* group. We add objects directly, not as a reference.
*
* 4. finally replace reference to the old network zone object in the
* interface with reference to this new group.
*
* 5. we store ID of the original network zone object
* using iface->setStr("orig_netzone_id")
*
* This ensures netzones do not contain other groups and do not
* require any recursive expanding anymore. Since objects were added
* to netzones directly, we do not need to bother with dereferencing,
* too.
*/
list<FWObject*> ol;
helper.expand_group_recursive(netzone,ol);
FWObject *nz = objdb->createObjectGroup();
assert(nz!=NULL);
nz->setName("netzone_"+iface->getLabel());
objdb->add(nz);
for (list<FWObject*>::iterator j=ol.begin(); j!=ol.end(); ++j)
{
netzone_objects.insert( pair<string,FWObject*>(iface->getLabel(),*j));
nz->add(*j);
}
iface->setStr("orig_netzone_id", netzone_id );
iface->setStr("network_zone",
FWObjectDatabase::getStringId(nz->getId()) );
}
/*
* the same object (network or host) can not belong to network zones
* of two different interfaces. Map netzone_objects holds pairs
* interface_id/object. We just make sure the same object does not
* appear in two pairs with different interfaces.
*/
multimap<string,FWObject*>::iterator k;
for (k=netzone_objects.begin(); k!=netzone_objects.end(); ++k)
{
multimap<string,FWObject*>::iterator l;
l=k;
++l;
for ( ; l!=netzone_objects.end(); ++l)
{
if ( l->second->getId() == k->second->getId() )
{
if (k->first==l->first)
{
QString err("Object %1 is used more than once in network zone of interface %2");
abort(fw, NULL, NULL,
err.arg(l->second->getName().c_str())
.arg(k->first.c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
} else
{
QString err("Object %1 is used in network zones of "
"interfaces %2 and %3");
abort(fw, NULL, NULL,
err.arg(l->second->getName().c_str())
.arg(k->first.c_str())
.arg(l->first.c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
}
}
}
/* Now that all checks are done, we can drop copies of cluster
* interfaces that were added to the firewall by
* CompilerDriver::populateClusterElements()
*/
list<FWObject*> copies_of_cluster_interfaces;
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
{
Interface *iface = Interface::cast(*i);
assert(iface);
if (iface->getOptionsObject()->getBool("cluster_interface"))
copies_of_cluster_interfaces.push_back(iface);
}
while (copies_of_cluster_interfaces.size())
{
fw->remove(copies_of_cluster_interfaces.front());
copies_of_cluster_interfaces.pop_front();
}
all_interfaces = fw->getByTypeDeep(Interface::TYPENAME);
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
{
Interface *iface = dynamic_cast<Interface*>(*i);
assert(iface);
/*
* missing labels on interfaces
*
*/
if (iface->getLabel()=="")
{
string lbl;
if (iface->isDedicatedFailover())
{
// dedicated failover interface misses label. This
// interface can be used in failover cluster group
// or state sync group. Assign label depending on
// the function.
FWObjectTypedChildIterator it =
cluster->findByType(StateSyncClusterGroup::TYPENAME);
StateSyncClusterGroup *state_sync_group =
StateSyncClusterGroup::cast(*it);
if (state_sync_group && state_sync_group->hasMember(iface))
lbl = "state";
if (!iface->getOptionsObject()->getStr("failover_group_id").empty())
lbl = "failover";
}
if (lbl.empty())
{
if (iface->getSecurityLevel()==0) lbl="outside";
else
{
if (iface->getSecurityLevel()==100) lbl="inside";
else
{
QString l("dmz%1");
lbl = l.arg(iface->getSecurityLevel()).toStdString();
}
}
}
iface->setLabel(lbl);
}
}
/*
* now sort interfaces by their network zone "width" (that
* is, more narrow network zone should go first, interface
* with network zone "any" should be the last)
*
std::sort(fw->begin(), fw->end(), sort_by_net_zone() );
*/
std::auto_ptr<Preprocessor> prep(new Preprocessor(objdb , fw, false));
prep->compile();
std::auto_ptr<OSConfigurator> oscnf(new OSConfigurator_pix_os(objdb , fw, false));
if (inTestMode()) oscnf->setTestMode();
if (inEmbeddedMode()) oscnf->setEmbeddedMode();
oscnf->prolog();
oscnf->processFirewallOptions();
/* create compilers and run the whole thing */
std::auto_ptr<NATCompiler_pix> n(new NATCompiler_pix(objdb, fw, false, oscnf.get()));
RuleSet *nat = RuleSet::cast(fw->getFirstByType(NAT::TYPENAME));
if (nat)
{
n->setSourceRuleSet(nat);
n->setRuleSetName(nat->getName());
if (inTestMode()) n->setTestMode();
if (inEmbeddedMode()) n->setEmbeddedMode();
n->setSingleRuleCompileMode(single_rule_id);
n->setDebugLevel( dl );
if (rule_debug_on) n->setDebugRule( drn );
n->setVerbose( verbose );
if ( n->prolog() > 0 )
{
n->compile();
n->epilog();
} else
info(" Nothing to compile in NAT");
}
std::auto_ptr<PolicyCompiler_pix> c(
new PolicyCompiler_pix(objdb, fw, false, oscnf.get() , n.get()));
RuleSet *policy = RuleSet::cast(fw->getFirstByType(Policy::TYPENAME));
if (policy)
{
c->setSourceRuleSet(policy);
c->setRuleSetName(policy->getName());
if (inTestMode()) c->setTestMode();
if (inEmbeddedMode()) c->setEmbeddedMode();
c->setSingleRuleCompileMode(single_rule_id);
c->setDebugLevel( dl );
if (rule_debug_on) c->setDebugRule( drp );
c->setVerbose( verbose );
if ( c->prolog() > 0 )
{
c->compile();
c->epilog();
} else
info(" Nothing to compile in Policy");
}
std::auto_ptr<RoutingCompiler_pix> r(new RoutingCompiler_pix(objdb, fw, false, oscnf.get()));
RuleSet *routing = RuleSet::cast(fw->getFirstByType(Routing::TYPENAME));
if (routing)
{
r->setSourceRuleSet(routing);
r->setRuleSetName(routing->getName());
if (inTestMode()) r->setTestMode();
if (inEmbeddedMode()) r->setEmbeddedMode();
r->setSingleRuleCompileMode(single_rule_id);
r->setDebugLevel( dl );
if (rule_debug_on) r->setDebugRule( drp );
r->setVerbose( verbose );
if ( r->prolog() > 0 )
{
r->compile();
r->epilog();
} else
info(" Nothing to compile in Routing");
}
if (haveErrorsAndWarnings())
{
all_errors.push_front(getErrors("").c_str());
}
system_configuration_script = oscnf->getCompiledScript();
policy_script = c->getCompiledScript();
nat_script = n->getCompiledScript();
routing_script = r->getCompiledScript();
if (c->haveErrorsAndWarnings())
all_errors.push_back(c->getErrors("C ").c_str());
if (n->haveErrorsAndWarnings())
all_errors.push_back(n->getErrors("N ").c_str());
if (r->haveErrorsAndWarnings())
all_errors.push_back(r->getErrors("R ").c_str());
script_buffer = assembleFwScript(
cluster, fw, !cluster_id.empty(), oscnf.get());
if (single_rule_compile_on)
{
return //all_errors.join("\n").toStdString() +
policy_script + nat_script + routing_script;
}
QFileInfo finfo(ofname);
if (finfo.isRelative())
{
// if ofname is relative, it is relative to the
// directory the program started in, which can be
// different from wdir and different from the current dir
// at this point because we do chdir to the directory
// defined by the -d command line option
QFileInfo new_finfo(start_current_dir, ofname);
ofname = new_finfo.absoluteFilePath();
}
info("Output file name: " + ofname.toStdString());
QFile fw_file(ofname);
if (fw_file.open(QIODevice::WriteOnly))
{
QTextStream fw_str(&fw_file);
fw_str << script_buffer;
fw_file.close();
fw_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
QFile::ReadGroup | QFile::ReadOther |
QFile::ExeOwner |
QFile::ExeGroup |
QFile::ExeOther );
info(" Compiled successfully");
} else
{
QString err(" Failed to open file %1 for writing: %2; Current dir: %3");
abort(err.arg(fw_file.fileName()).arg(fw_file.error()).arg(QDir::current().path()).toStdString());
}
}
catch (FatalErrorInSingleRuleCompileMode &ex)
{
return getErrors("");
}
return "";
}
/*
* Sanity checks for the cluster configuration. Per ticket #606:
*
* - state sync group must have master
*
* - one interface must be marked as "dedicated failover" for failover
* group.
*
* - this interface must have failover group with members, one of
* which must be master
*
* - failover interfaces in member firewalls must have ip addresses
* which should be different but in the same subnet.
*
* - possibly another interface can be defined as "dedicated failover"
* and used in state sync group.
*
* - if second interface is used for state sync, it must have ip
* address in member firewalls (different)
*
* - addresses of the dedicated failover interfaces must belong to the
* same subnet in each pair of failover inetrfaces (failover and state sync)
*
* - failover interfaces of both members used in the failover cluster
* group of the cluster object must have the same name.
*
* - The same check should be performed in the state sync group.
*
*
*/
void CompilerDriver_pix::pixClusterConfigurationChecks(Cluster *cluster,
Firewall*)
{
if (cluster==NULL) return;
FWObjectTypedChildIterator it = cluster->findByType(StateSyncClusterGroup::TYPENAME);
StateSyncClusterGroup *state_sync_group = StateSyncClusterGroup::cast(*it);
if (state_sync_group->getStr("master_iface").empty())
{
QString err("One of the interfaces in the state synchronization group "
"must be marked as 'Master'");
abort(cluster, NULL, NULL, err.toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
pixClusterGroupChecks(state_sync_group);
bool failover_group_inspected = false;
list<FWObject*> l2 = cluster->getByTypeDeep(Interface::TYPENAME);
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface = dynamic_cast<Interface*>(*i);
assert(iface);
FailoverClusterGroup *failover_group =
FailoverClusterGroup::cast(
iface->getFirstByType(FailoverClusterGroup::TYPENAME));
if (failover_group)
{
for (FWObjectTypedChildIterator it =
failover_group->findByType(FWObjectReference::TYPENAME);
it != it.end(); ++it)
{
Interface *member_iface = Interface::cast(FWObjectReference::getObject(*it));
assert(member_iface);
pixClusterGroupChecks(failover_group);
if (member_iface->isDedicatedFailover())
{
failover_group_inspected = true;
}
}
}
}
}
void CompilerDriver_pix::pixClusterGroupChecks(ClusterGroup *cluster_group)
{
FWObject *cluster = cluster_group;
while (cluster && !Cluster::isA(cluster)) cluster = cluster->getParent();
FWObject *cluster_interface = NULL;
FWObject *p = cluster_group->getParent();
if (Interface::isA(p)) cluster_interface = p;
map<QString, const InetAddrMask*> addresses_and_masks;
for (FWObjectTypedChildIterator it = cluster_group->findByType(FWObjectReference::TYPENAME);
it != it.end(); ++it)
{
Interface *member_iface = Interface::cast(FWObjectReference::getObject(*it));
assert(member_iface);
FWObject *member = member_iface->getParentHost();
if (cluster_interface)
{
// check consistency of the names.
// In case of PIX the name of the cluster interface should match
// names of member interfaces
if (cluster_interface->getName() != member_iface->getName())
{
QString err("Names of interfaces used in state synchronization "
"or failover group must match the name of the "
"cluster inetrface. Interface %1:%2 has the name "
"that is different from the cluster interface name %3");
abort(cluster, NULL, NULL,
err.arg(member->getName().c_str())
.arg(member_iface->getName().c_str())
.arg(cluster_interface->getName().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
}
if (StateSyncClusterGroup::isA(cluster_group) &&
!member_iface->isDedicatedFailover())
{
QString err("Interface %1 is used in a state synchronization "
"but is not marked as 'Dedicated Failover' "
"interface. All interfaces used for the state "
"synchronization or failover must be marked "
"'Dedicated Failover'. ");
abort(member, NULL, NULL,
err.arg(member_iface->getName().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
if (!member_iface->isRegular() || member_iface->countInetAddresses(true)==0)
{
QString err("Interface %1 which is used in state synchronization "
"or failover group does not have an IP address. "
"All interfaces used for the state "
"synchronization or failover must have ip addresses.");
abort(member, NULL, NULL,
err.arg(member_iface->getName().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
QString key("%1:%2");
FWObjectTypedChildIterator it_addr = member_iface->findByType(IPv4::TYPENAME);
IPv4* addr = IPv4::cast(*it_addr);
addresses_and_masks[key.arg(member->getName().c_str()).arg(member_iface->getName().c_str())] =
addr->getInetAddrMaskObjectPtr();
}
if (addresses_and_masks.size() >= 2)
{
QString first_key;
const InetAddr *first_network_addr = NULL;
map<QString, const InetAddrMask*>::iterator it;
for (it=addresses_and_masks.begin(); it!=addresses_and_masks.end(); ++it)
{
QString key = it->first;
const InetAddrMask *am = it->second;
if (first_network_addr == NULL)
{
first_key = key;
first_network_addr = am->getNetworkAddressPtr();
} else
{
const InetAddr *network_addr = am->getNetworkAddressPtr();
if (*first_network_addr != *(network_addr))
{
QString err("Interfaces used in state synchronization "
"or failover group must have IP addresses on "
"the same subnet. Interfaces %1 and %2 have "
"addresses on different subnets: %3 , %4");
abort(cluster, NULL, NULL,
err.arg(first_key).arg(key)
.arg(first_network_addr->toString().c_str())
.arg(network_addr->toString().c_str()).toStdString());
throw FatalErrorInSingleRuleCompileMode();
}
}
}
}
}