1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-03-23 03:37:15 +01:00
fwbuilder/src/pflib/PolicyCompiler_pf_writers.cpp
Vadim Kurland a0314a7d97 * src/pflib/PolicyCompiler_pf_writers.cpp (PrintRule::processNext):
fixes #1210 "syntax error in PF rule - "modulate state" is
required".  Per bug reported in the mailing list (and according to
the pf.conf manual), pf.conf requires "keep state", "modulate
state" or "synproxy"if any of the stateful tracking options are
used in the rule. These include "max", "no-sync", "pflow",
"sloppy", "source-track" and others.
2010-02-07 05:24:21 +00:00

1187 lines
37 KiB
C++

/*
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
*/
#include "PolicyCompiler_pf.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/AddressRange.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/IPService.h"
#include "fwbuilder/ICMPService.h"
#include "fwbuilder/ICMP6Service.h"
#include "fwbuilder/TCPService.h"
#include "fwbuilder/UDPService.h"
#include "fwbuilder/CustomService.h"
#include "fwbuilder/TagService.h"
#include "fwbuilder/UserService.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/FWOptions.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/DNSName.h"
#include "fwbuilder/AddressTable.h"
#include "fwbuilder/XMLTools.h"
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <assert.h>
#include <QStringList>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
/**
*-----------------------------------------------------------------------
* Methods for printing
*/
void PolicyCompiler_pf::PrintRule::_printAction(PolicyRule *rule)
{
FWOptions *ruleopt =rule->getOptionsObject();
Service *srv=compiler->getFirstSrv(rule); assert(srv);
switch (rule->getAction())
{
case PolicyRule::Accept:
case PolicyRule::Tag:
case PolicyRule::Classify:
case PolicyRule::Accounting:
case PolicyRule::Route:
compiler->output << "pass ";
break;
case PolicyRule::Deny:
compiler->output << "block ";
break;
case PolicyRule::Reject:
if (TCPService::isA(srv)) compiler->output << "block return-rst ";
else
{
string aor=ruleopt->getStr("action_on_reject");
string code;
if ( aor.find("ICMP")!=string::npos )
{
code="return-icmp ";
if (aor.find("unreachable")!=string::npos )
{
if (aor.find("net")!=string::npos) code=code+"( 0 ) ";
if (aor.find("host")!=string::npos) code=code+"( 1 ) ";
if (aor.find("protocol")!=string::npos) code=code+"( 2 ) ";
if (aor.find("port")!=string::npos) code=code+"( 3 ) ";
}
if (aor.find("prohibited")!=string::npos )
{
if (aor.find("net")!=string::npos) code=code+"( 9 ) ";
if (aor.find("host")!=string::npos) code=code+"( 10 ) ";
}
} else
code="return-icmp ";
compiler->output << "block " << code;
}
break;
case PolicyRule::Scrub:
compiler->output << "scrub ";
break;
case PolicyRule::Custom:
compiler->output << ruleopt->getStr("custom_str") << " ";
break;
case PolicyRule::Branch:
{
RuleSet *ruleset = rule->getBranch();
if (ruleset==NULL)
{
compiler->abort(
rule,
"Branching rule refers ruleset that does not exist");
// if we are in test mode or single-rule compile mode
compiler->output << "anchor \"UNDEFINED\" ";
}else
{
string ruleset_name = ruleset->getName();
compiler->output << "anchor \"" << ruleset_name << "\" ";
}
break;
}
default:
compiler->abort(
rule,
string("Unknown action ") + rule->getActionAsString());
}
}
void PolicyCompiler_pf::PrintRule::_printRouteOptions(PolicyRule *rule)
{
FWOptions *ruleopt =rule->getOptionsObject();
if (rule->getAction() == PolicyRule::Route)
{
string prefix = "pf";
if (compiler->myPlatformName()=="ipf") prefix="ipf";
string ro = ruleopt->getStr(prefix+"_route_option");
if (ruleopt->getBool("pf_fastroute") && ro != "none")
{
compiler->abort(
rule,
"Cannot use fastroute and route methods in "
"the same rule because they are mutually "
"exclusive.");
} else if (ruleopt->getBool("pf_fastroute") && ro == "none")
{
compiler->output << "fastroute ";
} else
{
string roif = ruleopt->getStr(prefix+"_route_opt_if");
string roaddr_list = ruleopt->getStr(prefix+"_route_opt_addr");
string roload = ruleopt->getStr("pf_route_load_option");
if (!ro.empty())
{
if (roif.empty())
compiler->abort(
rule,
"Interface specification is required "
"for action Route.");
if (ro == "route_through")
compiler->output << "route-to ";
else if (ro == "route_reply_through")
compiler->output << "reply-to ";
else if (ro == "route_copy_through")
compiler->output << "dup-to ";
else
compiler->abort(
rule,
"Unknown option for rule action Route: '" +
ro + "'");
compiler->output << "{ ";
int route_member = 0;
std::istringstream buf(roaddr_list);
string roaddr;
while (std::getline(buf, roaddr, ','))
{
if (!roaddr.empty())
{
if (route_member > 0 )
{
compiler->output << ", ";
}
compiler->output << "( ";
compiler->output << roif << " ";
compiler->output << roaddr << " ";
compiler->output << ") ";
std::string::size_type sp = roaddr.find('/');
if (sp!=std::string::npos)
{
// roaddr is addr/netmask
try
{
string a = roaddr.substr(0,sp);
InetAddr roaddr_addr = InetAddr(a);
} catch (FWException &ex)
{
compiler->abort(
rule,
"Illegal IP address for next hop");
}
try
{
InetAddr roaddr_netmask;
string n = roaddr.substr(sp+1);
if (n.find('.')!=std::string::npos)
{
roaddr_netmask = InetAddr(n);
} else
{
roaddr_netmask = InetAddr(
atoi(n.c_str()));
}
if (roaddr_netmask.getLength()==32)
route_member++;
else
// lame way to tell compiler that
// we actually have several addresses for
// the next hop. We do not exactly care
// how many there are, as long as it is
// greater than 1.
route_member += 2;
} catch (FWException &ex)
{
compiler->abort(
rule,
"Illegal netmask for next hop");
}
} else
{
// roaddr is just an addres
try
{
InetAddr roaddr_addr = InetAddr(roaddr);
} catch (FWException &ex)
{
compiler->abort(
rule,
"Illegal IP address for next hop");
}
route_member++;
}
}
}
if (route_member < 1)
{
compiler->abort(
rule,
"No router specified rule action Route: '"+
ro + "'");
}
if (route_member >= 2 && (roload.empty() || roload == "none"))
{
compiler->abort(
rule,
"More than one router specified without load balancing for rule action Route: '" +
ro + "'");
}
if (route_member == 1 && ((!roload.empty()) && roload != "none"))
{
compiler->abort(
rule,
"Only one router specified with load balancing for rule action Route: '" +
ro + "'");
}
compiler->output << "} ";
if (!roload.empty())
{
if (roload == "bitmask")
compiler->output << "bitmask ";
else if (roload == "random")
compiler->output << "random ";
else if (roload == "source_hash")
compiler->output << "source-hash ";
else if (roload == "round_robin")
compiler->output << "round-robin ";
}
}
}
}
}
void PolicyCompiler_pf::PrintRule::_printQueue(PolicyRule *rule)
{
FWOptions *ruleopt =rule->getOptionsObject();
if (rule->getAction() == PolicyRule::Classify)
compiler->output << "queue " << ruleopt->getStr("classify_str") << " ";
}
void PolicyCompiler_pf::PrintRule::_printUser(PolicyRule *rule)
{
RuleElementSrv *srvrel = rule->getSrv();
FWObject *o = srvrel->front();
if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *srv= Service::cast(o);
if (!UserService::isA(srv)) return;
ostringstream str;
if (srvrel->size()==1)
{
str << "user ";
if (srvrel->getNeg()) str << "!= ";
str << UserService::constcast(srv)->getUserId() << " ";
compiler->output << str.str() << " ";
} else
{
int counter = 0;
for (FWObject::iterator i=srvrel->begin(); i!=srvrel->end(); i++)
{
FWObject *o= *i;
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *s=Service::cast( o );
assert(s);
if (counter > 0) str << ",";
str << " ";
if (srvrel->getNeg()) str << "!= ";
str << UserService::constcast(s)->getUserId();
counter++;
}
if ( counter )
{
compiler->output << "user {" << str.str() << " } ";
}
}
}
void PolicyCompiler_pf::PrintRule::_printTag(PolicyRule *rule)
{
if (rule->getAction() == PolicyRule::Tag)
compiler->output << "tag " << rule->getTagValue() << " ";
// compiler->output << "tag " << ruleopt->getStr("tagvalue") << " ";
}
void PolicyCompiler_pf::PrintRule::_printDirection(PolicyRule *rule)
{
if (rule->getDirection()==PolicyRule::Outbound) compiler->output << "out ";
if (rule->getDirection()==PolicyRule::Inbound) compiler->output << "in ";
}
void PolicyCompiler_pf::PrintRule::_printLogging(PolicyRule *rule)
{
if (rule->getAction() != PolicyRule::Branch &&
rule->getLogging()) compiler->output << " log ";
}
void PolicyCompiler_pf::PrintRule::_printLabel(PolicyRule *rule)
{
FWOptions *ruleopt =rule->getOptionsObject();
string s=ruleopt->getStr("log_prefix");
if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_prefix");
if (!s.empty())
compiler->output << " label " << _printLogPrefix(rule,s) << " ";
}
string PolicyCompiler_pf::PrintRule::_printLogPrefix(PolicyRule *rule,
const string &prefix)
{
string s=prefix;
/* deal with our logging macros:
* %N - rule number
* %A - action
* %I - interface name
* %C - chain name
*/
string::size_type n;
if (rule && (n=s.find("%N"))!=string::npos )
{
std::ostringstream s1;
s1 << rule->getPosition();
s.replace(n,2,s1.str());
}
if (rule && (n=s.find("%A"))!=string::npos )
{
std::ostringstream s1;
switch (rule->getAction())
{
case PolicyRule::Accept: s1 << "ACCEPT"; break;
case PolicyRule::Deny: s1 << "DROP"; break;
case PolicyRule::Reject: s1 << "REJECT"; break;
case PolicyRule::Return: s1 << "RETURN"; break;
default: break;
}
s.replace(n,2,s1.str());
}
if (rule && (n=s.find("%I"))!=string::npos )
{
std::ostringstream s1;
RuleElementItf *intf_re = rule->getItf();
string rule_interfaces;
if (!intf_re->isAny())
{
for (FWObject::iterator it=intf_re->begin(); it!=intf_re->end(); ++it)
{
FWObject *o = *it;
if (FWReference::cast(o)!=NULL) o = FWReference::cast(o)->getPointer();
rule_interfaces += " " + o->getName();
}
}
if (!rule_interfaces.empty())
s.replace(n, 2, rule_interfaces);
else
s.replace(n, 2, "global");
// string rule_iface = rule->getInterfaceStr();
// if (rule_iface!="")
// {
// s1 << rule_iface;
// s.replace(n,2,s1.str());
// } else
// s.replace(n,2,"global");
}
if (rule && (n=s.find("%C"))!=string::npos )
{
s.replace(n,2,""); // there is no chain in PF and friends
}
return "\"" + s + "\" ";
}
void PolicyCompiler_pf::PrintRule::_printInterface(PolicyRule *rule)
{
RuleElementItf *intf_re = rule->getItf();
string rule_interfaces;
int intf_count = 0;
if (!intf_re->isAny())
{
for (FWObject::iterator it=intf_re->begin(); it!=intf_re->end(); ++it)
{
FWObject *o = *it;
if (FWReference::cast(o)!=NULL) o = FWReference::cast(o)->getPointer();
rule_interfaces += " " + o->getName();
intf_count++;
}
compiler->output << "on";
if (intf_count > 1) compiler->output << " {";
compiler->output << rule_interfaces;
if (intf_count > 1) compiler->output << " }";
compiler->output << " ";
}
// string iface_name = rule->getInterfaceStr();
// if (iface_name!="")
// compiler->output << "on " << iface_name << " ";
}
// print address family
void PolicyCompiler_pf::PrintRule::_printAF(PolicyRule*)
{
PolicyCompiler_pf *pf_comp=dynamic_cast<PolicyCompiler_pf*>(compiler);
if (pf_comp->ipv6) compiler->output << "inet6 ";
else compiler->output << "inet ";
}
void PolicyCompiler_pf::PrintRule::_printProtocol(Service *srv)
{
// CustomService returns protocol name starting with v3.0.4
// However CustomService can return protocol name "any", which we should
// just skip.
// CustomService returns protocol name starting with v3.0.4
if (CustomService::isA(srv))
{
// check if the code string for this custom service already includes
// "proto ..." fragment
string code = CustomService::cast(srv)->getCodeForPlatform(
compiler->myPlatformName());
std::size_t minus_p = code.find("proto ");
if (minus_p != string::npos) return;
string pn = srv->getProtocolName();
if (pn == "any") return;
}
if (!srv->isAny() && !TagService::isA(srv) && !UserService::isA(srv) &&
srv->getProtocolName()!="ip")
{
compiler->output << "proto ";
compiler->output << srv->getProtocolName();
compiler->output << " ";
}
}
string PolicyCompiler_pf::PrintRule::_printPort(int rs,int re,bool neg)
{
ostringstream str;
if (rs<0) rs=0;
if (re<0) re=0;
if (!neg)
{
if (rs>0 || re>0)
{
if (rs>re && re==0) re=rs;
if (rs==re) str << rs; // TODO: do we need '=' here ?
else
if (rs==0 && re!=0) str << "<= " << re;
else
if (rs!=0 && re==65535) str << ">= " << rs;
else
{
/*
* port range. Operator '><' defines range in a such way that boundaries
* are not included. Since we assume it is inclusive, let's move boundaries
*/
if (rs>0 ) rs--;
if (re<65535) re++;
str << rs << " >< " << re;
}
}
} else
{
if (rs>0 || re>0)
{
if (rs==re) str << "!= " << rs;
else
if (rs==0 && re!=0) str << "> " << re;
else
if (rs!=0 && re==65535) str << "< " << rs;
else
{
str << rs << " <> " << re;
}
}
}
return str.str();
}
/*
* we made sure that all services in rel represent the same protocol.
*/
void PolicyCompiler_pf::PrintRule::_printSrcService(RuleElementSrv *rel)
{
/* I do not want to use rel->getFirst because it traverses the tree to
* find the object. I'd rather use a cached copy in the compiler
*/
FWObject *o=rel->front();
if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *srv= Service::cast(o);
string prefix = "";
if (UDPService::isA(srv) || TCPService::isA(srv)) prefix = "port ";
if (rel->size()==1)
{
if (UDPService::isA(srv) || TCPService::isA(srv))
{
string str=_printSrcService( srv , rel->getNeg());
if (! str.empty() ) compiler->output << prefix << str << " ";
}
} else
{
string str;
for (FWObject::iterator i=rel->begin(); i!=rel->end(); i++)
{
FWObject *o= *i;
if (FWReference::cast(o)!=NULL)
o=FWReference::cast(o)->getPointer();
Service *s = Service::cast( o );
assert(s);
string str1;
if (UDPService::isA(srv) || TCPService::isA(srv))
str1 = _printSrcService(s , rel->getNeg() );
if (! str.empty() && ! str1.empty() ) str = str + ", ";
str = str + str1;
}
if ( !str.empty() )
compiler->output << prefix << "{ " << str << "} ";
}
}
string PolicyCompiler_pf::PrintRule::_printSrcService(Service *srv, bool neg)
{
ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=TCPUDPService::cast(srv)->getSrcRangeStart();
int re=TCPUDPService::cast(srv)->getSrcRangeEnd();
str << _printPort(rs,re,neg);
}
return str.str();
}
void PolicyCompiler_pf::PrintRule::_printDstService(RuleElementSrv *rel)
{
FWObject *o=rel->front();
if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *srv= Service::cast(o);
if (rel->size()==1)
{
string str=_printDstService( srv , rel->getNeg());
if ( ! str.empty() )
{
if (UDPService::isA(srv) || TCPService::isA(srv))
compiler->output << "port " << str << " ";
else
{
if (ICMPService::isA(srv))
compiler->output << "icmp-type " << str << " ";
else
if (ICMP6Service::isA(srv))
compiler->output << "icmp6-type " << str << " ";
else
compiler->output << str << " ";
}
}
if (TCPService::isA(srv))
{
str=_printTCPFlags(TCPService::cast(srv));
if (!str.empty()) compiler->output << "flags " << str << " ";
}
if (IPService::isA(srv))
{
if (srv->getBool("fragm") || srv->getBool("short_fragm"))
compiler->output << " fragment ";
const IPService *ip = IPService::constcast(srv);
string tos = ip->getTOSCode();
string dscp = ip->getDSCPCode();
if (!tos.empty()) compiler->output << " tos " << tos << " ";
if (!dscp.empty())
compiler->abort(
rel->getParent(),
"PF does not support DSCP matching");
}
} else
{
string str;
for (FWObject::iterator i=rel->begin(); i!=rel->end(); i++)
{
FWObject *o= *i;
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *s=Service::cast( o );
assert(s);
string str1= _printDstService(s , rel->getNeg() );
if (! str.empty() && ! str1.empty() ) str = str + ", ";
str = str + str1;
}
if ( !str.empty() )
{
if (UDPService::isA(srv) || TCPService::isA(srv))
compiler->output << "port { " << str << " } ";
else
{
if (ICMPService::isA(srv))
compiler->output << "icmp-type { " << str << " } ";
else
{
if (ICMP6Service::isA(srv))
compiler->output << "icmp6-type { " << str << " } ";
else
compiler->output << str << " " << endl;
}
}
}
}
}
string PolicyCompiler_pf::PrintRule::_printDstService(Service *srv, bool neg)
{
ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=TCPUDPService::cast(srv)->getDstRangeStart();
int re=TCPUDPService::cast(srv)->getDstRangeEnd();
str << _printPort(rs,re,neg);
}
if (ICMPService::isA(srv) && srv->getInt("type")!=-1)
{
str << srv->getStr("type") << " ";
if (srv->getInt("code")!=-1)
str << "code " << srv->getStr("code") << " ";
}
if (CustomService::isA(srv))
{
str << CustomService::cast(srv)->getCodeForPlatform(
compiler->myPlatformName() ) << " ";
}
if (TagService::isA(srv))
{
str << "tagged " << TagService::constcast(srv)->getCode() << " ";
}
return str.str();
}
string PolicyCompiler_pf::PrintRule::_printTCPFlags(libfwbuilder::TCPService *srv)
{
string str;
if (srv->inspectFlags())
{
if (srv->getTCPFlag(TCPService::URG)) str+="U";
if (srv->getTCPFlag(TCPService::ACK)) str+="A";
if (srv->getTCPFlag(TCPService::PSH)) str+="P";
if (srv->getTCPFlag(TCPService::RST)) str+="R";
if (srv->getTCPFlag(TCPService::SYN)) str+="S";
if (srv->getTCPFlag(TCPService::FIN)) str+="F";
str+="/";
if (srv->getTCPFlagMask(TCPService::URG)) str+="U";
if (srv->getTCPFlagMask(TCPService::ACK)) str+="A";
if (srv->getTCPFlagMask(TCPService::PSH)) str+="P";
if (srv->getTCPFlagMask(TCPService::RST)) str+="R";
if (srv->getTCPFlagMask(TCPService::SYN)) str+="S";
if (srv->getTCPFlagMask(TCPService::FIN)) str+="F";
}
return str;
}
void PolicyCompiler_pf::PrintRule::_printAddr(Address *o,bool )
{
MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o);
if (atrt!=NULL)
{
if (atrt->getSubstitutionTypeName()==DNSName::TYPENAME)
{
compiler->output << atrt->getSourceName() << " ";
return;
}
if (atrt->getSubstitutionTypeName()==AddressTable::TYPENAME)
{
compiler->output << "<" << o->getName() << "> ";
return;
}
assert(atrt==NULL);
}
const InetAddr *addr = o->getAddressPtr();
InetAddr mask;
if (Interface::cast(o)!=NULL)
{
Interface *interface_=Interface::cast(o);
if (interface_->isDyn())
{
compiler->output << "(" << interface_->getName() << ") ";
return;
}
mask = InetAddr(InetAddr::getAllOnes());
} else
{
mask = *(o->getNetmaskPtr());
}
if (o->dimension()==1)
{
mask = InetAddr(InetAddr::getAllOnes());
}
if (addr->isAny() && mask.isAny())
{
compiler->output << "any ";
} else
{
// if (neg) compiler->output << "! ";
compiler->output << addr->toString();
if (!mask.isHostMask())
{
compiler->output << "/" << mask.getLength();
}
compiler->output << " ";
}
}
void PolicyCompiler_pf::PrintRule::_printAddrList(FWObject *grp,bool negflag)
{
compiler->output << "{ ";
for (FWObject::iterator i=grp->begin(); i!=grp->end(); i++)
{
if (i!=grp->begin()) compiler->output << ", ";
FWObject *o= *i;
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Address *s=Address::cast( o );
assert(s);
_printAddr(s , negflag);
}
compiler->output << "} ";
}
void PolicyCompiler_pf::PrintRule::_printSrcAddr(RuleElementSrc *rel)
{
FWObject *o=rel->front();
FWReference *oref = FWReference::cast(o);
if (o && oref!=NULL) o=oref->getPointer();
Address *src= Address::cast(o);
_printNegation(rel);
if (o==NULL)
{
PolicyRule *rule = PolicyRule::cast(rel->getParent());
ostringstream errstr;
errstr << "Broken rule element "
<< rel->getTypeName()
<< " in rule '"
<< rule->getLabel()
<< "' rel->front(): "
<< oref->getPointerId();
compiler->abort(rel->getParent(), errstr.str());
}
if (rel->size()==1 && ! o->getBool("pf_table") )
{
_printAddr( src , rel->getNeg() );
} else
{
if (o->getBool("pf_table"))
{
compiler->output << "<" << o->getName() << "> ";
} else
{
_printAddrList(rel,rel->getNeg());
}
}
}
void PolicyCompiler_pf::PrintRule::_printDstAddr(RuleElementDst *rel)
{
FWObject *o=rel->front();
FWReference *oref = FWReference::cast(o);
if (o && oref!=NULL) o=oref->getPointer();
Address *dst= Address::cast(o);
_printNegation(rel);
if (o==NULL)
{
PolicyRule *rule = PolicyRule::cast(rel->getParent());
ostringstream errstr;
errstr << "Broken rule element "
<< rel->getTypeName()
<< " in rule '"
<< rule->getLabel()
<< "' rel->front(): "
<< oref->getPointerId();
compiler->abort(rel->getParent(), errstr.str());
}
if (rel->size()==1 && ! o->getBool("pf_table") )
{
_printAddr( dst , rel->getNeg());
} else
{
if (o->getBool("pf_table"))
{
compiler->output << "<" << o->getName() << "> ";
} else
{
_printAddrList(rel,rel->getNeg());
}
}
}
void PolicyCompiler_pf::PrintRule::_printNegation(libfwbuilder::RuleElement *rel)
{
if (rel->getNeg())
compiler->output << "! ";
}
PolicyCompiler_pf::PrintRule::PrintRule(const std::string &name) : PolicyRuleProcessor(name)
{
init=true;
}
bool PolicyCompiler_pf::PrintRule::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
FWOptions *ruleopt =rule->getOptionsObject();
string version=compiler->fw->getStr("version");
tmp_queue.push_back(rule);
compiler->output << compiler->printComment(rule, current_rule_label, "#");
// string err = rule->getStr(".error_msg");
// if (!err.empty()) compiler->output << "# " << err << endl;
RuleElementSrc *srcrel=rule->getSrc();
// Address *src =compiler->getFirstSrc(rule); assert(src);
RuleElementDst *dstrel=rule->getDst();
// Address *dst =compiler->getFirstDst(rule); assert(dst);
RuleElementSrv *srvrel=rule->getSrv();
Service *srv =compiler->getFirstSrv(rule); assert(srv);
_printAction(rule);
_printDirection(rule);
_printLogging(rule);
if ( rule->getBool("quick") ) compiler->output << " quick ";
_printInterface(rule);
_printRouteOptions(rule);
_printAF(rule);
_printProtocol(srv);
// cerr << "CP 2" << endl;
compiler->output << " from ";
_printSrcAddr(srcrel);
_printSrcService(srvrel);
compiler->output << " to ";
_printDstAddr(dstrel);
_printDstService(srvrel);
_printTag(rule);
_printUser(rule);
/*
* Dealing with "keep state" and "modulate state" flags
*
* 1. both flags do not apply to deny/reject rules.
* 2. modulate state applies only to TCP services. Since we use splitServices,
* all services in a rule are of the same protocol, therefore we can simply
* check type of srv
*/
if ( ! ruleopt->getBool("stateless") )
{
TCPService *tcpsrv=TCPService::cast(srv);
if (tcpsrv!=NULL && ! tcpsrv->inspectFlags() )
{
// tcp service, no special flag match
// if ( version == "4.x")
if (XMLTools::version_compare(version, "4.0")>=0)
{
if (compiler->getCachedFwOpt()->getBool(
"accept_new_tcp_with_no_syn") )
// v4.x, accept connections opened prior to restart
compiler->output << "flags any ";
// else - no 'flags' option since in 4.x
// 'flags S/SA' is the default
if (ruleopt->getBool("pf_keep_state") )
compiler->output << "keep state ";
} else
{
// v3.x
if ( compiler->getCachedFwOpt()->getBool(
"accept_new_tcp_with_no_syn") )
{
// no 'flags ' option needed
;
} else
// v3.x, stateful
compiler->output << "flags S/SA ";
}
}
/*
* in PF "modulate state", "synproxy state", "keep state" are
* mutually exclusive "keep state" can be used with any
* protocol, while "modulate state" and "synproxy state" can
* only be used with tcp.
*/
bool have_state_option = false;
/*
* First, set explicit state tracking parameter, then add
* stateful tracking options.
*/
if (compiler->getCachedFwOpt()->getBool("pf_synproxy") && tcpsrv!=NULL)
{
compiler->output << "synproxy state ";
have_state_option = true;
} else
{
if ((ruleopt->getBool("pf_modulate_state") ||
compiler->getCachedFwOpt()->getBool("pf_modulate_state")) &&
tcpsrv!=NULL)
{
compiler->output << "modulate state ";
have_state_option = true;
} else
{
/*
* "flags S/SA keep state" is implicit in 4.x
* However see section "1.2. Operational changes" in
* http://www.openbsd.org/faq/upgrade41.html
*
* Quote:
*
* In particular care should be taken with the enc0
* interface, as floating states are a potential problem
* for filtering IPsec traffic: states need to be
* interface bound, to avoid permitting unencrypted
* traffic should isakmpd(8) exit. Therefore all rules on
* the enc0 interface should explicitly set keep state
* (if-bound).
*
* This seems to imply that even though "keep state" is
* the default, it should be explicitly used with enc0
* interface. Adding rule option "Set 'keep state'
* explicitly" to cope with this.
*/
if (XMLTools::version_compare(version, "4.0") < 0 ||
compiler->getCachedFwOpt()->getBool("pf_keep_state"))
{
compiler->output << "keep state ";
have_state_option = true;
}
}
}
/*
* Stateful tracking options. According to the pf.conf manual,
* one of keep state, modulate state, or synproxy state must
* be specified explicitly to apply these options to a rule.
* Using flags need_state_option and have_state_option for that.
*/
QStringList options;
bool need_state_option = false;
if (ruleopt->getInt("pf_rule_max_state")>0)
{
options.push_back(QString("max %1").arg(ruleopt->getInt("pf_rule_max_state")));
need_state_option = true;
}
if (ruleopt->getBool("pf_sloppy_tracker"))
{
options.push_back("sloppy");
need_state_option = true;
}
if (ruleopt->getBool("pf_no_sync"))
{
options.push_back("no-sync");
need_state_option = true;
}
if (ruleopt->getBool("pf_pflow"))
{
options.push_back("pflow");
need_state_option = true;
}
if (ruleopt->getBool("pf_source_tracking"))
{
if (ruleopt->getInt("pf_max_src_nodes") > 0)
{
options.push_back(QString("max-src-nodes %1").arg(
ruleopt->getInt("pf_max_src_nodes")));
need_state_option = true;
}
if (ruleopt->getInt("pf_max_src_states")>0)
{
options.push_back(QString("max-src-states %1").arg(
ruleopt->getInt("pf_max_src_states")));
need_state_option = true;
}
}
bool check_overload_opts = false;
if (ruleopt->getInt("pf_max_src_conn")>0)
{
options.push_back(QString("max-src-conn %1").arg(
ruleopt->getInt("pf_max_src_conn")));
check_overload_opts = true;
need_state_option = true;
}
if (ruleopt->getInt("pf_max_src_conn_rate_num")>0 &&
ruleopt->getInt("pf_max_src_conn_rate_seconds")>0)
{
options.push_back(QString("max-src-conn-rate %1/%2")
.arg(ruleopt->getInt("pf_max_src_conn_rate_num"))
.arg(ruleopt->getInt("pf_max_src_conn_rate_seconds")));
check_overload_opts = true;
need_state_option = true;
}
if (check_overload_opts)
{
QStringList overload_opts;
if (ruleopt->getStr("pf_max_src_conn_overload_table")!="")
overload_opts.push_back(
QString("overload <%1>").arg(
ruleopt->getStr("pf_max_src_conn_overload_table").c_str()));
if (ruleopt->getBool("pf_max_src_conn_flush"))
overload_opts.push_back("flush");
if (ruleopt->getBool("pf_max_src_conn_global"))
overload_opts.push_back("global");
if (overload_opts.size() > 0)
options.push_back(overload_opts.join(" "));
}
if (need_state_option && !have_state_option)
{
compiler->output << "keep state ";
}
// looks like pf.conf syntax requires '(' ')' even if there is
// only one option
if (options.size() > 0) compiler->output << "( ";
compiler->output << options.join(", ").toStdString();
if (options.size() > 0) compiler->output << " )";
} else
{
// stateless rule
if (XMLTools::version_compare(version, "4.0")>=0)
{
// v4.x, stateless rule
compiler->output << "no state ";
}
}
if (rule->getBool("allow_opts")) compiler->output << "allow-opts ";
_printQueue(rule);
_printLabel(rule);
compiler->output << endl;
return true;
}
bool PolicyCompiler_pf::PrintTables::processNext()
{
PolicyCompiler_pf *pf_comp=dynamic_cast<PolicyCompiler_pf*>(compiler);
slurp();
if (tmp_queue.size()==0) return false;
/* print tables */
compiler->output << pf_comp->tables->PrintTables();
return true;
}