From c710c1003db63f5bdf5c321c4ce580319e2fab06 Mon Sep 17 00:00:00 2001 From: Vadim Kurland Date: Sun, 12 Oct 2008 04:03:22 +0000 Subject: [PATCH] attribute "mangle_table_only" for iptables policy rulesets --- doc/ChangeLog | 16 ++++ src/gui/RuleSetDialog.cpp | 37 ++++++- src/ipt/MangleTableCompiler_ipt.cpp | 93 ++++++++++-------- src/ipt/MangleTableCompiler_ipt.h | 1 + src/ipt/PolicyCompiler_ipt.cpp | 45 +++++++-- src/ipt/PolicyCompiler_ipt.h | 4 +- src/ipt/ipt.cpp | 62 ++++++------ test/ipt/objects-for-regression-tests.fwb | 112 +++++++++++++++++++++- 8 files changed, 283 insertions(+), 87 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 8454dec21..d4ac7be25 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,21 @@ 2008-10-11 Vadim Kurland + * PolicyCompiler_ipt.cpp (finalizeChain::processNext): Always + placing rules with action "Accept" in table mangle in chain + PREROUTING + + * RuleSetDialog.cpp (RuleSetDialog::loadFWObject): Added attribute + to the Policy object for iptables to indicate that this policy + ruleset should be compiled into filter and mangle tables or only + for the mangle table. This makes sense (and is only shown) for + iptables firewalls. By default the attribute is set to + "filter+mangle" which means compiler will try to figure out which + table each rule should go to. However some combinations of service + objects and actions are ambiguous and can be used in both filter + and mangle tables. In cases like these, user can help by creating + separate Policy ruleset that will be translated only into iptables + rules in the mangle table. + * PolicyCompiler_ipt.cpp (singleSrvNegation::processNext): fixed bug #2148378: "Negation does not work on Tag Service". Policy compiler for iptables should be able to use "!" single-object diff --git a/src/gui/RuleSetDialog.cpp b/src/gui/RuleSetDialog.cpp index e0131caba..2cc768419 100644 --- a/src/gui/RuleSetDialog.cpp +++ b/src/gui/RuleSetDialog.cpp @@ -72,7 +72,7 @@ void RuleSetDialog::loadFWObject(FWObject *o) RuleSet *s = dynamic_cast(obj); assert(s!=NULL); - init=true; + init = true; m_dialog->obj_name->setText( QString::fromUtf8(s->getName().c_str()) ); m_dialog->comment->setText( QString::fromUtf8(s->getComment().c_str()) ); @@ -84,8 +84,10 @@ void RuleSetDialog::loadFWObject(FWObject *o) FWObject *fw = o; while (fw && fw->getTypeName()!="Firewall") fw = fw->getParent(); if (fw) platform = fw->getStr("platform"); + FWOptions *fwopt = Firewall::cast(fw)->getOptionsObject(); if (platform == "iptables") + { m_dialog->top_rule_set->setToolTip( QApplication::translate("RuleSetDialog_q", "On iptables \"top\" rule set goes into \n" @@ -96,6 +98,27 @@ void RuleSetDialog::loadFWObject(FWObject *o) "the rule set.", 0, QApplication::UnicodeUTF8)); + if (Policy::isA(obj)) + { + // if this attribute is absent, we consider it False, so for + // backwards compatibility the rule set is considered + // filter+mangle rather than mangle only. + m_dialog->iptables_only->show(); + QStringList mangle_rulesets = + QString(fwopt->getStr("ipt_mangle_only_rulesets").c_str()). + split(" "); + bool f = (mangle_rulesets.indexOf(s->getName().c_str()) >= 0); + m_dialog->ipt_filter_table->setChecked(!f); + m_dialog->ipt_mangle_table->setChecked(f); + } else + m_dialog->iptables_only->hide(); + + } else + { + m_dialog->iptables_only->hide(); + } + + if (platform == "pf") m_dialog->top_rule_set->setToolTip( QApplication::translate("RuleSetDialog_q", @@ -161,6 +184,10 @@ void RuleSetDialog::applyChanges() RuleSet *s = dynamic_cast(obj); assert(s!=NULL); + FWObject *fw = obj; + while (fw && fw->getTypeName()!="Firewall") fw = fw->getParent(); + FWOptions *fwopt = Firewall::cast(fw)->getOptionsObject(); + string oldname=obj->getName(); obj->setName( string(m_dialog->obj_name->text().toUtf8().constData()) ); obj->setComment( @@ -168,6 +195,14 @@ void RuleSetDialog::applyChanges() s->setV6(m_dialog->ipv6_rule_set->isChecked()); s->setTop(m_dialog->top_rule_set->isChecked()); + QStringList mangle_rulesets = + QString(fwopt->getStr("ipt_mangle_only_rulesets").c_str()). + split(" "); + if (mangle_rulesets.indexOf(s->getName().c_str()) < 0) + mangle_rulesets.push_back(s->getName().c_str()); + fwopt->setStr("ipt_mangle_only_rulesets", + mangle_rulesets.join(" ").toAscii().constData()); + mw->updateObjName(obj,QString::fromUtf8(oldname.c_str())); init=true; diff --git a/src/ipt/MangleTableCompiler_ipt.cpp b/src/ipt/MangleTableCompiler_ipt.cpp index aa0c27455..04b0563bb 100644 --- a/src/ipt/MangleTableCompiler_ipt.cpp +++ b/src/ipt/MangleTableCompiler_ipt.cpp @@ -34,6 +34,9 @@ #include "fwbuilder/Rule.h" #include +#include +#include +#include using namespace libfwbuilder; using namespace fwcompiler; @@ -41,6 +44,7 @@ using namespace std; string MangleTableCompiler_ipt::myPlatformName() { return "iptables"; } + int MangleTableCompiler_ipt::prolog() { return PolicyCompiler_ipt::prolog(); @@ -63,49 +67,57 @@ int MangleTableCompiler_ipt::prolog() bool MangleTableCompiler_ipt::keepMangleTableRules::processNext() { - PolicyRule *rule=getNext(); if (rule==NULL) return false; - FWOptions *ruleopt =rule->getOptionsObject(); + PolicyRule *rule = getNext(); if (rule==NULL) return false; + FWOptions *ruleopt = rule->getOptionsObject(); + PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); - if (rule->getAction() == PolicyRule::Branch && - ruleopt->getBool("ipt_branch_in_mangle")) - { - PolicyRule* r; - - // this is a branching rule for mangle table. Need to put it - // into PREROUTING and POSTROUTING chains as well because some - // targets that work with mangle table can only go into these - // chains, yet we do not know what kind of rules will user - // place in the branch - - if (rule->getDirection()==PolicyRule::Undefined || - rule->getDirection()==PolicyRule::Both || - rule->getDirection()==PolicyRule::Inbound) - { - r= PolicyRule::cast(compiler->dbcopy->create(PolicyRule::TYPENAME)); - compiler->temp_ruleset->add(r); - r->duplicate(rule); - r->setStr("ipt_chain","PREROUTING"); - tmp_queue.push_back(r); - } - - if (rule->getDirection()==PolicyRule::Undefined || - rule->getDirection()==PolicyRule::Both || - rule->getDirection()==PolicyRule::Outbound) - { - r= PolicyRule::cast(compiler->dbcopy->create(PolicyRule::TYPENAME)); - compiler->temp_ruleset->add(r); - r->duplicate(rule); - r->setStr("ipt_chain","POSTROUTING"); - tmp_queue.push_back(r); - } + string ruleset_name = compiler->getRuleSetName(); + if (ipt_comp->isMangleOnlyRuleSet(ruleset_name)) tmp_queue.push_back(rule); - } + else + { + if (rule->getAction() == PolicyRule::Branch && + ruleopt->getBool("ipt_branch_in_mangle")) + { + PolicyRule* r; + + // this is a branching rule for mangle table. Need to put it + // into PREROUTING and POSTROUTING chains as well because some + // targets that work with mangle table can only go into these + // chains, yet we do not know what kind of rules will user + // place in the branch - if (rule->getAction() == PolicyRule::Tag || - rule->getAction() == PolicyRule::Route || - rule->getAction() == PolicyRule::Classify || - ruleopt->getBool("put_in_mangle_table")) tmp_queue.push_back(rule); + if (rule->getDirection()==PolicyRule::Undefined || + rule->getDirection()==PolicyRule::Both || + rule->getDirection()==PolicyRule::Inbound) + { + r= PolicyRule::cast(compiler->dbcopy->create(PolicyRule::TYPENAME)); + compiler->temp_ruleset->add(r); + r->duplicate(rule); + r->setStr("ipt_chain","PREROUTING"); + tmp_queue.push_back(r); + } + + if (rule->getDirection()==PolicyRule::Undefined || + rule->getDirection()==PolicyRule::Both || + rule->getDirection()==PolicyRule::Outbound) + { + r= PolicyRule::cast(compiler->dbcopy->create(PolicyRule::TYPENAME)); + compiler->temp_ruleset->add(r); + r->duplicate(rule); + r->setStr("ipt_chain","POSTROUTING"); + tmp_queue.push_back(r); + } + + tmp_queue.push_back(rule); + } + + if (rule->getAction() == PolicyRule::Tag || + rule->getAction() == PolicyRule::Route || + rule->getAction() == PolicyRule::Classify || + ruleopt->getBool("put_in_mangle_table")) tmp_queue.push_back(rule); + } return true; } @@ -118,7 +130,8 @@ void MangleTableCompiler_ipt::addRuleFilter() string MangleTableCompiler_ipt::flushAndSetDefaultPolicy() { - return ""; + return printAutomaticRulesForMangleTable(have_connmark, + have_connmark_in_output); } diff --git a/src/ipt/MangleTableCompiler_ipt.h b/src/ipt/MangleTableCompiler_ipt.h index 1eb429842..fd8664367 100644 --- a/src/ipt/MangleTableCompiler_ipt.h +++ b/src/ipt/MangleTableCompiler_ipt.h @@ -41,6 +41,7 @@ namespace fwcompiler { * this processor drops all rules except for those that require mangle table */ DECLARE_POLICY_RULE_PROCESSOR(keepMangleTableRules); + friend class keepMangleTableRules; public: diff --git a/src/ipt/PolicyCompiler_ipt.cpp b/src/ipt/PolicyCompiler_ipt.cpp index ce7278804..c0677dee9 100644 --- a/src/ipt/PolicyCompiler_ipt.cpp +++ b/src/ipt/PolicyCompiler_ipt.cpp @@ -64,6 +64,8 @@ #include #include #include +#include +#include #include @@ -341,7 +343,15 @@ int PolicyCompiler_ipt::prolog() if (fw->getStr("platform")!="iptables") abort(_("Unsupported platform ") + fw->getStr("platform") ); - int n= PolicyCompiler::prolog(); + int n = PolicyCompiler::prolog(); + + FWOptions *fwopt = getCachedFwOpt(); + + istringstream is(fwopt->getStr("ipt_mangle_only_rulesets")); + std::copy(istream_iterator(is), + istream_iterator(), + back_inserter(mangle_only_rulesets)); + // initialize counters for the standard chains for (list::const_iterator i = @@ -396,7 +406,6 @@ int PolicyCompiler_ipt::prolog() cacheObj(bcast255); - FWOptions *fwopt = getCachedFwOpt(); bool afpa = fwopt->getBool("firewall_is_part_of_any_and_networks"); for(FWObject::iterator i=combined_ruleset->begin(); @@ -734,6 +743,11 @@ bool PolicyCompiler_ipt::Route::processNext() bool PolicyCompiler_ipt::dropMangleTableRules::processNext() { PolicyRule *rule=getNext(); if (rule==NULL) return false; + PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); + + string ruleset_name = compiler->getRuleSetName(); + + if (ipt_comp->isMangleOnlyRuleSet(ruleset_name)) return true; if (rule->getAction() == PolicyRule::Tag || rule->getAction() == PolicyRule::Route || @@ -2869,20 +2883,26 @@ bool PolicyCompiler_ipt::finalizeChain::processNext() rule->setStr("ipt_chain","FORWARD"); break; } + + if (rule->getAction() == PolicyRule::Accept) + rule->setStr("ipt_chain","PREROUTING"); + } else { // RuleElementSrc *srcrel=rule->getSrc(); - Address *src =compiler->getFirstSrc(rule); + Address *src = compiler->getFirstSrc(rule); if (src==NULL) - compiler->abort(string("finalizeChain: Empty Source rule element in rule ") + - rule->getLabel()); + compiler->abort( + string("finalizeChain: Empty Source rule element in rule ") + + rule->getLabel()); // RuleElementDst *dstrel=rule->getDst(); Address *dst =compiler->getFirstDst(rule); if (dst==NULL) - compiler->abort(string("finalizeChain: Empty Destination rule element in rule ") + - rule->getLabel()); + compiler->abort( + string("finalizeChain: Empty Destination rule element in rule ") + + rule->getLabel()); bool b,m; /* @@ -3974,8 +3994,8 @@ void PolicyCompiler_ipt::compile() add( new decideOnChainIfLoopback("any-any rule on loopback" ) ); // add( new decideOnChainForClassify("set chain if action is Classify")); - add( new finalizeChain( "decide on chain" ) ); - add( new decideOnTarget( "decide on target" ) ); + add( new finalizeChain( "decide on chain" ) ); + add( new decideOnTarget( "decide on target" ) ); add( new checkForRestoreMarkInOutput( "check if we need -A OUTPUT -j CONNMARK --restore-mark")); @@ -4243,4 +4263,11 @@ bool PolicyCompiler_ipt::newIptables(const string &version) XMLTools::version_compare(version, "1.2.6")>0); } +bool PolicyCompiler_ipt::isMangleOnlyRuleSet(const string &ruleset_name) +{ + return (std::find(mangle_only_rulesets.begin(), + mangle_only_rulesets.end(), + ruleset_name) != mangle_only_rulesets.end()); +} + diff --git a/src/ipt/PolicyCompiler_ipt.h b/src/ipt/PolicyCompiler_ipt.h index 2e4981eb6..f511662f9 100644 --- a/src/ipt/PolicyCompiler_ipt.h +++ b/src/ipt/PolicyCompiler_ipt.h @@ -75,6 +75,7 @@ namespace fwcompiler { bool have_connmark; bool have_connmark_in_output; std::string my_table; + std::list mangle_only_rulesets; std::map tmp_chain_no; std::map chain_usage_counter; @@ -996,7 +997,8 @@ namespace fwcompiler { bool haveConnMarkRules() { return have_connmark; } bool haveConnMarkRulesInOutput() { return have_connmark_in_output; } - + + bool isMangleOnlyRuleSet(const std::string &rule_set_name); }; diff --git a/src/ipt/ipt.cpp b/src/ipt/ipt.cpp index cc3544528..fb6e623af 100644 --- a/src/ipt/ipt.cpp +++ b/src/ipt/ipt.cpp @@ -783,27 +783,27 @@ _("Dynamic interface %s should not have an IP address object attached to it. Thi if (policy->isV6()!=ipv6_policy) continue; - MangleTableCompiler_ipt *m = new MangleTableCompiler_ipt( + MangleTableCompiler_ipt m( objdb , fwobjectname.toUtf8().constData(), ipv6_policy , oscnf, &minus_n_commands_mangle ); if (!policy->isTop()) - m->registerRuleSetChain(branch_name); + m.registerRuleSetChain(branch_name); - m->setSourceRuleSet( policy ); - m->setRuleSetName(branch_name); + m.setSourceRuleSet( policy ); + m.setRuleSetName(branch_name); - m->setDebugLevel( dl ); - m->setDebugRule( drp ); - m->setVerbose( (bool)(verbose) ); - m->setHaveDynamicInterfaces(have_dynamic_interfaces); - if (test_mode) m->setTestMode(); + m.setDebugLevel( dl ); + m.setDebugRule( drp ); + m.setVerbose( (bool)(verbose) ); + m.setHaveDynamicInterfaces(have_dynamic_interfaces); + if (test_mode) m.setTestMode(); - if ( (mangle_rules_count = m->prolog()) > 0 ) + if ( (mangle_rules_count = m.prolog()) > 0 ) { - m->compile(); - m->epilog(); + m.compile(); + m.epilog(); // We need to generate automatic rules in mangle // table (-j CONNMARK --restore-mark) if CONNMARK @@ -816,30 +816,36 @@ _("Dynamic interface %s should not have an IP address object attached to it. Thi // later if either of these flags is true after // all rulesets have been processed. - have_connmark |= m->haveConnMarkRules(); - have_connmark_in_output |= m->haveConnMarkRulesInOutput(); + have_connmark |= m.haveConnMarkRules(); + have_connmark_in_output |= m.haveConnMarkRulesInOutput(); long m_str_pos = m_str.tellp(); - if (policy->isTop()) top_level_mangle_compiler = m; + if (policy->isTop()) + { + m_str << "# ================ Table 'mangle', "; + m_str << "automatic rules"; + m_str << endl; + m_str << m.flushAndSetDefaultPolicy(); + } - if (m->getCompiledScriptLength() > 0) + if (m.getCompiledScriptLength() > 0) { m_str << "# ================ Table 'mangle', rule set " << branch_name << endl; - if (m->haveErrorsAndWarnings()) + if (m.haveErrorsAndWarnings()) { m_str << "# Policy compiler errors and warnings:" << endl; - m_str << m->getErrors("# "); + m_str << m.getErrors("# "); } - m_str << m->getCompiledScript(); + m_str << m.getCompiledScript(); } if (m_str_pos!=m_str.tellp()) { - m_str << m->commit(); + m_str << m.commit(); m_str << endl; empty_output = false; } @@ -847,7 +853,7 @@ _("Dynamic interface %s should not have an IP address object attached to it. Thi PolicyCompiler_ipt c( - objdb, fwobjectname.toUtf8().constData(), ipv6_policy, oscnf, + objdb,fwobjectname.toUtf8().constData(), ipv6_policy, oscnf, &minus_n_commands_filter); if (!policy->isTop()) @@ -895,18 +901,6 @@ _("Dynamic interface %s should not have an IP address object attached to it. Thi } - string mangle_table_script = ""; - if (top_level_mangle_compiler && - (have_connmark || have_connmark_in_output)) - { - mangle_table_script = "# ================ Table 'mangle', "; - mangle_table_script += "automatic rules"; - mangle_table_script += "\n"; - mangle_table_script += - top_level_mangle_compiler->printAutomaticRulesForMangleTable( - have_connmark, have_connmark_in_output); - } - if (!empty_output) { if (ipv6_policy) @@ -925,7 +919,7 @@ _("Dynamic interface %s should not have an IP address object attached to it. Thi generated_script += dumpScript(nocomm, fw, reset_rules.str(), n_str.str(), - mangle_table_script + m_str.str(), + m_str.str(), c_str.str(), ipv6_policy); } diff --git a/test/ipt/objects-for-regression-tests.fwb b/test/ipt/objects-for-regression-tests.fwb index b6433427b..adba6106c 100644 --- a/test/ipt/objects-for-regression-tests.fwb +++ b/test/ipt/objects-for-regression-tests.fwb @@ -1,6 +1,6 @@ - + @@ -601,6 +601,8 @@ + + @@ -19340,7 +19342,7 @@ echo '%FWBPROMPT%'; sh /tmp/%FWSCRIPT% - + @@ -20664,6 +20666,111 @@ echo '%FWBPROMPT%'; sh /tmp/%FWSCRIPT% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -20699,6 +20806,7 @@ echo '%FWBPROMPT%'; sh /tmp/%FWSCRIPT% +