1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2025-10-15 23:18:51 +02:00
fwbuilder/src/ipt/OSConfigurator_linux24.cpp
2008-04-26 19:13:45 +00:00

769 lines
28 KiB
C++

/*
Firewall Builder
Copyright (C) 2002 NetCitadel, LLC
Author: Vadim Kurland vadim@vk.crocodile.org
$Id: OSConfigurator_linux24.cpp 1369 2007-06-17 03:28:20Z vkurland $
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 "OSConfigurator_linux24.h"
#include "fwbuilder/InetAddr.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/FWOptions.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/Network.h"
#include "fwbuilder/Address.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/MultiAddress.h"
#ifndef _WIN32
# include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <assert.h>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
string OSConfigurator_linux24::myPlatformName() { return "Linux24"; }
OSConfigurator_linux24::OSConfigurator_linux24(FWObjectDatabase *_db,
const string &fwname) :
OSConfigurator(_db,fwname) , os_data(fw->getStr("host_OS"))
{
}
string OSConfigurator_linux24::getInterfaceVarName(FWObject *iface)
{
ostringstream ostr;
string iname=iface->getName();
string::size_type p1;
while ( (p1=iname.find("."))!=string::npos)
iname=iname.replace(p1,1,"_");
ostr << "i_" << iname;
return ostr.str();
}
void OSConfigurator_linux24::processFirewallOptions()
{
FWOptions* options=fw->getOptionsObject();
string s;
int i;
/*
* check if all interfaces configured for the firewall are present
*/
if (options->getBool("verify_interfaces"))
{
list<FWObject*> l2=fw->getByType(Interface::TYPENAME);
if ( ! l2.empty() )
{
output << endl;
output << "INTERFACES=\"";
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface=Interface::cast(*i);
if (iface->getName().find("*")==string::npos)
output << iface->getName() << " ";
}
output << "\"" << endl;
output << "for i in $INTERFACES ; do" << endl;
output << " $IP link show \"$i\" > /dev/null 2>&1 || {" << endl;
output << " log \"Interface $i does not exist\"" << endl;
output << " exit 1" << endl;
output << " }" << endl;
output << "done" << endl;
output << endl;
}
}
/*
* Turn off packet forwarding for now. We'll turn it on if needed in the end
*
* turned this off. This seems to be an overkill as we set default
* policy to DROP in all chains before we purge the current firewall policy.
output << "\n\n";
output << "FWD=`cat /proc/sys/net/ipv4/ip_forward`\n";
output << "echo \"0\" > /proc/sys/net/ipv4/ip_forward\n\n";
*/
s=options->getStr("linux24_ip_dynaddr");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/ip_dynaddr\n\n";
s=options->getStr("linux24_rp_filter");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/conf/all/rp_filter\n\n";
s=options->getStr("linux24_accept_source_route");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/conf/all/accept_source_route\n\n";
s=options->getStr("linux24_accept_redirects");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/conf/all/accept_redirects\n\n";
s=options->getStr("linux24_log_martians");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/conf/all/log_martians\n\n";
s=options->getStr("linux24_icmp_echo_ignore_broadcasts");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts\n\n";
s=options->getStr("linux24_icmp_echo_ignore_all");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/icmp_echo_ignore_all\n\n";
s=options->getStr("linux24_icmp_ignore_bogus_error_responses");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses\n\n";
if ( (i=options->getInt("linux24_tcp_fin_timeout"))>0 )
output << "echo " << i << " > /proc/sys/net/ipv4/tcp_fin_timeout\n\n";
if ( (i=options->getInt("linux24_tcp_keepalive_interval"))>0 )
output << "echo " << i << " > /proc/sys/net/ipv4/tcp_keepalive_intvl\n\n";
s=options->getStr("linux24_tcp_window_scaling");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_window_scaling\n\n";
s=options->getStr("linux24_tcp_sack");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_sack\n\n";
s=options->getStr("linux24_tcp_fack");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_fack\n\n";
s=options->getStr("linux24_tcp_syncookies");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_syncookies\n\n";
s=options->getStr("linux24_tcp_ecn");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_ecn\n\n";
s=options->getStr("linux24_tcp_timestamps");
if (!s.empty())
output << "echo " << s << " > /proc/sys/net/ipv4/tcp_timestamps\n\n";
output << endl;
}
void OSConfigurator_linux24::addVirtualAddressForNAT(const Network *nw)
{
ostringstream ostr;
FWOptions* options=fw->getOptionsObject();
if ( options->getBool("manage_virtual_addr") )
{
if (virtual_addresses.empty() ||
find(virtual_addresses.begin(),virtual_addresses.end(),
nw->getAddress())==virtual_addresses.end())
{
Interface *iface=findInterfaceFor( nw, fw );
if (iface!=NULL)
{
const InetAddr& a = nw->getAddress();
string str, subnet, first, last;
first = (a + 1).toString();
last = (nw->getBroadcastAddress() -1).toString();
ostr << endl;
ostr << "a=\"" << first << "\"" << endl;
ostr << "while test \"$a\" != \"" << last << "\"; do" << endl;
ostr << " add_addr ${a} " << nw->getNetmask().getLength()
<< " " << iface->getName() << endl;
ostr << endl;
ostr << " OIFS=$IFS" << endl;
ostr << " IFS=\".\"" << endl;
ostr << " set $a" << endl;
ostr << " a4=$1" << endl;
ostr << " a3=$2" << endl;
ostr << " a2=$3" << endl;
ostr << " a1=$4" << endl;
ostr << " IFS=$OIFS" << endl;
ostr << " incaddr a4 a3 a2 a1" << endl;
ostr << " a=$a4\".\"$a3\".\"$a2\".\"$a1" << endl;
ostr << "done" << endl << endl;
virtual_addresses.push_back(nw->getAddress());
registerVirtualAddressForNat();
} else
warning(_("Can not add virtual address ") + nw->getAddress().toString() +
_(" (object ") + nw->getName() + ")" );
}
commands_to_add_virtual_addresses.push_back(ostr.str());
}
}
void OSConfigurator_linux24::addVirtualAddressForNAT(const Address *addr)
{
ostringstream ostr;
FWOptions* options=fw->getOptionsObject();
if ( options->getBool("manage_virtual_addr") )
{
if (virtual_addresses.empty() ||
find(virtual_addresses.begin(),virtual_addresses.end(),
addr->getAddress())==virtual_addresses.end())
{
FWObject *vaddr = findAddressFor(addr, fw );
if (vaddr!=NULL)
{
Interface *iface = Interface::cast(vaddr->getParent());
assert(iface!=NULL);
const InetAddrMask *vaddr_addr = Address::cast(
vaddr)->getAddressObjectInetAddrMask();
assert(vaddr_addr!=NULL);
ostr << "add_addr " << addr->getAddress().toString() << " "
<< vaddr_addr->getNetmask().getLength() << " "
<< iface->getName() << endl;
virtual_addresses.push_back(addr->getAddress());
registerVirtualAddressForNat();
} else
warning(_("Can not add virtual address ") +
addr->getAddress().toString() +
_(" (object ") + addr->getName() + ")" );
}
commands_to_add_virtual_addresses.push_back(ostr.str());
return;
}
}
void OSConfigurator_linux24::printCommandsToAddVirtualAddressesForNAT()
{
output << "# Add virtual addresses for NAT rules" << endl;
list<string>::iterator i;
for (i=commands_to_add_virtual_addresses.begin();
i!=commands_to_add_virtual_addresses.end(); ++i)
output << *i;
output << endl;
}
void OSConfigurator_linux24::registerMultiAddressObject(MultiAddressRunTime *at)
{
address_table_objects[at->getName()] = at->getSourceName();
}
void OSConfigurator_linux24::printChecksForRunTimeMultiAddress()
{
output << "# Using " << address_table_objects.size() << " address table files" << endl;
map<string,string>::iterator i;
for (i=address_table_objects.begin(); i!=address_table_objects.end(); ++i)
{
string at_name = i->first;
string at_file = i->second;
output << "check_file \"" + at_name + "\" \"" + at_file + "\"" << endl;
}
output << endl;
}
void OSConfigurator_linux24::configureInterfaces()
{
FWOptions* options=fw->getOptionsObject();
output << "# Configure interfaces" << endl;
/*
* Remove all host static routes and "pub" ARP entries if we are going to
* create new ones
*/
if ( options->getBool("manage_virtual_addr") )
{
list<FWObject*> l2=fw->getByType(Interface::TYPENAME);
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *interface_=Interface::cast(*i);
if ( interface_->isDyn() ) continue;
if ( interface_->isUnnumbered() ) continue;
if ( interface_->isBridgePort() ) continue;
if ( interface_->isLoopback() ) continue;
output << "$IP -4 neigh flush dev "
<< interface_->getName() << " >/dev/null 2>&1" << endl;
output << "$IP -4 addr flush dev "
<< interface_->getName()
<< " secondary label \"" << interface_->getName() << ":FWB*\""
<< " >/dev/null 2>&1" << endl;
}
output << endl;
}
if ( options->getBool("configure_interfaces") )
{
output << endl;
FWObjectTypedChildIterator i=fw->findByType(Interface::TYPENAME);
for ( ; i!=i.end(); ++i )
{
Interface *iface=dynamic_cast<Interface*>(*i);
assert(iface);
if (iface->isDyn()) continue;
if (iface->isUnnumbered()) continue;
if (iface->isBridgePort() ) continue;
FWObjectTypedChildIterator j=iface->findByType(IPv4::TYPENAME);
for ( ; j!=j.end(); ++j )
{
const InetAddrMask *iaddr = Address::cast(*j)->getAddressObjectInetAddrMask();
output << "add_addr " << iaddr->getAddress().toString() << " "
<< iaddr->getNetmask().getLength() << " "
<< iface->getName() << endl;
// add to the table of virtual addresses so we won't generate code to
// configure the same address if it is needed for NAT
virtual_addresses.push_back(iaddr->getAddress());
}
output << "$IP link set " << iface->getName() << " up" << endl;
}
output << endl;
}
/*
* get addresses of dynamic interfaces
*/
FWObjectTypedChildIterator j=fw->findByType(Interface::TYPENAME);
for ( ; j!=j.end(); ++j )
{
Interface *iface=Interface::cast(*j);
if ( iface->isDyn() )
{
/* if interface name ends with '*', this is a wildcard interface. Do
* not get its address at this time. */
if (iface->getName().find("*")==string::npos)
output << "getaddr "
<< iface->getName()
<< " "
<< getInterfaceVarName(iface)
<< endl;
}
}
output << endl;
}
int OSConfigurator_linux24::prolog()
{
printShellFunctions();
/*
* Process firewall options, build OS network configuration script
*/
// processFirewallOptions();
output << endl;
// configureInterfaces();
return 0;
}
void OSConfigurator_linux24::printShellFunctions()
{
FWOptions* options=fw->getOptionsObject();
output << endl;
output << "log() {" << endl;
output << " echo \"$1\"" << endl;
output << " test -x \"$LOGGER\" && $LOGGER -p info \"$1\"" << endl;
output << "}" << endl;
output << endl;
output << "check_file() {" << endl;
output << " test -r \"$2\" || {" << endl;
output << " echo \"Can not find file $2 referenced by AddressTable object $1\"" << endl;
output << " exit 1" << endl;
output << " }" << endl;
output << "}" << endl;
output << endl;
output << "va_num=1" << endl;
output << "add_addr() {" << endl;
output << " addr=$1" << endl;
output << " nm=$2" << endl;
output << " dev=$3" << endl;
output << "" << endl;
output << " type=\"\"" << endl;
output << " aadd=\"\"" << endl;
output << "" << endl;
output << " L=`$IP -4 link ls $dev | head -n1`" << endl;
output << " if test -n \"$L\"; then" << endl;
output << " OIFS=$IFS" << endl;
output << " IFS=\" /:,<\"" << endl;
output << " set $L" << endl;
output << " type=$4" << endl;
output << " IFS=$OIFS" << endl;
output << " if test \"$type\" = \"NO-CARRIER\"; then" << endl;
output << " type=$5" << endl;
output << " fi" << endl;
output << "" << endl;
/*
* see comment about using grep with or without regex below
*/
output << " L=`$IP -4 addr ls $dev to $addr | grep inet | grep -v :`" << endl;
output << " if test -n \"$L\"; then" << endl;
output << " OIFS=$IFS" << endl;
output << " IFS=\" /\"" << endl;
output << " set $L" << endl;
output << " aadd=$2" << endl;
output << " IFS=$OIFS" << endl;
output << " fi" << endl;
output << " fi" << endl;
output << " if test -z \"$aadd\"; then" << endl;
output << " if test \"$type\" = \"POINTOPOINT\"; then"<< endl;
output << " $IP -4 addr add $addr dev $dev scope global label $dev:FWB${va_num}" << endl;
output << " va_num=`expr $va_num + 1`" << endl;
output << " fi" << endl;
output << " if test \"$type\" = \"BROADCAST\"; then" << endl;
output << " $IP -4 addr add $addr/$nm dev $dev brd + scope global label $dev:FWB${va_num}" << endl;
output << " va_num=`expr $va_num + 1`" << endl;
output << " fi" << endl;
output << " fi" << endl;
output << "}" << endl;
output << endl;
output << "getInterfaceVarName() {" << endl;
output << " echo $1 | sed 's/\\./_/'" << endl;
output << "}" << endl;
output << endl;
output << "getaddr() {" << endl;
output << " dev=$1" << endl;
output << " name=$2" << endl;
/*
* originally this command looked like this:
* $IP -4 addr ls $dev to $addr | grep inet | grep -E \"$dev$\"`
*
* i.e. it looked for a line that ends with "$dev":
* inet 10.3.14.40/24 brd 10.3.14.255 scope global eth0
* as opposed to
* inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0:1
*
* It turns out, some busybox-based systems have grep compiled w/o
* support for regular expressions. Using "grep -v :" seems to be an
* easy way to filter out secondary addresses without using regex
*/
output << " L=`$IP -4 addr show dev $dev | grep inet | grep -v :`" << endl;
output << " test -z \"$L\" && { " << endl;
output << " eval \"$name=''\"" << endl;
output << " return" << endl;
output << " }" << endl;
output << " OIFS=$IFS" << endl;
output << " IFS=\" /\"" << endl;
output << " set $L" << endl;
output << " eval \"$name=$2\"" << endl;
output << " IFS=$OIFS" << endl;
output << "}" << endl;
output << endl;
output << endl;
/* we use function getinterfaces to process wildcard interfaces */
output << "getinterfaces() {" << endl;
output << " NAME=$1" << endl;
output << " $IP link show | grep \": $NAME\" | while read L; do" << endl;
output << " OIFS=$IFS" << endl;
output << " IFS=\" :\"" << endl;
output << " set $L" << endl;
output << " IFS=$OIFS" << endl;
output << " echo $2" << endl;
output << " done" << endl;
output << "}" << endl;
output << endl;
output << endl;
output << "# increment ip address" << endl;
output << "incaddr()" << endl;
output << "{" << endl;
output << " n1=$4" << endl;
output << " n2=$3" << endl;
output << " n3=$2" << endl;
output << " n4=$1" << endl;
output << endl;
output << " vn1=`eval \"echo \\\\$$n1\"`" << endl;
output << endl;
output << " R=`expr $vn1 \\< 255`" << endl;
output << " if test $R = \"1\"; then" << endl;
output << " eval \"$n1=`expr $vn1 + 1`\"" << endl;
output << " else" << endl;
output << " eval \"$n1=0\"" << endl;
output << " incaddr XX $n4 $n3 $n2" << endl;
output << " fi" << endl;
output << "}" << endl;
output << endl;
/* check if package iproute2 is installed, but do this only if
* we really need /usr/sbin/ip
*/
if (options->getBool("verify_interfaces") ||
options->getBool("manage_virtual_addr") ||
options->getBool("configure_interfaces") )
{
output << "if $IP link ls >/dev/null 2>&1; then" << endl;
output << " echo;" << endl;
output << "else" << endl;
output << " echo \"iproute not found\"" << endl;
output << " exit 1" << endl;
output << "fi" << endl;
}
output << endl;
}
string OSConfigurator_linux24::printPathForAllTools(const string &os)
{
string res;
FWOptions* options=fw->getOptionsObject();
string s, path_lsmod, path_modprobe, path_iptables, path_iptables_restore, path_ip, path_logger;
s=options->getStr("linux24_path_lsmod");
if (!s.empty()) path_lsmod=s;
else path_lsmod=os_data.getPathForTool(os,OSData::LSMOD);
s=options->getStr("linux24_path_modprobe");
if (!s.empty()) path_modprobe=s;
else path_modprobe=os_data.getPathForTool(os,OSData::MODPROBE);
s=options->getStr("linux24_path_iptables");
if (!s.empty()) path_iptables=s;
else path_iptables=os_data.getPathForTool(os,OSData::IPTABLES);
s=options->getStr("linux24_path_iptables_restore");
if (!s.empty()) path_iptables_restore=s;
else path_iptables_restore=os_data.getPathForTool(os,OSData::IPTABLES_RESTORE);
s=options->getStr("linux24_path_ip");
if (!s.empty()) path_ip=s;
else path_ip=os_data.getPathForTool(os,OSData::IP);
s=options->getStr("linux24_path_logger");
if (!s.empty()) path_logger=s;
else path_logger=os_data.getPathForTool(os,OSData::LOGGER);
res += "LSMOD=\"" +path_lsmod+"\"\n";
res += "MODPROBE=\""+path_modprobe+"\"\n";
res += "IPTABLES=\""+path_iptables+"\"\n";
res += "IPTABLES_RESTORE=\""+path_iptables_restore+"\"\n";
res += "IP=\"" +path_ip+"\"\n";
res += "LOGGER=\"" +path_logger+"\"\n";
res += "\n";
return res;
}
void OSConfigurator_linux24::generateCodeForProtocolHandlers(bool have_nat)
{
FWOptions* options=fw->getOptionsObject();
bool nomod=Resources::os_res[fw->getStr("host_OS")]->Resources::getResourceBool("/FWBuilderResources/Target/options/suppress_modules");
/* there is no need to load modules on linksys */
if (options->getBool("load_modules") && !nomod)
{
std::string sed_command = "sed -e 's/^.*\\///' -e 's/\\([^\\.]\\)\\..*/\\1/'";
output << endl;
output << "MODULES_DIR=\"/lib/modules/`uname -r`/kernel/net/\"" << endl;
output << "MODULES=`find $MODULES_DIR -name '*conntrack*'|" << sed_command << "`" << endl;
if (have_nat)
{
output << "MODULES=\"$MODULES `find $MODULES_DIR -name '*nat*'|" << sed_command << "`\"" << endl;
}
output << "for module in $MODULES; do " << endl;
output << " if $LSMOD | grep ${module} >/dev/null; then continue; fi" << endl;
output << " $MODPROBE ${module} || exit 1 " << endl;
output << "done" << endl;
output << endl;
output << endl;
}
}
string OSConfigurator_linux24::printRunTimeWrappers(FWObject *rule,
const string &command)
{
string command_line = command;
ostringstream ext_command_line;
int nlines = 0;
string::size_type p1 = 0;
string::size_type p2, p3;
p1=command_line.find("$at_");
if ( p1!=string::npos )
{
p2=command_line.find(" ",p1);
string at_var= command_line.substr(p1+1,p2-p1-1); // skip '$'
string atfile = rule->getStr("address_table_file");
ext_command_line << "grep -Ev '^#|^;|^\\s*$' " << atfile << " | ";
ext_command_line << "while read L ; do" << endl;
ext_command_line << " set $L; " << at_var << "=$1; ";
ext_command_line << command_line;
ext_command_line << "done" << endl;
command_line = ext_command_line.str();
}
p1 = 0;
while (1)
{
p1=command_line.find_first_of("\n\r",p1);
if (p1==string::npos) break;
nlines++;
p1=command_line.find_first_not_of("\n\r",p1);
if (p1==string::npos) break;
}
/* if anywhere in command_line we used variable holding an address of
* dynamic interface (named $i_something) then we need to add this
* command with a check for the value of this variable. We execute
* iptables command only if the value is a non-empty string.
*
* bug #1851166: there could be two dynamic interfaces in the same
* rule.
*/
if (command_line.find("$i_")==string::npos) return command_line;
ostringstream res;
bool wildcard_interfaces = false;
p1=0;
while ((p1=command_line.find("$i_", p1))!=string::npos)
{
string iface_name;
string iface_var;
p2=command_line.find(" ",p1);
p3=command_line.find("_",p1) +1;
iface_name=command_line.substr(p3,p2-p3);
iface_var= command_line.substr(p1,p2-p1);
/* if interface name ends with '*', this is a wildcard interface. */
string::size_type p4;
if ((p4=iface_name.find("*"))!=string::npos)
{
wildcard_interfaces = true;
string cmdline=command_line;
string iface_family_name=iface_name.substr(0,p4);
res << "getinterfaces " << iface_family_name << " | while read I; do" << endl;
res << " ivar=`getInterfaceVarName $I`" << endl;
res << " getaddr $I $ivar" << endl;
res << " cmd=\"$\"$ivar" << endl;
res << " eval \"addr=$cmd\"" << endl;
cmdline.replace(p1,p2-p1,"$addr");
res << " test -n \"$addr\" && ";
if (nlines>1) res << "{" << endl;
res << cmdline;
if (nlines>1) res << "}" << endl;
res << "done" << endl;
} else
{
// bug #1851166: there could be two dynamic interfaces in
// the same rule. Just print "test" command here and continue
// in the "while" loop. We'll print actual commands when the loop
// ends.
res << "test -n \"" << iface_var << "\" && ";
}
p1++; // p1 points at the previous "$i_" fragment
}
// for wildcard interfaces we only support one such interface
// per rule and we have already printed the actual command above.
if (!wildcard_interfaces)
{
if (nlines>1) res << "{" << endl;
res << command_line;
if (nlines>1) res << "}" << endl;
}
return res.str();
}
void OSConfigurator_linux24::epilog()
{
FWOptions* options=fw->getOptionsObject();
try {
output << "#" << endl;
output << "#" << endl;
/* Turn on packet forwarding if we have to */
string s=options->getStr("linux24_ip_forward");
if (!s.empty())
{
if (s=="1" || s=="On" || s=="on") s="1";
else s="0";
output << "echo " << s << " > /proc/sys/net/ipv4/ip_forward\n\n";
}
// else
// output << "echo \"$FWD\" > /proc/sys/net/ipv4/ip_forward\n\n";
} catch (FWException ex) {
error(ex.toString());
exit(1);
}
}