mirror of
https://github.com/webmin/webmin.git
synced 2025-08-16 14:51:18 +00:00
721 lines
17 KiB
Perl
Executable File
721 lines
17 KiB
Perl
Executable File
# Functions for managing an ipfw firewall.
|
|
# Works on a file as generated by ipfw list and read by ipfw /path/name,
|
|
# rather than a script.
|
|
# XXX some thing are not supported by ipfw1
|
|
|
|
BEGIN { push(@INC, ".."); };
|
|
use WebminCore;
|
|
&init_config();
|
|
if (&foreign_check("net")) {
|
|
&foreign_require("net", "net-lib.pl");
|
|
if (defined(&net::get_rc_conf)) {
|
|
$has_net_lib = 1;
|
|
}
|
|
}
|
|
|
|
# Work out save file
|
|
$ipfw_file = "$module_config_directory/ipfw.rules";
|
|
if ($config{'save_file'}) {
|
|
$ipfw_file = $config{'save_file'};
|
|
}
|
|
elsif ($has_net_lib) {
|
|
# Use entry in rc.conf, if set
|
|
local %rc = &net::get_rc_conf();
|
|
if ($rc{'firewall_type'} =~ /^\//) {
|
|
$ipfw_file = $rc{'firewall_type'};
|
|
}
|
|
}
|
|
|
|
@actions = ( "allow", "deny", "reject", "reset", "skipto", "fwd", "check-state",
|
|
"count", "divert", "pipe", "queue", "tee", "unreach" );
|
|
|
|
@unreaches = ( "net", "host", "protocol", "port", "needfrag", "srcfail",
|
|
"net-unknown", "host-unknown", "isolated", "net-prohib",
|
|
"host-prohib", "tosnet", "toshost", "filter-prohib",
|
|
"host-precedence", "precedence-cutoff" );
|
|
|
|
@options = ( "bridged", "established", "frag", "in", "out",
|
|
"keep-state", "setup" );
|
|
|
|
@one_options = ( "gid", "uid", "icmptypes", "recv", "xmit",
|
|
"via", "tcpflags" );
|
|
|
|
@two_options = ( "limit", "mac" );
|
|
|
|
@multi_options = ( "dst-port", "src-port" );
|
|
|
|
@icmptypes = ( "echo-reply", undef, undef, "destination-unreachable",
|
|
"source-quench", "redirect", undef, undef, "echo-request",
|
|
"router-advertisement", "router-solicitation", "ttl-exceeded",
|
|
"ip-header-bad", "timestamp-request", "timestamp-reply",
|
|
"information-request", "information-reply",
|
|
"address-mask-request", "address-mask-reply" );
|
|
|
|
@tcpflags = ( "fin", "syn", "rst", "psh", "ack", "urg" );
|
|
|
|
# Get the detected ipfw version
|
|
if (open(VERSION, "<$module_config_directory/version")) {
|
|
chop($ipfw_version = <VERSION>);
|
|
close(VERSION);
|
|
}
|
|
|
|
# get_config([file], [&output])
|
|
# Returns a list of rules from the firewall file
|
|
sub get_config
|
|
{
|
|
local $file = $_[0] || $ipfw_file;
|
|
local $fmt = &get_ipfw_format();
|
|
if ($file =~ /\|$/) {
|
|
# When getting from command, there is never an 'add'
|
|
$fmt = 0;
|
|
}
|
|
local @rv;
|
|
local $cmt;
|
|
local $lnum = -1;
|
|
open(LIST, $file =~ /\|$/ ? $file : "<".$file);
|
|
while(<LIST>) {
|
|
${$_[1]} .= $_ if ($_[1]);
|
|
$lnum++;
|
|
if ($fmt == 1 && !/^add\s+/ && !/^#/) {
|
|
# Expecting 'add' suffixes, but found some other directive
|
|
local $rule = { 'index' => scalar(@rv),
|
|
'line' => $lnum-scalar(@cmts),
|
|
'eline' => $lnum,
|
|
'other' => 1,
|
|
'text' => $_ };
|
|
$cmt = undef;
|
|
push(@rv, $rule);
|
|
}
|
|
elsif (/^(add\s+)?(\d+)\s+(.*)/) {
|
|
# an ipfw rule
|
|
local @cmts = split(/\n/, $cmt);
|
|
local $rule = { 'index' => scalar(@rv),
|
|
'line' => $lnum-scalar(@cmts),
|
|
'eline' => $lnum,
|
|
'num' => $2,
|
|
'text' => $3,
|
|
'cmd' => $1,
|
|
'cmt' => $cmt };
|
|
$cmt = undef;
|
|
local @w = &split_quoted_string($3);
|
|
$rule->{'cmd'} =~ s/\s+$//;
|
|
|
|
# Parse counts, if given
|
|
if ($w[0] =~ /^\d+$/) {
|
|
$rule->{'count1'} = shift(@w);
|
|
$rule->{'count2'} = shift(@w);
|
|
}
|
|
|
|
# parse the set number
|
|
if ($w[0] eq "set") {
|
|
shift(@w);
|
|
$rule->{'set'} = shift(@w);
|
|
}
|
|
|
|
# parse the probability of match
|
|
if ($w[0] eq "prob") {
|
|
shift(@w);
|
|
$rule->{'prob'} = shift(@w);
|
|
}
|
|
|
|
# Parse the action
|
|
$rule->{'action'} = shift(@w);
|
|
if ($rule->{'action'} =~ /divert|fwd|forward|pipe|queue|skipto|tee|unreach/) {
|
|
# Action has an arg
|
|
$rule->{'aarg'} = shift(@w);
|
|
}
|
|
|
|
# Parse the log section
|
|
if ($w[0] eq "log") {
|
|
$rule->{'log'} = 1;
|
|
shift(@w);
|
|
if ($w[0] eq "logamount") {
|
|
shift(@w);
|
|
$rule->{'logamount'} = shift(@w);
|
|
}
|
|
}
|
|
|
|
# Parse the protocol
|
|
local $hasproto;
|
|
if ($w[0] eq "{" || $w[0] eq "(") {
|
|
$rule->{'proto'} = &words_to_orblock(\@w);
|
|
}
|
|
else {
|
|
$rule->{'proto'} = shift(@w);
|
|
$hasproto++ if ($rule->{'proto'} ne "ip" &&
|
|
$rule->{'proto'} ne "any");
|
|
}
|
|
|
|
# Parse the source and destination sections
|
|
local $s;
|
|
foreach $s ("from", "to") {
|
|
local $sn = shift(@w);
|
|
next if ($sn ne $s);
|
|
|
|
# Parse IP address
|
|
if ($w[0] eq "not") {
|
|
$rule->{$s."_not"} = 1;
|
|
shift(@w);
|
|
}
|
|
if ($w[0] eq "{" || $w[0] eq "(") {
|
|
$rule->{$s} = &words_to_orblock(\@w);
|
|
}
|
|
else {
|
|
$rule->{$s} = shift(@w);
|
|
}
|
|
|
|
# Parse ports
|
|
local $pr = $rule->{'proto'};
|
|
if ($w[0] eq "not" && @w > 1 &&
|
|
($w[1] =~ /^\d+$/ || $w[1] =~ /,/ ||
|
|
$w[1] =~ /\-/ ||
|
|
defined(getservbyname($w[1], $rule->{'proto'})))) {
|
|
shift(@w);
|
|
$rule->{$s."_ports_not"} = 1;
|
|
}
|
|
if ($w[0] =~ /^\d+$/ || $w[0] =~ /,/ ||
|
|
($w[0] =~ /^(\S+)\-(\S+)$/ &&
|
|
&valid_port($1, $pr) &&
|
|
&valid_port($2, $pr)) ||
|
|
&valid_port($w[0], $pr)) {
|
|
$rule->{$s."_ports"} = shift(@w);
|
|
}
|
|
}
|
|
|
|
# Parse any options
|
|
if ($w[0] eq "{" || $w[0] eq "(") {
|
|
# XXX can be an or-block!
|
|
$rule->{'options'} = &words_to_orblock(\@w);
|
|
}
|
|
else {
|
|
local $nextnot = 0;
|
|
while(@w) {
|
|
local $o = lc(shift(@w));
|
|
$o = "icmptypes" if ($o eq "icmptype");
|
|
if ($o eq "not") {
|
|
$nextnot = 1;
|
|
}
|
|
else {
|
|
if (&indexof($o, @options) >= 0) {
|
|
# Stand-alone option
|
|
$rule->{$o}++;
|
|
$rule->{$o."_not"} = $nextnot;
|
|
}
|
|
elsif (&indexof($o, @one_options) >= 0) {
|
|
# Option with one value
|
|
$rule->{$o} = shift(@w);
|
|
$rule->{$o."_not"} = $nextnot;
|
|
}
|
|
elsif (&indexof($o, @two_options) >= 0) {
|
|
$rule->{$o} = [ shift(@w), shift(@w) ];
|
|
$rule->{$o."_not"} = $nextnot;
|
|
}
|
|
elsif (&indexof($o, @multi_options) >= 0) {
|
|
$rule->{$o} = [ ];
|
|
while(@w && $w[0] =~ /^\d+$/) {
|
|
push(@{$rule->{$o}}, shift(@w));
|
|
}
|
|
$rule->{$o."_not"} = $nextnot;
|
|
}
|
|
else {
|
|
# Unknown option!!
|
|
push(@{$rule->{'unknown'}}, "not") if ($nextnot);
|
|
push(@{$rule->{'unknown'}}, $o);
|
|
}
|
|
$nextnot = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
push(@rv, $rule);
|
|
}
|
|
elsif (/^#\s*(.*)/) {
|
|
# A comment, which applies to the next rule
|
|
$cmt .= "\n" if ($cmt);
|
|
$cmt .= $1;
|
|
}
|
|
}
|
|
close(LIST);
|
|
return \@rv;
|
|
}
|
|
|
|
# valid_port(text, protocol)
|
|
sub valid_port
|
|
{
|
|
return 1 if ($_[0] =~ /^\d+$/);
|
|
return 1 if (defined(getservbyname($_[0], $_[1])));
|
|
return 0;
|
|
}
|
|
|
|
# save_config(&rules)
|
|
# Updates the firewall file with a list of rules
|
|
sub save_config
|
|
{
|
|
open(LIST, ">$ipfw_file");
|
|
foreach $r (@{$_[0]}) {
|
|
local @lines = &rule_lines($r);
|
|
local $l;
|
|
foreach $l (@lines) {
|
|
print LIST $l,"\n";
|
|
}
|
|
}
|
|
close(LIST);
|
|
}
|
|
|
|
# rule_lines(&rule, [no-comment], [no-add])
|
|
# Returns the lines of text to make up a rule
|
|
sub rule_lines
|
|
{
|
|
local ($rule, $nocmt, $noadd) = @_;
|
|
local @cmts = $nocmt ? ( ) : map { "# $_" } split(/\n/, $rule->{'cmt'});
|
|
local $fmt = &get_ipfw_format();
|
|
if ($rule->{'other'}) {
|
|
# Some other line (non-add) that never changes
|
|
return (@cmts, $rule->{'text'});
|
|
}
|
|
elsif (defined($rule->{'text'})) {
|
|
# A rule line that has not changed
|
|
if ($fmt && !$rule->{'cmd'}) {
|
|
$rule->{'cmd'} = 'add';
|
|
}
|
|
return (@cmts, ($rule->{'cmd'} ? $rule->{'cmd'}." " : "").
|
|
(defined($rule->{'num'}) ? $rule->{'num'}." " : "").
|
|
$rule->{'text'});
|
|
}
|
|
else {
|
|
# Need to construct
|
|
local @w;
|
|
|
|
# Add the basic rule parameters
|
|
if ($fmt == 1 && !$noadd) {
|
|
push(@w, "add");
|
|
}
|
|
push(@w, $rule->{'num'});
|
|
push(@w, "set", $rule->{'set'}) if (defined($rule->{'set'}));
|
|
push(@w, "prob", $rule->{'prob'}) if (defined($rule->{'prob'}));
|
|
push(@w, $rule->{'action'});
|
|
push(@w, $rule->{'aarg'}) if (defined($rule->{'aarg'}));
|
|
if ($rule->{'log'}) {
|
|
push(@w, "log");
|
|
push(@w, "logamount", $rule->{'logamount'})
|
|
if (defined($rule->{'logamount'}));
|
|
}
|
|
push(@w, &orblock_to_words($rule->{'proto'}));
|
|
|
|
# Add the from and to sections
|
|
local $s;
|
|
foreach $s ("from", "to") {
|
|
push(@w, $s);
|
|
push(@w, "not") if ($rule->{$s."_not"});
|
|
push(@w, &orblock_to_words($rule->{$s}));
|
|
if (defined($rule->{$s."_ports"})) {
|
|
push(@w, "not") if ($rule->{$s."_ports_not"});
|
|
push(@w, $rule->{$s."_ports"});
|
|
}
|
|
}
|
|
|
|
# Add the options
|
|
if (ref($rule->{'options'})) {
|
|
push(@w, &orblock_to_words($rule->{'options'}));
|
|
}
|
|
else {
|
|
local $o;
|
|
foreach $o (@options) {
|
|
if ($rule->{$o}) {
|
|
push(@w, "not") if ($rule->{$o."_not"});
|
|
push(@w, $o);
|
|
}
|
|
}
|
|
foreach $o (@one_options) {
|
|
if (defined($rule->{$o})) {
|
|
push(@w, "not") if ($rule->{$o."_not"});
|
|
push(@w, $o);
|
|
push(@w, $rule->{$o});
|
|
}
|
|
}
|
|
foreach $o (@two_options, @multi_options) {
|
|
if (defined($rule->{$o})) {
|
|
push(@w, "not") if ($rule->{$o."_not"});
|
|
push(@w, $o);
|
|
push(@w, @{$rule->{$o}});
|
|
}
|
|
}
|
|
push(@w, @{$rule->{'unknown'}});
|
|
}
|
|
|
|
# Create the resulting rule string
|
|
local @w = map { /\(|\)/ ? "'$_'" : $_ } @w;
|
|
return (@cmts, join(" ", @w));
|
|
}
|
|
}
|
|
|
|
sub describe_rule
|
|
{
|
|
local $r = $_[0];
|
|
local @rv;
|
|
if ($r->{'proto'} ne 'all' && $r->{'proto'} ne 'ip') {
|
|
push(@rv, &text($r->{'proto_not'} ? 'desc_proto_not' : 'desc_proto',
|
|
"<b>".uc($r->{'proto'})."</b>"));
|
|
}
|
|
if ($r->{'from'} ne 'any') {
|
|
push(@rv, &text($r->{'from_not'} ? 'desc_from_not' : 'desc_from',
|
|
$r->{'from'} eq 'me' ? $text{'desc_me'} : "<b>$r->{'from'}</b>"));
|
|
}
|
|
if ($r->{'from_ports'} ne '') {
|
|
push(@rv, &text($r->{'from_ports_not'} ? 'desc_from_ports_not'
|
|
: 'desc_from_ports',
|
|
"<b>$r->{'from_ports'}</b>"));
|
|
}
|
|
if ($r->{'to'} ne 'any') {
|
|
push(@rv, &text($r->{'to_not'} ? 'desc_to_not' : 'desc_to',
|
|
$r->{'to'} eq 'me' ? $text{'desc_me'} : "<b>$r->{'to'}</b>"));
|
|
}
|
|
if ($r->{'to_ports'} ne '') {
|
|
push(@rv, &text($r->{'to_ports_not'} ? 'desc_to_ports_not'
|
|
: 'desc_to_ports',
|
|
"<b>$r->{'to_ports'}</b>"));
|
|
}
|
|
push(@rv, $text{'desc_in'}) if ($r->{'in'});
|
|
push(@rv, $text{'desc_out'}) if ($r->{'out'});
|
|
local $o;
|
|
foreach $o (@options) {
|
|
if ($r->{$o} && $r->{$o."_not"}) {
|
|
push(@rv, $text{'desc_'.$o.'_not'});
|
|
}
|
|
elsif ($r->{$o}) {
|
|
push(@rv, $text{'desc_'.$o});
|
|
}
|
|
}
|
|
foreach $o (@one_options) {
|
|
local $v = $r->{$o};
|
|
if ($o eq "icmptypes") {
|
|
$v = join(",", map { $icmptypes[$_] || $_ }
|
|
split(/,/, $v));
|
|
}
|
|
if ($r->{$o} && $r->{$o."_not"}) {
|
|
push(@rv, &text('desc_'.$o.'_not', "<b>$v</b>"));
|
|
}
|
|
elsif ($r->{$o}) {
|
|
push(@rv, &text('desc_'.$o, "<b>$v</b>"));
|
|
}
|
|
}
|
|
if ($r->{'mac'}) {
|
|
if ($r->{'mac'}->[0] eq "any") {
|
|
push(@rv, &text('desc_mac1', "<b>$r->{'mac'}->[1]</b>"));
|
|
}
|
|
elsif ($r->{'mac'}->[1] eq "any") {
|
|
push(@rv, &text('desc_mac2', "<b>$r->{'mac'}->[0]</b>"));
|
|
}
|
|
else {
|
|
push(@rv, &text('desc_mac', "<b>$r->{'mac'}->[0]</b>",
|
|
"<b>$r->{'mac'}->[1]</b>"));
|
|
}
|
|
}
|
|
if ($r->{'limit'}) {
|
|
$limit = &text('desc_limit', $text{'desc_'.$r->{'limit'}->[0]}, $r->{'limit'}->[1]);
|
|
}
|
|
if ($r->{'dst-port'}) {
|
|
push(@rv, &text('desc_dstport', join(", ", @{$r->{'dst-port'}})));
|
|
}
|
|
if ($r->{'src-port'}) {
|
|
push(@rv, &text('desc_srcport', join(", ", @{$r->{'src-port'}})));
|
|
}
|
|
return @rv ? &text($_[1] ? 'desc_where' : 'desc_if',
|
|
join(" $text{'desc_and'} ", @rv)).$limit
|
|
: $text{$_[1] ? 'desc_all' : 'desc_always'}.$limit;
|
|
}
|
|
|
|
# words_to_orblock(&words)
|
|
sub words_to_orblock
|
|
{
|
|
local $st = shift(@{$_[0]});
|
|
while($_[0]->[0] ne $st) {
|
|
push(@or, shift(@{$_[0]}));
|
|
}
|
|
shift(@{$_[0]});
|
|
return \@or;
|
|
}
|
|
|
|
# orblock_to_words(&block)
|
|
sub orblock_to_words
|
|
{
|
|
if (ref($_[0])) {
|
|
return ( "{", @{$_[0]}, "}" );
|
|
}
|
|
else {
|
|
return ( $_[0] );
|
|
}
|
|
}
|
|
|
|
# real_action(name)
|
|
# Returns the proper name for some action
|
|
sub real_action
|
|
{
|
|
return $_[0] =~ /accept|pass|permit/ ? "allow" :
|
|
$_[0] =~ /drop/ ? "deny" :
|
|
$_[0] =~ /forward/ ? "fwd" : $_[0];
|
|
}
|
|
|
|
sub list_protocols
|
|
{
|
|
local @stdprotos = ( 'tcp', 'udp', 'icmp' );
|
|
local @otherprotos;
|
|
open(PROTOS, "</etc/protocols");
|
|
while(<PROTOS>) {
|
|
s/\r|\n//g;
|
|
s/#.*$//;
|
|
push(@otherprotos, $1) if (/^(\S+)\s+(\d+)/);
|
|
}
|
|
close(PROTOS);
|
|
@otherprotos = sort { lc($a) cmp lc($b) } @otherprotos;
|
|
return &unique(@stdprotos, @otherprotos);
|
|
}
|
|
|
|
# apply_rules([&rules])
|
|
# Apply the supplied firewall rules
|
|
sub apply_rules
|
|
{
|
|
local $conf = $_[0];
|
|
$conf ||= &get_config();
|
|
local $dir = &get_current_dir();
|
|
chdir("/");
|
|
local $fmt = &get_ipfw_format();
|
|
&system_logged("$config{'ipfw'} -f flush >/dev/null 2>&1");
|
|
if ($fmt == 0) {
|
|
# Apply each rule in turn
|
|
local $r;
|
|
foreach $r (@$conf) {
|
|
if (!$r->{'other'} &&
|
|
$r->{'num'} != 65535) { # skip auto-added final rule
|
|
local ($line) = &rule_lines($r, 1, 1);
|
|
$line = join(" ",
|
|
map { /^['"].*["']$/ ? $_ : quotemeta($_) }
|
|
split(/\s+/, $line));
|
|
local $cmd = "$config{'ipfw'} add $line";
|
|
$out = &backquote_logged("$cmd 2>&1 </dev/null");
|
|
return "<tt>$cmd</tt> failed : <tt>$out</tt>" if ($?);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
# The ipfw command can apply the whole file
|
|
local $out = &backquote_logged(
|
|
"$config{'ipfw'} ".quotemeta($ipfw_file)." 2>&1 </dev/null");
|
|
return "<tt>$config{'ipfw'} $ipfw_file</tt> failed : <tt>$out</tt>"
|
|
if ($?);
|
|
}
|
|
chdir($dir);
|
|
return undef;
|
|
}
|
|
|
|
# disable_rules()
|
|
# Returns the system to an 'accept all' state
|
|
sub disable_rules
|
|
{
|
|
local $dir = `pwd`;
|
|
chop($dir);
|
|
chdir("/");
|
|
&system_logged("$config{'ipfw'} -f flush >/dev/null 2>&1");
|
|
&system_logged("$config{'ipfw'} add allow ip from any to any >/dev/null 2>&1");
|
|
chdir($dir);
|
|
return undef;
|
|
}
|
|
|
|
# interface_choice(name, value, noignored)
|
|
sub interface_choice
|
|
{
|
|
local @ifaces;
|
|
if ($has_net_lib) {
|
|
return &net::interface_choice($_[0], $_[1],
|
|
$_[2] ? undef : "<$text{'edit_ignored'}>");
|
|
}
|
|
else {
|
|
return "<input name=$_[0] size=6 value='$_[1]'>";
|
|
}
|
|
}
|
|
|
|
sub create_firewall_init
|
|
{
|
|
&foreign_require("init", "init-lib.pl");
|
|
&foreign_require("cron", "cron-lib.pl");
|
|
&cron::create_wrapper("$module_config_directory/start.pl",
|
|
$module_name, "start.pl");
|
|
&cron::create_wrapper("$module_config_directory/stop.pl",
|
|
$module_name, "stop.pl");
|
|
&init::enable_at_boot($module_name,
|
|
"Start firewall",
|
|
"$module_config_directory/start.pl",
|
|
"$module_config_directory/stop.pl");
|
|
}
|
|
|
|
# list_cluster_servers()
|
|
# Returns a list of servers on which the firewall is managed
|
|
sub list_cluster_servers
|
|
{
|
|
&foreign_require("servers", "servers-lib.pl");
|
|
local %ids = map { $_, 1 } split(/\s+/, $config{'servers'});
|
|
return grep { $ids{$_->{'id'}} } &servers::list_servers();
|
|
}
|
|
|
|
# add_cluster_server(&server)
|
|
sub add_cluster_server
|
|
{
|
|
local @sids = split(/\s+/, $config{'servers'});
|
|
$config{'servers'} = join(" ", @sids, $_[0]->{'id'});
|
|
&save_module_config();
|
|
}
|
|
|
|
# delete_cluster_server(&server)
|
|
sub delete_cluster_server
|
|
{
|
|
local @sids = split(/\s+/, $config{'servers'});
|
|
$config{'servers'} = join(" ", grep { $_ != $_[0]->{'id'} } @sids);
|
|
&save_module_config();
|
|
}
|
|
|
|
# server_name(&server)
|
|
sub server_name
|
|
{
|
|
return $_[0]->{'desc'} ? $_[0]->{'desc'} : $_[0]->{'host'};
|
|
}
|
|
|
|
# copy_to_cluster([force])
|
|
# Copy all firewall rules from this server to those in the cluster
|
|
sub copy_to_cluster
|
|
{
|
|
return if (!$config{'servers'}); # no servers defined
|
|
return if (!$_[0] && $config{'cluster_mode'}); # only push out when applying
|
|
local $s;
|
|
foreach $s (&list_cluster_servers()) {
|
|
&remote_foreign_require($s, "ipfw", "ipfw-lib.pl");
|
|
local $rfile = &remote_eval($s, "ipfw", "\$ipfw_file");
|
|
&remote_write($s, $ipfw_file, $rfile);
|
|
}
|
|
}
|
|
|
|
# apply_cluster_configuration()
|
|
# Activate the current configuration on all servers in the cluster
|
|
sub apply_cluster_configuration
|
|
{
|
|
return undef if (!$config{'servers'});
|
|
if ($config{'cluster_mode'}) {
|
|
©_to_cluster(1);
|
|
}
|
|
local $s;
|
|
foreach $s (&list_cluster_servers()) {
|
|
&remote_foreign_require($s, "ipfw", "ipfw-lib.pl");
|
|
local $err = &remote_foreign_call($s, "ipfw", "apply_rules");
|
|
if ($err) {
|
|
return &text('apply_remote', $s->{'host'}, $err);
|
|
}
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# check_boot()
|
|
# Returns 1 if enabled at boot via an init script, 2 if enabled via rc.conf,
|
|
# -1 if a different file is enabled at boot, 0 otherwise
|
|
sub check_boot
|
|
{
|
|
&foreign_require("init", "init-lib.pl");
|
|
local $atboot = &init::action_status($module_name);
|
|
if ($atboot == 2) {
|
|
return 1;
|
|
}
|
|
if ($has_net_lib && defined(&net::get_rc_conf)) {
|
|
local %rc = &net::get_rc_conf();
|
|
if ($rc{'firewall_enable'} ne 'YES') {
|
|
# Disabled
|
|
return 0;
|
|
}
|
|
elsif ($rc{'firewall_type'} eq $ipfw_file) {
|
|
return 2;
|
|
}
|
|
elsif ($rc{'firewall_type'}) {
|
|
# A *different* file is enabled
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# enable_boot()
|
|
# Make sure ipfw gets started at boot. Uses rc.conf if possible
|
|
sub enable_boot
|
|
{
|
|
return 0 if (&check_boot()); # Already on
|
|
if ($has_net_lib && defined(&net::get_rc_conf) && &get_ipfw_format() == 1) {
|
|
# Add to rc.conf
|
|
local %rc = &net::get_rc_conf();
|
|
&lock_file("/etc/rc.conf");
|
|
&net::save_rc_conf('firewall_type', $ipfw_file);
|
|
&net::save_rc_conf('firewall_enable', 'YES');
|
|
&net::save_rc_conf('firewall_quiet', 'YES');
|
|
&unlock_file("/etc/rc.conf");
|
|
return 2;
|
|
}
|
|
else {
|
|
# Create init script
|
|
&create_firewall_init();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub disable_boot
|
|
{
|
|
local $mode = &check_boot();
|
|
return 0 if ($mode <= 0);
|
|
if ($mode == 1) {
|
|
# Turn off init script
|
|
&init::disable_at_boot($module_name);
|
|
}
|
|
elsif ($mode == 2) {
|
|
# Take out rc.conf entry
|
|
&lock_file("/etc/rc.conf");
|
|
&net::save_rc_conf('firewall_enable', 'NO');
|
|
&unlock_file("/etc/rc.conf");
|
|
}
|
|
return $mode;
|
|
}
|
|
|
|
# get_ipfw_format()
|
|
# Works out the IPFW file format we should use. Returns 1 for with 'add' at the
|
|
# start, vs 0 for without.
|
|
sub get_ipfw_format
|
|
{
|
|
if (defined($get_ipfw_format_cache)) {
|
|
return $get_ipfw_format_cache;
|
|
}
|
|
local $fmt;
|
|
if (open(FILE, "<".$ipfw_file)) {
|
|
# Check existing format
|
|
while(<FILE>) {
|
|
if (/^(\d+)\s/) {
|
|
# Numeric line
|
|
$fmt = 0;
|
|
last;
|
|
}
|
|
elsif (/\S/ && !/^\#/) {
|
|
# Add or other directive line
|
|
$fmt = 1;
|
|
last;
|
|
}
|
|
}
|
|
close(FILE);
|
|
}
|
|
if (!defined($fmt)) {
|
|
if (-r "/etc/rc.conf") {
|
|
# FreeBSD - use it's format
|
|
$fmt = 1;
|
|
}
|
|
else {
|
|
# Assume numeric format
|
|
$fmt = 0;
|
|
}
|
|
}
|
|
$get_ipfw_format_cache = $fmt;
|
|
return $fmt;
|
|
}
|
|
|
|
1;
|