/* 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_ipt.h" #include "OSConfigurator_linux24.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/Policy.h" #include "fwbuilder/Network.h" #include "fwbuilder/DNSName.h" #include "fwbuilder/AddressRange.h" #include "fwbuilder/FWObjectDatabase.h" #include "fwbuilder/RuleElement.h" #include "fwbuilder/Policy.h" #include "fwbuilder/Interface.h" #include "fwbuilder/IPv4.h" #include "fwbuilder/Firewall.h" #include "fwbuilder/Resources.h" #include "fwbuilder/AddressTable.h" #include "fwbuilder/UserService.h" #include "fwbuilder/Inet6AddrMask.h" #include "combinedAddress.h" #include "Configlet.h" #include #include #if __GNUC__ > 3 || \ (__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || (__GNUC_MINOR__ == 2 ) ) ) || \ _MSC_VER # include #else # include #endif #include #include #include #include #include #include using namespace libfwbuilder; using namespace fwcompiler; using namespace std; /** *----------------------------------------------------------------------- * Methods for printing */ /* * Prints single --option with argument and negation "!" * taking into account the change that happened in iptables 1.4.3.1 * that causes warning * Using intrapositioned negation (`--option ! this`) is deprecated in favor of extrapositioned (`! --option this`). */ string PolicyCompiler_ipt::PrintRule::_printSingleOptionWithNegation( const string &option, RuleElement *rel, const string &arg) { ostringstream ostr; if (XMLTools::version_compare(version, "1.4.3")>=0) { ostr << _printSingleObjectNegation(rel); ostr << option << " "; ostr << arg << " "; } else { ostr << option << " "; ostr << _printSingleObjectNegation(rel); ostr << arg << " "; } return ostr.str(); } void PolicyCompiler_ipt::PrintRule::initializeMinusNTracker() { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); for (list::const_iterator i = PolicyCompiler_ipt::getStandardChains().begin(); i != PolicyCompiler_ipt::getStandardChains().end(); ++i) { (*(ipt_comp->minus_n_commands))[*i] = true; } minus_n_tracker_initialized = true; } /* * check and create new chain if needed */ string PolicyCompiler_ipt::PrintRule::_createChain(const string &chain) { string res; PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); if (!minus_n_tracker_initialized) initializeMinusNTracker(); if ( ipt_comp->minus_n_commands->count(chain)==0 ) { res = string((ipt_comp->ipv6) ? "$IP6TABLES -N " : "$IPTABLES -N ") + chain; if (ipt_comp->my_table != "filter") res += " -t " + ipt_comp->my_table; res += "\n"; (*(ipt_comp->minus_n_commands))[chain] = true; } return res; } string PolicyCompiler_ipt::PrintRule::_startRuleLine() { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); string res = (ipt_comp->ipv6) ? "$IP6TABLES " : "$IPTABLES "; if (ipt_comp->my_table != "filter") res += "-t " + ipt_comp->my_table + " "; res += "-A "; return res; } string PolicyCompiler_ipt::PrintRule::_endRuleLine() { return string("\n"); } string PolicyCompiler_ipt::PrintRule::_printRuleLabel(PolicyRule *rule) { ostringstream res; bool nocomm = Resources::os_res[compiler->fw->getStr("host_OS")]-> Resources::getResourceBool( "/FWBuilderResources/Target/options/suppress_comments"); // TODO: convert this into virtual function PolicyCompiler_ipt::printComment() string rl=rule->getLabel(); if (rl != current_rule_label) { if (!compiler->inSingleRuleCompileMode()) { if (!nocomm) { res << "# " << endl; res << "# Rule " << rl << endl; res << "# " << endl; } res << "echo " << _quote(string("Rule ")+rl) << endl; res << "# " << endl; } /* do not put comment in the script if it is intended for linksys */ if (!nocomm || compiler->inSingleRuleCompileMode()) { QStringList comm = QString(rule->getComment().c_str()).split("\n", QString::SkipEmptyParts); foreach(QString line, comm) { res << "# " << line.toStdString() << endl; } //res << "# " << endl; } } current_rule_label = rl; string err = rule->getStr(".error_msg"); if (!err.empty()) res << "# " << err << endl; return res.str(); } /** *----------------------------------------------------------------------- */ string PolicyCompiler_ipt::PrintRule::_printChain(PolicyRule *rule) { string s = rule->getStr("ipt_chain"); if (s.empty()) s = "UNKNOWN"; // check chain name length per bug report #2507239 if (s.length() > 30) { ostringstream str; str << "Chain name '" << s << "' "; str << "is longer than 30 characters. Rule " << rule->getLabel(); compiler->abort(rule, str.str()); } s= s + " "; return s; } string PolicyCompiler_ipt::PrintRule::_printModules(PolicyRule *rule) { std::ostringstream ostr; string target=rule->getStr("ipt_target"); if (target.empty()) target="UNKNOWN"; FWOptions *ruleopt =rule->getOptionsObject(); int lim = 0; /* * Here is what do we do with limits: * * Limit set globally in 'Firewall' tab of the firewall dialog * applies only to logging * * Limit set in the rule options dialog applies only to this * rule's target. * * this is so as of 1.0.11 ( 28/06/03 ) --vk */ if (target=="LOG") { FWOptions *compopt=compiler->getCachedFwOpt(); if ((lim=compopt->getInt("limit_value"))>0) { ostr << " -m limit --limit " << lim; string ls=compopt->getStr("limit_suffix"); if (!ls.empty()) ostr << ls; int lb=compopt->getInt("limit_burst"); if (lb>0) ostr << " --limit-burst " << lb; } } else { if (ruleopt!=NULL && (lim=ruleopt->getInt("limit_value"))>0) { if (ruleopt->getBool("limit_value_not")) ostr << " -m limit \\! --limit " << lim; else ostr << " -m limit --limit " << lim; string ls=ruleopt->getStr("limit_suffix"); if (!ls.empty()) ostr << ls; int lb=ruleopt->getInt("limit_burst"); if (lb>0) ostr << " --limit-burst " << lb; } } if (ruleopt!=NULL && (lim=ruleopt->getInt("connlimit_value"))>0) { if (ruleopt->getBool("connlimit_above_not")) ostr << " -m connlimit \\! --connlimit-above " << lim; else ostr << " -m connlimit --connlimit-above " << lim; int ml=ruleopt->getInt("connlimit_masklen"); if (ml>0) ostr << " --connlimit-mask " << ml; } if (ruleopt!=NULL && (lim=ruleopt->getInt("hashlimit_value"))>0) { string module_name = "hashlimit"; if (ruleopt->getBool("hashlimit_dstlimit")) module_name = "dstlimit"; ostr << " -m " << module_name << " --" << module_name << " " << lim; string ls = ruleopt->getStr("hashlimit_suffix"); if (!ls.empty()) ostr << ls; int lb=ruleopt->getInt("hashlimit_burst"); if (lb>0) ostr << " --" << module_name << "-burst " << lb; ls = ruleopt->getStr("hashlimit_mode"); if (ls.empty()) { /* syntax "--hashlimit-mode srcip,srcport " (i.e. with options separated by commas) tested with iptables 1.3.6 */ QStringList opts; bool f; f = ruleopt->getBool("hashlimit_mode_srcip"); if (f) opts.push_back("srcip"); f = ruleopt->getBool("hashlimit_mode_dstip"); if (f) opts.push_back("dstip"); f = ruleopt->getBool("hashlimit_mode_srcport"); if (f) opts.push_back("srcport"); f = ruleopt->getBool("hashlimit_mode_dstport"); if (f) opts.push_back("dstport"); if (!opts.isEmpty()) ostr << " --" << module_name << "-mode " << opts.join(",").toStdString(); } else // hashlimit_mode is v2.1 option. In v3 we have options // hashlimit_mode_srcip // hashlimit_mode_dstip // hashlimit_mode_srcport // hashlimit_mode_dstport ostr << " --" << module_name << "-mode " << ls; string hl_name = ruleopt->getStr("hashlimit_name"); if (hl_name.empty()) { std::ostringstream hn; hn << "htable_rule_" << rule->getPosition(); hl_name = hn.str(); } ostr << " --" << module_name << "-name " << hl_name; int arg = ruleopt->getInt("hashlimit_size"); if (arg>0) ostr << " --" << module_name << "-htable-size " << arg; arg = ruleopt->getInt("hashlimit_max"); if (arg>0) ostr << " --" << module_name << "-htable-max " << arg; arg = ruleopt->getInt("hashlimit_expire"); if (arg>0) ostr << " --" << module_name << "-htable-expire " << arg; arg = ruleopt->getInt("hashlimit_gcinterval"); if (arg>0) ostr << " --" << module_name << "-htable-gcinterval " << arg; } return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printTarget(PolicyRule *rule) { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); std::ostringstream ostr; string target=rule->getStr("ipt_target"); if (target.empty()) target="UNKNOWN"; FWOptions *ruleopt =rule->getOptionsObject(); if (target=="CUSTOM") { ostr << " " << ruleopt->getStr("custom_str"); return ostr.str(); } // there is no ULOG for ip6tables yet if (!ipt_comp->ipv6 && compiler->getCachedFwOpt()->getBool("use_ULOG") && target=="LOG") target="ULOG"; ostr << " -j " << target << " "; if (target=="REJECT") ostr << _printActionOnReject(rule); if (target=="LOG" || target=="ULOG") ostr << _printLogParameters(rule); if (target=="MARK") { // ostr << " --set-mark " << ruleopt->getStr("tagvalue"); ostr << " --set-mark " << rule->getTagValue(); } if (target=="CONNMARK") { ostr << ruleopt->getStr("CONNMARK_arg"); } if (target=="CLASSIFY") { ostr << " --set-class " << ruleopt->getStr("classify_str"); } if (target=="ROUTE") { string a; a = ruleopt->getStr("ipt_iif"); if (!a.empty()) ostr << " --iif " << a; a = ruleopt->getStr("ipt_oif"); if (!a.empty()) ostr << " --oif " << a; a = ruleopt->getStr("ipt_gw"); if (!a.empty()) ostr << " --gw " << a; bool c = ruleopt->getBool("ipt_continue"); if (c) ostr << " --continue"; c = ruleopt->getBool("ipt_tee"); if (c) ostr << " --tee"; } return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printMultiport(PolicyRule *rule) { RuleElementSrv *srvrel=rule->getSrv(); string s; if(srvrel->size()>1 && rule->getBool("ipt_multiport")) s= " -m multiport "; return s; } string PolicyCompiler_ipt::PrintRule::_printDirectionAndInterface(PolicyRule *rule) { std::ostringstream ostr; string iface_name = rule->getInterfaceStr(); if (iface_name.empty() || iface_name=="nil" ) return ""; RuleElementItf *itfrel = rule->getItf(); /* if interface name ends with '*', this is a wildcard * interface. Iptables supports wildcard interfaces but uses '+' as a * wildcard symbol */ string::size_type n; if ( (n=iface_name.find("*"))!=string::npos) iface_name[n]='+'; Interface *rule_iface = Interface::cast(compiler->dbcopy->findInIndex(rule->getInterfaceId())); if (rule_iface && rule_iface->isBridgePort() && (version.empty() || XMLTools::version_compare(version, "1.3.0")>=0)) { if (rule->getDirection()==PolicyRule::Inbound) ostr << " -m physdev --physdev-in " << iface_name; if (rule->getDirection()==PolicyRule::Outbound) ostr << " -m physdev --physdev-out " << iface_name; } else { if (rule->getDirection()==PolicyRule::Inbound) ostr << _printSingleOptionWithNegation(" -i", itfrel, iface_name); if (rule->getDirection()==PolicyRule::Outbound) ostr << _printSingleOptionWithNegation(" -o", itfrel, iface_name); } // if (rule->getDirection()==PolicyRule::Both) // compiler->output << "-i " << rule_iface->getName() // << " -o " << rule_iface->getName(); ostr << " "; return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printActionOnReject(PolicyRule *rule) { std::ostringstream str; PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); // RuleElementSrv *srvrel=rule->getSrv(); Service *srv = compiler->getFirstSrv(rule); assert(srv); string s = ipt_comp->getActionOnReject(rule); if (!s.empty()) { if (ipt_comp->isActionOnRejectTCPRST(rule)) str << " --reject-with tcp-reset"; if (s.find("ICMP")!=string::npos) { if (ipt_comp->ipv6) { if (s.find("unreachable")!=string::npos) { if (s.find("net")!=string::npos || s.find("host")!=string::npos) str << " --reject-with icmp6-addr-unreachable"; if (s.find("port")!=string::npos || s.find("proto")!=string::npos) str << " --reject-with icmp6-port-unreachable"; } if (s.find("prohibited")!=string::npos) { str << " --reject-with icmp6-adm-prohibited"; } } else { if (s.find("unreachable")!=string::npos) { if (s.find("net")!=string::npos) str << " --reject-with icmp-net-unreachable"; if (s.find("host")!=string::npos) str << " --reject-with icmp-host-unreachable"; if (s.find("port")!=string::npos) str << " --reject-with icmp-port-unreachable"; if (s.find("proto")!=string::npos) str << " --reject-with icmp-proto-unreachable"; } if (s.find("prohibited")!=string::npos) { if (s.find("net")!=string::npos) str << " --reject-with icmp-net-prohibited"; if (s.find("host")!=string::npos) str << " --reject-with icmp-host-prohibited"; if (XMLTools::version_compare(version, "1.2.9")>=0 && s.find("admin")!=string::npos) str << " --reject-with icmp-admin-prohibited"; } } } } str << " "; return str.str(); } string PolicyCompiler_ipt::PrintRule::_printGlobalLogParameters() { return _printLogParameters(NULL); } string PolicyCompiler_ipt::PrintRule::_printLogPrefix(const string &rule_num, const string &action, const string &interf, const string &chain, const string &ruleset, const string& , const string &prefix) { string s = prefix; /* deal with our logging macros: * %N - rule number ('2', or '2/3' for rule in a branch) * %A - action * %I - interface name * %C - chain name * %R - ruleset name */ string::size_type n; if ((n=s.find("%N"))!=string::npos ) { s.replace(n, 2, rule_num); } if ((n=s.find("%A"))!=string::npos ) { s.replace(n, 2, action); } if ((n=s.find("%I"))!=string::npos ) { s.replace(n, 2, interf); } if ((n=s.find("%C"))!=string::npos ) { s.replace(n, 2, chain); } if ((n=s.find("%R"))!=string::npos ) { s.replace(n, 2, ruleset); } if (s.length()>29) { compiler->warning( "Log prefix has been truncated to 29 characters"); s=s.substr(0,29); } return _quote( s ); } string PolicyCompiler_ipt::PrintRule::_printLogPrefix(PolicyRule *rule, const string &prefix) { FWObject *ruleset = rule->getParent(); char action[64]; strncpy(action,rule->getStr("stored_action").c_str(),sizeof(action)); for (char *cptr=action; *cptr; cptr++) *cptr=toupper(*cptr); string rule_iface = rule->getInterfaceStr(); if (rule_iface=="") rule_iface = "global"; std::ostringstream s1; int pos=rule->getPosition(); // parent_rule_num is set by processor "Branching" for branch rules string ppos = rule->getStr("parent_rule_num"); if (ppos != "") s1 << ppos << "/"; s1 << pos; return _printLogPrefix(s1.str(), action, rule_iface, rule->getStr("ipt_chain"), ruleset->getName(), rule->getLabel(), prefix); } string PolicyCompiler_ipt::PrintRule::_printLogParameters(PolicyRule *rule) { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); std::ostringstream str; string s; FWOptions *ruleopt = (rule!=NULL) ? rule->getOptionsObject() : compiler->getCachedFwOpt(); // there is no ULOG for ip6tables yet bool use_ulog = (!ipt_comp->ipv6 && compiler->getCachedFwOpt()->getBool("use_ULOG")); if (use_ulog) { s=ruleopt->getStr("ulog_nlgroup"); if (s.empty()) s=compiler->getCachedFwOpt()->getStr("ulog_nlgroup"); if (!s.empty()) str << " --ulog-nlgroup " << s; s=ruleopt->getStr("log_prefix"); if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_prefix"); if (!s.empty()) str << " --ulog-prefix " << _printLogPrefix(rule, s); int r=compiler->getCachedFwOpt()->getInt("ulog_cprange"); if (r!=0) str << " --ulog-cprange " << r << " "; r=compiler->getCachedFwOpt()->getInt("ulog_qthreshold"); if (r!=0) str << " --ulog-qthreshold " << r << " "; } else { bool numeric_levels; numeric_levels=compiler->getCachedFwOpt()->getBool("use_numeric_log_levels"); s=ruleopt->getStr("log_level"); if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_level"); if (!s.empty()) { if ( numeric_levels ) { if (s=="alert") s="1"; if (s=="crit") s="2"; if (s=="error") s="3"; if (s=="warning") s="4"; if (s=="notice") s="5"; if (s=="info") s="6"; if (s=="debug") s="7"; } str << " --log-level " << s; } s=ruleopt->getStr("log_prefix"); if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_prefix"); if (!s.empty()) str << " --log-prefix " << _printLogPrefix(rule, s); if (ruleopt->getBool("log_tcp_seq") || compiler->getCachedFwOpt()->getBool("log_tcp_seq")) str << " --log-tcp-sequence "; if (ruleopt->getBool("log_tcp_opt") || compiler->getCachedFwOpt()->getBool("log_tcp_opt")) str << " --log-tcp-options "; if (ruleopt->getBool("log_ip_opt") || compiler->getCachedFwOpt()->getBool("log_ip_opt")) str << " --log-ip-options "; } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printLimit(libfwbuilder::PolicyRule *rule) { std::ostringstream str; string s; int l, lb; FWOptions *ruleopt =rule->getOptionsObject(); FWOptions *compopt =compiler->getCachedFwOpt(); if ( (ruleopt!=NULL && (l=ruleopt->getInt("limit_value"))>0) || (l=compopt->getInt("limit_value"))>0 ) { str << " -m limit --limit " << l; if (ruleopt!=NULL) s=ruleopt->getStr("limit_suffix"); if (s.empty()) s=compopt->getStr("limit_suffix"); if (!s.empty()) str << s; lb=-1; if (ruleopt!=NULL) lb=ruleopt->getInt("limit_burst"); if (lb<0) lb=compopt->getInt("limit_burst"); if (lb>0) str << " --limit-burst " << lb; } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printProtocol(Service *srv) { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); string s; // CustomService returns protocol name starting with v3.0.4 // However CustomService can return protocol name "any", which we should // just skip. if (CustomService::isA(srv)) { // check if the code string for this custom service already includes // "-p proto" fragment string code = CustomService::cast(srv)->getCodeForPlatform( compiler->myPlatformName()); std::size_t minus_p = code.find("-p "); if (minus_p != string::npos) return ""; string pn = srv->getProtocolName(); if (pn == "any") return ""; } if (!srv->isAny() && !TagService::isA(srv) && !UserService::isA(srv)) { string pn = srv->getProtocolName(); if (pn=="ip" || pn=="any") pn = "all"; if (ipt_comp->ipv6) { if (ICMPService::isA(srv)) { compiler->abort( "Can not use ICMPService in ipv6 rule; " "use ICMP6Service object instead"); } if (ICMP6Service::isA(srv)) { s = "-p ipv6-icmp "; if (srv->getInt("type")!=-1 && (version.empty() || XMLTools::version_compare(version, "1.3.0")>=0)) s += " -m icmp6"; } else { // ip6tables issues warning for commands using "-p all" // Warning: never matched protocol: all. use exension match instead // Skip "-p all" if ipv6 if (pn!="all") s = "-p " + pn + " "; } } else { if (ICMP6Service::isA(srv)) { compiler->abort( "Can not use ICMP6Service in ipv4 rule; " "use ICMPService object instead"); } if (ICMPService::isA(srv)) { s = "-p icmp "; if (version.empty() || XMLTools::version_compare(version, "1.2.9")>=0) s += " -m icmp "; } else { s = "-p " + pn + " "; } } if (pn == "tcp") s += "-m tcp "; if (pn == "udp") s += "-m udp "; } return s; } string PolicyCompiler_ipt::PrintRule::_printPorts(int rs,int re) { std::ostringstream str; compiler->normalizePortRange(rs,re); if (rs>0 || re>0) { if (rs==re) str << rs; else if (rs==0 && re!=0) str << ":" << re; else str << rs << ":" << re; } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printSrcPorts(Service *srv) { std::ostringstream str; if (TCPService::isA(srv) || UDPService::isA(srv)) { int rs = TCPUDPService::cast(srv)->getSrcRangeStart(); int re = TCPUDPService::cast(srv)->getSrcRangeEnd(); str << _printPorts(rs,re); } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printDstPorts(Service *srv) { std::ostringstream str; if (TCPService::isA(srv) || UDPService::isA(srv)) { int rs = TCPUDPService::cast(srv)->getDstRangeStart(); int re = TCPUDPService::cast(srv)->getDstRangeEnd(); str << _printPorts(rs,re); } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printICMP(ICMPService *srv) { std::ostringstream str; if (ICMPService::cast(srv) && srv->getInt("type")!=-1) { str << srv->getStr("type"); if (srv->getInt("code")!=-1) str << "/" << srv->getStr("code") << " "; } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printIP(IPService *srv, PolicyRule *rule) { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); std::ostringstream str; if (srv->getBool("fragm") || srv->getBool("short_fragm")) { if (ipt_comp->ipv6) str << " -m frag --fragmore"; else str << " -f "; } string tos = srv->getTOSCode(); string dscp = srv->getDSCPCode(); if (!tos.empty()) str << " -m tos --tos " << tos; else if (!dscp.empty()) { if (dscp.find("BE")==0 || dscp.find("EF")==0 || dscp.find("AF")==0 || dscp.find("CS")==0) str << " -m dscp --dscp-class " << dscp; else str << " -m dscp --dscp " << dscp; } if (srv->hasIpOptions()) { if (!ipt_comp->ipv6) { str << " -m ipv4options "; if (srv->getBool("any_opt")) str << " --any-opt"; else { if (srv->getBool("lsrr")) str << " --lsrr"; if (srv->getBool("ssrr")) str << " --ssrr"; if (srv->getBool("rr")) str << " --rr"; if (srv->getBool("ts")) str << " --ts"; if (srv->getBool("rtralt")) str << " --ra"; } } else { compiler->abort( rule, "IP options match is not supported for IPv6."); } } return str.str(); } string PolicyCompiler_ipt::PrintRule::_printTCPFlags(libfwbuilder::TCPService *srv) { string str; if (srv->inspectFlags()) { TCPService::TCPFlag f1[2]={ TCPService::SYN }; TCPService::TCPFlag f2[7]={ TCPService::URG, TCPService::ACK, TCPService::PSH, TCPService::RST, TCPService::SYN, TCPService::FIN }; std::set none; std::set syn( f1, f1+1 ); std::set all_masks( f2 , f2+6 ); if (srv->getAllTCPFlags()==syn && srv->getAllTCPFlagMasks()==all_masks) str=" --tcp-flags SYN,RST,ACK SYN "; else { str=" --tcp-flags "; bool first=true; if (srv->getAllTCPFlagMasks()==all_masks) str+="ALL"; else { if (srv->getTCPFlagMask(TCPService::URG)) { if (!first) str+=","; str+="URG"; first=false; } if (srv->getTCPFlagMask(TCPService::ACK)) { if (!first) str+=","; str+="ACK"; first=false; } if (srv->getTCPFlagMask(TCPService::PSH)) { if (!first) str+=","; str+="PSH"; first=false; } if (srv->getTCPFlagMask(TCPService::RST)) { if (!first) str+=","; str+="RST"; first=false; } if (srv->getTCPFlagMask(TCPService::SYN)) { if (!first) str+=","; str+="SYN"; first=false; } if (srv->getTCPFlagMask(TCPService::FIN)) { if (!first) str+=","; str+="FIN"; first=false; } } str+=" "; if (srv->getAllTCPFlags()==none) str+="NONE"; else { first=true; if (srv->getTCPFlag(TCPService::URG)) { if (!first) str+=","; str+="URG"; first=false; } if (srv->getTCPFlag(TCPService::ACK)) { if (!first) str+=","; str+="ACK"; first=false; } if (srv->getTCPFlag(TCPService::PSH)) { if (!first) str+=","; str+="PSH"; first=false; } if (srv->getTCPFlag(TCPService::RST)) { if (!first) str+=","; str+="RST"; first=false; } if (srv->getTCPFlag(TCPService::SYN)) { if (!first) str+=","; str+="SYN"; first=false; } if (srv->getTCPFlag(TCPService::FIN)) { if (!first) str+=","; str+="FIN"; first=false; } } } } return str; } /* * we made sure that all services in rel represent the same protocol */ string PolicyCompiler_ipt::PrintRule::_printSrcService(RuleElementSrv *rel) { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); std::ostringstream ostr; /* 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); if (rel->size()==1) { if (UDPService::isA(srv) || TCPService::isA(srv) || TagService::isA(srv)) { string str=_printSrcPorts( srv ); if (! str.empty() ) { ostr << _printSingleOptionWithNegation(" --sport", rel, str); } } } else { /* use multiport */ string str; bool first=true; 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); if (UDPService::isA(srv) || TCPService::isA(srv)) { if (!first) str+=","; str+= _printSrcPorts( s ); if (!str.empty()) first=false; } } if ( !str.empty() ) { if (ipt_comp->newIptables(version)) ostr << " --sports "; else ostr << " --source-port "; ostr << str << " "; } } return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printDstService(RuleElementSrv *rel) { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); std::ostringstream ostr; FWObject *o=rel->front(); if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer(); Service *srv= Service::cast(o); if (rel->size()==1) { if (UDPService::isA(srv) || TCPService::isA(srv)) { string str=_printDstPorts( srv ); if (! str.empty() ) { ostr << _printSingleOptionWithNegation(" --dport", rel, str); } } if (TCPService::isA(srv)) { string str=_printTCPFlags(TCPService::cast(srv)); if (!str.empty()) { ostr << _printSingleOptionWithNegation("", rel, str); } } if (ICMPService::isA(srv) || ICMP6Service::isA(srv)) { string icmp_type_str = (ipt_comp->ipv6) ? " --icmpv6-type" : " --icmp-type"; string str = _printICMP( ICMPService::cast(srv) ); if (str.empty() ) { // module icmp6 does not like "--icmp6-type any" if ((version.empty() || XMLTools::version_compare(version, "1.2.6")>0) && !ipt_comp->ipv6) ostr << icmp_type_str << " any "; } else { ostr << _printSingleOptionWithNegation(icmp_type_str, rel, str); } } if (IPService::isA(srv)) { string str = _printIP(IPService::cast(srv), PolicyRule::cast(rel->getParent())); if (! str.empty() ) { ostr << _printSingleObjectNegation(rel) << str << " "; } } if (CustomService::isA(srv)) { ostr << _printSingleObjectNegation(rel) << " " << CustomService::cast(srv)->getCodeForPlatform( compiler->myPlatformName() ) << " "; } if (TagService::isA(srv)) { ostr << "-m mark " << _printSingleObjectNegation(rel) << "--mark " << TagService::constcast(srv)->getCode() << " "; } if (UserService::isA(srv)) { ostr << "-m owner " << _printSingleObjectNegation(rel) << "--uid-owner " << UserService::cast(srv)->getUserId() << " "; } } else { /* use multiport */ 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); if (UDPService::isA(srv) || TCPService::isA(srv)) { string str1 = _printDstPorts( s ); if (str!="" && str1!="") str+=","; str+=str1; } } if ( !str.empty() ) { if (ipt_comp->newIptables(version)) ostr << " --dports "; else ostr << " --destination-port "; ostr << str << " "; } } return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printSrcAddr(RuleElement *rel, Address *o) { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); string res; if (AddressRange::cast(o)!=NULL) { AddressRange *ar = AddressRange::cast(o); const InetAddr &range_start = ar->getRangeStart(); const InetAddr &range_end = ar->getRangeEnd(); if (range_start != range_end) { if (!have_m_iprange) { res = "-m iprange "; have_m_iprange = true; } res += _printSingleObjectNegation(rel) + "--src-range "; res += range_start.toString() + "-" + range_end.toString() + " "; } else res += "-s " + range_start.toString() + " "; return res; } MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o); if (atrt!=NULL && atrt->getSubstitutionTypeName()==AddressTable::TYPENAME && ipt_comp->can_use_module_set) { string set_match = "--set " + o->getName() + " src"; ostringstream ostr; ostr << "-m set " << _printSingleOptionWithNegation("", rel, set_match); return ostr.str(); } return _printSingleOptionWithNegation(" -s", rel, _printAddr(o)); } string PolicyCompiler_ipt::PrintRule::_printDstAddr(RuleElement *rel, Address *o) { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); string res; if (AddressRange::cast(o)!=NULL) { AddressRange *ar = AddressRange::cast(o); const InetAddr &range_start = ar->getRangeStart(); const InetAddr &range_end = ar->getRangeEnd(); if (range_start != range_end) { if (!have_m_iprange) { res = "-m iprange "; have_m_iprange = true; } res += _printSingleObjectNegation(rel) + "--dst-range "; res += range_start.toString() + "-" + range_end.toString() + " "; } else res += "-d " + range_start.toString() + " "; return res; } MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o); if (atrt!=NULL && atrt->getSubstitutionTypeName()==AddressTable::TYPENAME && ipt_comp->can_use_module_set) { string set_match = "--set " + o->getName() + " dst"; ostringstream ostr; ostr << "-m set " << _printSingleOptionWithNegation("", rel, set_match); return ostr.str(); } return _printSingleOptionWithNegation(" -d", rel, _printAddr(o)); } string PolicyCompiler_ipt::PrintRule::_printAddr(Address *o) { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); std::ostringstream ostr; MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o); if (atrt!=NULL) { if (atrt->getSubstitutionTypeName()==AddressTable::TYPENAME) { ostr << "$" << ipt_comp->getAddressTableVarName(atrt) << " "; return ostr.str(); } if (atrt->getSubstitutionTypeName()==DNSName::TYPENAME) { return atrt->getSourceName(); } // at this time we only support two types of MultiAddress // objects: AddressTable and DNSName. Both should be converted // to MultiAddressRunTime at this point. If we get some other // kind of MultiAddressRunTime object, we do not know what to do // with it so we stop. assert(atrt==NULL); } if (Interface::cast(o)!=NULL) { Interface *iface=Interface::cast(o); if (iface->isDyn()) ostr << "$" << ipt_comp->getInterfaceVarName(iface, ipt_comp->ipv6) << " "; return ostr.str(); } const InetAddr *addr = o->getAddressPtr(); const InetAddr *mask = o->getNetmaskPtr(); if (addr==NULL) { compiler->warning( string("Empty inet address in object ") + o->getName() + "(" + FWObjectDatabase::getStringId(o->getId()) + ")"); return ostr.str(); } // Note that mask can be NULL, for example if o is AddressRange. if (addr->isAny() && (mask==NULL || mask->isAny())) { ostr << "0/0 "; } else { ostr << addr->toString(); if (Interface::cast(o)==NULL && Address::cast(o)->dimension() > 1 && !mask->isHostMask()) { ostr << "/" << mask->getLength(); } ostr << " "; } return ostr.str(); } string PolicyCompiler_ipt::PrintRule::_printSingleObjectNegation( RuleElement *rel) { if (rel->getBool("single_object_negation")) return "! "; else return ""; } string PolicyCompiler_ipt::PrintRule::_printTimeInterval(PolicyRule *r) { std::ostringstream ostr; RuleElementInterval* ri=r->getWhen(); if (ri==NULL || ri->isAny()) return ""; std::map daysofweek; daysofweek[0]="Sun"; daysofweek[1]="Mon"; daysofweek[2]="Tue"; daysofweek[3]="Wed"; daysofweek[4]="Thu"; daysofweek[5]="Fri"; daysofweek[6]="Sat"; daysofweek[7]="Sun"; int smin, shour, sday, smonth, syear, sdayofweek; int emin, ehour, eday, emonth, eyear, edayofweek; string days_of_week; Interval *interval = compiler->getFirstWhen(r); assert(interval!=NULL); interval->getStartTime( &smin, &shour, &sday, &smonth, &syear, &sdayofweek); interval->getEndTime( &emin, &ehour, &eday, &emonth, &eyear, &edayofweek); days_of_week = interval->getDaysOfWeek(); if (shour<0) shour=0; if (smin<0) smin=0; if (ehour<0) ehour=23; if (emin<0) emin=59; ostr << "-m time "; bool use_timestart_timestop = true; if (XMLTools::version_compare(version, "1.4.0")>=0) { /* in 1.4.0 date format has changed, it is now ISO 8601 * http://www.w3.org/TR/NOTE-datetime * * --datestart YYYY[-MM[-DD[Thh[:mm[:ss]]]]] * * --datestop YYYY[-MM[-DD[Thh[:mm[:ss]]]]] */ if (sday>0 && smonth>0 && syear>0) { ostr << "--datestart " << setw(2) << setfill('0') << syear << "-" << setw(2) << setfill('0') << smonth << "-" << setw(2) << setfill('0') << sday << "T" << setw(2) << setfill('0') << shour << ":" << setw(2) << setfill('0') << smin << ":00 "; use_timestart_timestop = false; } if (eday>0 && emonth>0 && eyear>0) { ostr << "--datestop " << setw(2) << setfill('0') << eyear << "-" << setw(2) << setfill('0') << emonth << "-" << setw(2) << setfill('0') << eday << "T" << setw(2) << setfill('0') << ehour << ":" << setw(2) << setfill('0') << emin << ":00 "; use_timestart_timestop = false; } if (use_timestart_timestop) { ostr << " --timestart " << setw(2) << setfill('0') << shour << ":" << setw(2) << setfill('0') << smin << " "; ostr << " --timestop " << setw(2) << setfill('0') << ehour << ":" << setw(2) << setfill('0') << emin << " "; } if (!days_of_week.empty() && days_of_week != "0,1,2,3,4,5,6") { ostr << " --weekdays "; istringstream istr(days_of_week); bool first= true; while (!istr.eof()) { if (!first) ostr << ','; first = false; int d; istr >> d; ostr << daysofweek[d]; char sep; istr >> sep; } } } else { /* "old" iptables time module TIME v1.2.11 options: --timestart value --timestop value --days listofdays timestart value : HH:MM timestop value : HH:MM listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri. Case sensitive */ ostr << " --timestart " << setw(2) << setfill('0') << shour << ":" << setw(2) << setfill('0') << smin << " "; ostr << " --timestop " << setw(2) << setfill('0') << ehour << ":" << setw(2) << setfill('0') << emin << " "; if (!days_of_week.empty() && days_of_week != "0,1,2,3,4,5,6") { ostr << " --days "; istringstream istr(days_of_week); bool first= true; while (!istr.eof()) { if (!first) ostr << ','; first = false; int d; istr >> d; ostr << daysofweek[d]; char sep; istr >> sep; } } } return ostr.str(); } PolicyCompiler_ipt::PrintRule::PrintRule(const std::string &name) : PolicyRuleProcessor(name) { init = true; print_once_on_top = true; // use delayed initialization for ipt_comp->minus_n_commands // because it requires pointer to the compiler which has not been // initialized yet when this constructor is executed. minus_n_tracker_initialized = false; } /* * Initialize some internal variables. Need to do this in a separate * method because pointer to the compiler object is set by * RuleProcessor::setContext and is not available in constructor. */ void PolicyCompiler_ipt::PrintRule::initialize() { // retrieve and save version for _printSingleOptionWithNegation and others version = compiler->fw->getStr("version"); } bool PolicyCompiler_ipt::PrintRule::processNext() { PolicyCompiler_ipt *ipt_comp=dynamic_cast(compiler); PolicyRule *rule =getNext(); if (rule==NULL) return false; string chain = rule->getStr("ipt_chain"); if (ipt_comp->chain_usage_counter[chain] > 0) { tmp_queue.push_back(rule); compiler->output << _printRuleLabel(rule); compiler->output << _createChain(rule->getStr("ipt_chain")); compiler->output << _createChain(rule->getStr("ipt_target")); compiler->output << dynamic_cast( compiler->osconfigurator)->printRunTimeWrappers( rule, PolicyRuleToString(rule), ipt_comp->ipv6); } return true; } string PolicyCompiler_ipt::PrintRule::PolicyRuleToString(PolicyRule *rule) { FWOptions *ruleopt = rule->getOptionsObject(); FWObject *ref; RuleElementSrc *srcrel=rule->getSrc(); ref=srcrel->front(); Address *src=Address::cast(FWReference::cast(ref)->getPointer()); if(src==NULL) compiler->abort(rule, string("Broken SRC in ") + rule->getLabel()); RuleElementDst *dstrel=rule->getDst(); ref=dstrel->front(); Address *dst=Address::cast(FWReference::cast(ref)->getPointer()); if(dst==NULL) compiler->abort(rule, string("Broken DST in ") + rule->getLabel()); RuleElementSrv *srvrel=rule->getSrv(); ref=srvrel->front(); Service *srv=Service::cast(FWReference::cast(ref)->getPointer()); if(srv==NULL) compiler->abort(rule, string("Broken SRV in ") + rule->getLabel()); std::ostringstream command_line; have_m_iprange = false; command_line << _startRuleLine(); command_line << _printChain(rule); command_line << _printDirectionAndInterface(rule); command_line << _printProtocol(srv); command_line << _printMultiport(rule); if (!src->isAny()) { if (physAddress::isA(src) || combinedAddress::isA(src)) { string physaddress = ""; if (physAddress::isA(src)) { physaddress = physAddress::cast(src)->getPhysAddress(); if (physaddress.empty()) { compiler->warning( rule, "Empty MAC address in rule"); physaddress = "00:00:00:00:00:00"; } } if (combinedAddress::isA(src)) physaddress = combinedAddress::cast(src)->getPhysAddress(); /* physAddress component of combinedAddress can be empty. For example * this happens when an object with both IP and MAC addresses is found * in "source" and rule is determined to go into OUTPUT chain. On the * other hand, if physAddress object has no MAC address, it is always * an error. */ if (!physaddress.empty()) { command_line << " -m mac"; command_line << _printSingleOptionWithNegation(" --mac-source", srcrel, physaddress); } /* * fool-proof: this is last resort check for situation when user * created IPv4 object for the interface but left it with empty * address ( 0.0.0.0 ). * * note that combinedAddress inherits IPv4 and therefore * combinedAddress::hasInetAddress returns true; * */ if (src->hasInetAddress() && !src->getAddressPtr()->isAny()) command_line << _printSrcAddr(srcrel, src); } else command_line << _printSrcAddr(srcrel, src); } command_line << _printSrcService(srvrel); if (!dst->isAny()) command_line << _printDstAddr(dstrel, dst); command_line << _printDstService(srvrel); /* keeping state does not apply to deny/reject however some rules need state check even if action is Deny autoupgrade transformation 2.1.11 -> 2.1.12 adds rule option 'stateless=True' for rules with action NOT 'Accept', 'Tag' or 'Route'. No need to check action here, just rely on this option and internal flag 'force_state_check' (05/07/07 --vk) */ if (!ruleopt->getBool("stateless") || rule->getBool("force_state_check") ) { /* * But not, when the line already contains a state matching */ if (command_line.str().find("-m state --state", 0) == string::npos) command_line << " -m state --state NEW "; } command_line << _printTimeInterval(rule); command_line << _printModules(rule); command_line << _printTarget(rule); command_line << _endRuleLine(); // command_line << endl; return command_line.str(); } string PolicyCompiler_ipt::PrintRule::_declareTable() { return ""; } string PolicyCompiler_ipt::PrintRule::_commit() { return ""; } string PolicyCompiler_ipt::PrintRule::_clampTcpToMssRule() { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); ostringstream res; if ( compiler->getCachedFwOpt()->getBool("clamp_mss_to_mtu")) { bool ipforw; if (ipt_comp->ipv6) { string s = compiler->getCachedFwOpt()->getStr("linux24_ipv6_forward"); ipforw = (s.empty() || s=="1" || s=="On" || s=="on"); // bug #2477775: target TCPMSS is not available in ip6tables // before 1.4.0 In fact I am not sure of the minimal required // version. According to the netfilter git log, it was added in // 1.3.8 if (XMLTools::version_compare(version, "1.3.8")<0) { if (ipforw) { res << "target TCPMSS is not supported by ip6tables before v1.3.8"; compiler->warning(res.str()); return "# " + res.str() + "\n\n"; } else return ""; } } else { string s = compiler->getCachedFwOpt()->getStr("linux24_ip_forward"); ipforw = (s.empty() || s=="1" || s=="On" || s=="on"); } if (ipforw) { res << _startRuleLine() << "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" << _endRuleLine(); res << endl; } } return res.str(); } string PolicyCompiler_ipt::PrintRule::_printOptionalGlobalRules() { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); ostringstream res; bool isIPv6 = ipt_comp->ipv6; string s = compiler->getCachedFwOpt()->getStr("linux24_ip_forward"); bool ipforward= (s.empty() || s=="1" || s=="On" || s=="on"); s = compiler->getCachedFwOpt()->getStr("linux24_ipv6_forward"); bool ip6forward= (s.empty() || s=="1" || s=="On" || s=="on"); bool ipforw = ((!ipt_comp->ipv6 && ipforward) || (ipt_comp->ipv6 && ip6forward)); Configlet configlet(compiler->fw, "linux24", "automatic_rules"); configlet.removeComments(); configlet.collapseEmptyStrings(true); configlet.setVariable("begin_rule", _startRuleLine().c_str()); configlet.setVariable("end_rule", _endRuleLine().c_str()); configlet.setVariable("ipforw", ipforw); configlet.setVariable("accept_established", compiler->getCachedFwOpt()->getBool("accept_established") && ipt_comp->my_table=="filter"); list ll = compiler->fw->getByTypeDeep(Interface::TYPENAME); for (FWObject::iterator i=ll.begin(); i!=ll.end(); i++) { Interface *intf = Interface::cast( *i ); if (intf->isManagement()) { configlet.setVariable("management_interface", intf->getName().c_str()); break; } } _printBackupSSHAccessRules(&configlet); configlet.setVariable( "drop_new_tcp_with_no_syn", ! compiler->getCachedFwOpt()->getBool("accept_new_tcp_with_no_syn")); configlet.setVariable( "add_rules_for_ipv6_neighbor_discovery", isIPv6 && compiler->getCachedFwOpt()->getBool("add_rules_for_ipv6_neighbor_discovery")); configlet.setVariable("drop_invalid", compiler->getCachedFwOpt()->getBool("drop_invalid") && !compiler->getCachedFwOpt()->getBool("log_invalid")); configlet.setVariable("drop_invalid_and_log", compiler->getCachedFwOpt()->getBool("drop_invalid") && compiler->getCachedFwOpt()->getBool("log_invalid")); configlet.setVariable("create_drop_invalid_chain", _createChain("drop_invalid").c_str()); if (compiler->getCachedFwOpt()->getBool("log_invalid") && !isIPv6 && compiler->getCachedFwOpt()->getBool("use_ULOG")) { configlet.setVariable("use_ulog", 1); string s = compiler->getCachedFwOpt()->getStr("ulog_nlgroup"); configlet.setVariable("use_nlgroup", !s.empty()); configlet.setVariable("nlgroup", s.c_str()); int r = compiler->getCachedFwOpt()->getInt("ulog_cprange"); configlet.setVariable("use_cprange", r!=0); configlet.setVariable("cprange", r); r = compiler->getCachedFwOpt()->getInt("ulog_qthreshold"); configlet.setVariable("use_qthreshold", r!=0); configlet.setVariable("qthreshold", r); } else configlet.setVariable("not_use_ulog", 1); configlet.setVariable("invalid_match_log_prefix", _printLogPrefix("-1", "DENY", "global", "drop_invalid", "Policy", "BLOCK INVALID", "INVALID state -- DENY ").c_str()); return configlet.expand().toStdString(); } void PolicyCompiler_ipt::PrintRule::_printBackupSSHAccessRules(Configlet *conf) { PolicyCompiler_ipt *ipt_comp = dynamic_cast(compiler); bool isIPv6 = ipt_comp->ipv6; if ( compiler->getCachedFwOpt()->getBool("mgmt_ssh") && ! compiler->getCachedFwOpt()->getStr("mgmt_addr").empty() ) { string addr_str = compiler->getCachedFwOpt()->getStr("mgmt_addr"); InetAddrMask *inet_addr = NULL; bool addr_is_good = true; if (isIPv6) { // check if given address is ipv6 try { inet_addr = new Inet6AddrMask(addr_str); } catch(const FWException &ex) { // address does not parse as ipv6, skip this rule. addr_is_good = false; QString err("Backup ssh access rule could not be added " "to IPv6 policy because specified address " "'%1' is invalid"); compiler->warning(err.arg(addr_str.c_str()).toStdString()); } } else { // check if given address parses as ipv4 try { inet_addr = new InetAddrMask(addr_str); } catch(const FWException &ex) { // address does not parse addr_is_good = false; QString err("Backup ssh access rule could not be added " "to IPv4 policy because specified address " "'%1' is invalid"); compiler->warning(err.arg(addr_str.c_str()).toStdString()); } } if (addr_is_good) { conf->setVariable("begin_rule", _startRuleLine().c_str()); conf->setVariable("end_rule", _endRuleLine().c_str()); conf->setVariable("mgmt_access", 1); conf->setVariable("ssh_management_address", inet_addr->toString().c_str()); } } } string PolicyCompiler_ipt::PrintRule::_quote(const string &s) { return "\"" + s + "\""; }