diff options
author | Bob Gilligan <gilligan@vyatta.com> | 2009-01-21 17:27:31 -0800 |
---|---|---|
committer | Bob Gilligan <gilligan@vyatta.com> | 2009-01-21 17:27:31 -0800 |
commit | c1b35e85eff4bb00ed7e7098fa1aee54d919608a (patch) | |
tree | 55d0f19e36d14f4a6fa4e24d14949fa3ae769d15 /scripts | |
parent | cc811731ba98e6b5f76cfea66960c2c8e2943532 (diff) | |
download | vyatta-cfg-firewall-c1b35e85eff4bb00ed7e7098fa1aee54d919608a.tar.gz vyatta-cfg-firewall-c1b35e85eff4bb00ed7e7098fa1aee54d919608a.zip |
Initial support for IPv6.
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/firewall/vyatta-firewall.pl | 313 |
1 files changed, 226 insertions, 87 deletions
diff --git a/scripts/firewall/vyatta-firewall.pl b/scripts/firewall/vyatta-firewall.pl index 0ad6a77..9625c43 100755 --- a/scripts/firewall/vyatta-firewall.pl +++ b/scripts/firewall/vyatta-firewall.pl @@ -9,19 +9,37 @@ use Getopt::Long; # Send output of shell commands to syslog for debugging and so that # the user is not confused by it. Log at debug level, which is supressed # by default, so that we don't unnecessarily fill up the syslog file. - my $logger = 'logger -t firewall-cfg -p local0.debug --'; +# Enable printing debug output to stdout. +my $debug_flag = 0; + +# Enable sending debug output to syslog. +my $syslog_flag = 0; + my @updateints = (); GetOptions("setup" => \$setup, "teardown" => \$teardown, "update-rules" => \$updaterules, "update-interfaces=s{4}" => \@updateints, + "debug" => \$debug_flag, + "syslog" => \$syslog_flag ); -# mapping from config node to iptables table -my %table_hash = ( 'name' => 'filter', - 'modify' => 'mangle', ); +# mapping from config node to iptables/ip6tables table +my %table_hash = ( 'name' => 'filter', + 'ipv6-name' => 'filter', + 'modify' => 'mangle', + 'ipv6-modify' => 'mangle' ); + +# mapping from config node to iptables command. Note that this table +# has the same keys as %table hash, so a loop iterating through the +# keys of %table_hash can use the same keys to find the value associated +# with the key in this table. +my %cmd_hash = ( 'name' => 'iptables', + 'ipv6-name' => 'ip6tables', + 'modify' => 'iptables', + 'ipv6-modify' => 'ip6tables'); sub other_table { my $this = shift; @@ -29,12 +47,14 @@ sub other_table { } if (defined $setup) { - setup_iptables(); + setup_iptables(iptables); + setup_iptables(ip6tables); exit 0; } my $update_zero_count = 0; if (defined $updaterules) { + # Iterate through the top-level trees under "firewall" foreach (keys %table_hash) { update_rules($_); } @@ -45,6 +65,7 @@ if ($#updateints == 3) { my ($action, $int_name, $direction, $chain) = @updateints; my $tree = chain_configured(0, $chain, undef); my $table = $table_hash{$tree}; + my $iptables_cmd = $cmd_hash{$tree}; if ($action eq "update") { # make sure chain exists if (!defined($tree)) { @@ -55,18 +76,23 @@ if ($#updateints == 3) { } # chain must have been set up. no need to set up again. # user may specify a chain in a different tree. try to delete it - # from the "other" tree first. - update_ints('delete', $int_name, $direction, $chain, other_table($table)); + # from the "other" trees first. + foreach $other_tree (keys %table_hash) { + if ($other_tree ne $tree) { + update_ints('delete', $int_name, $direction, $chain, + $table_hash{$other_tree}, $cmd_hash{$other_tree}); + } + } # do update action. - update_ints(@updateints, $table); + update_ints(@updateints, $table, $iptables_cmd); } else { # delete if (defined($tree)) { - update_ints(@updateints, $table); + update_ints(@updateints, $table, $iptables_cmd); } else { - # chain not configured. try both tables. + # chain not configured. try all tables. foreach (keys %table_hash) { - update_ints(@updateints, $table_hash{$_}); + update_ints(@updateints, $table_hash{$_}, $cmd_hash{$_}); } } } @@ -77,7 +103,7 @@ if ($#updateints == 3) { if (defined $teardown) { foreach (keys %table_hash) { $update_zero_count += 1; - teardown_iptables($table_hash{$_}); + teardown_iptables($table_hash{$_}, $cmd_hash{$_}); } exit 0; } @@ -95,29 +121,73 @@ sub help() { print "\n"; } +sub run_cmd($$$) { + my ($cmd_to_run, $redirect_flag, $logger_flag) = @_; + + my $cmd_extras; + + if ($debug_flag) { + print "DEBUG: Running: $cmd_to_run \n"; + } + + if ($syslog_flag) { + system("$logger DEBUG: Running: $cmd_to_run"); + } + + if ($redirect_flag) { + $cmd_extras = ' 2>&1'; + } + + if ($logger_flag) { + $cmd_extras = "$cmd_extras | $logger"; + } + + system("$cmd_to_run $cmd_extras"); +} + +sub log_msg($) { + my $message = shift; + + if ($debug_flag) { + print "DEBUG: $message"; + } + + if ($syslog_flag) { + system("$logger DEBUG: $message"); + } +} + sub update_rules($) { - my $tree = shift; - my $table = $table_hash{$tree}; + my $tree = shift; # name, modify, ipv6-name or ipv6-modify + my $table = $table_hash{$tree}; # "filter" or "mangle" + my $iptables_cmd = $cmd_hash{$tree}; # "iptables" or "ip6tables" my $config = new Vyatta::Config; my $name = undef; my %nodes = (); - system ("$logger Executing update_rules."); + log_msg "update_rules: $tree $table $iptables_cmd\n"; $config->setLevel("firewall $tree"); %nodes = $config->listNodeStatus(); if ((scalar (keys %nodes)) == 0) { + + log_msg "update_rules: no nodes at this level \n"; + # no names. teardown the user chains and return. $update_zero_count += 1; - teardown_iptables($table); + teardown_iptables($table, $iptables_cmd); return; } # by default, nothing needs to be tracked. my $stateful = 0; + # Iterate through ruleset names under "name" or "modify" for $name (keys %nodes) { + + log_msg "update_rules: status of node $name is $nodes{$name} \n"; + if ($nodes{$name} eq "static") { # not changed. check if stateful. $config->setLevel("firewall $tree $name rule"); @@ -132,6 +202,7 @@ sub update_rules($) { } next; } elsif ($nodes{$name} eq "added") { + # create the chain my $ctree = chain_configured(2, $name, $tree); if (defined($ctree)) { @@ -140,17 +211,20 @@ sub update_rules($) { . "Rule set name \"$name\" already used in \"$ctree\"\n"; exit 1; } - setup_chain($table, "$name"); + setup_chain($table, "$name", $iptables_cmd); # handle the rules below. } elsif ($nodes{$name} eq "deleted") { + + log_msg "node $name is $nodes{$name} \n"; + # delete the chain - if (chain_referenced($table, $name)) { + if (chain_referenced($table, $name, $iptables_cmd)) { # disallow deleting a chain if it's still referenced print STDERR 'Firewall config error: ' . "Cannot delete rule set \"$name\" (still in use)\n"; exit 1; } - delete_chain($table, "$name"); + delete_chain($table, "$name", $iptables_cmd); next; } elsif ($nodes{$name} eq "changed") { # handle the rules below. @@ -166,9 +240,10 @@ sub update_rules($) { # no rules. flush the user rules. # note that this clears the counters on the default DROP rule. # we could delete rule one by one if those are important. - system("$logger Running: iptables -F $name"); - system("iptables -t $table -F $name 2>&1 | $logger"); - add_default_drop_rule($table, $name); +# system("$logger Running: $iptables_cmd -F $name"); +# system("$iptables_cmd -t $table -F $name 2>&1 | $logger"); + run_cmd("$iptables_cmd -t $table -F $name", 1, 1); + add_default_drop_rule($table, $name, $iptables_cmd); next; } @@ -199,9 +274,12 @@ sub update_rules($) { if (!defined) { last; } - system ("$logger Insert iptables $table $name $iptablesrule $_"); - system ("iptables -t $table --insert $name $iptablesrule $_"); - die "iptables error: $! - $_" if ($? >> 8); + + # system ("$logger Insert $iptables_cmd $table $name $iptablesrule $_"); + # system ("$iptables_cmd -t $table --insert $name $iptablesrule $_"); + run_cmd("$iptables_cmd -t $table --insert $name $iptablesrule $_", + 0, 0); + die "$iptables_cmd error: $! - $_" if ($? >> 8); $iptablesrule++; } } elsif ("$rulehash{$rule}" eq "changed") { @@ -222,18 +300,22 @@ sub update_rules($) { my $ipt_rules = $oldnode->get_num_ipt_rules(); for (1 .. $ipt_rules) { - system ("$logger Delete iptables $table $name $iptablesrule"); - system ("iptables -t $table --delete $name $iptablesrule"); - die "iptables error: $! - $rule" if ($? >> 8); + # system ("$logger Delete $iptables_cmd $table $name $iptablesrule"); + # system ("$iptables_cmd -t $table --delete $name $iptablesrule"); + run_cmd("$iptables_cmd -t $table --delete $name $iptablesrule", 0, + 0); + die "$iptables_cmd error: $! - $rule" if ($? >> 8); } foreach (@rule_strs) { if (!defined) { last; } - system ("$logger Insert iptables $table $name $iptablesrule $_"); - system ("iptables -t $table --insert $name $iptablesrule $_"); - die "iptables error: $! - $rule_str" if ($? >> 8); + # system ("$logger Insert $iptables_cmd $table $name $iptablesrule $_"); + # system ("$iptables_cmd -t $table --insert $name $iptablesrule $_"); + run_cmd("$iptables_cmd -t $table --insert $name $iptablesrule $_", + 0, 0); + die "$iptables_cmd error: $! - $rule_str" if ($? >> 8); $iptablesrule++; } } elsif ("$rulehash{$rule}" eq "deleted") { @@ -242,17 +324,20 @@ sub update_rules($) { my $ipt_rules = $node->get_num_ipt_rules(); for (1 .. $ipt_rules) { - system ("$logger Delete iptables $table $name $iptablesrule"); - system ("iptables -t $table --delete $name $iptablesrule"); - die "iptables error: $! - $rule" if ($? >> 8); + # system ("$logger Delete $iptables_cmd $table $name $iptablesrule"); + # system ("$iptables_cmd -t $table --delete $name $iptablesrule"); + run_cmd("$iptables_cmd -t $table --delete $name $iptablesrule", + 0, 0); + die "$iptables_cmd error: $! - $rule" if ($? >> 8); } } } } + if ($stateful) { - enable_fw_conntrack(); + enable_fw_conntrack($iptables_cmd); } else { - disable_fw_conntrack(); + disable_fw_conntrack($iptables_cmd); } } @@ -282,11 +367,13 @@ sub chain_configured($$$) { } sub update_ints() { - my ($action, $int_name, $direction, $chain, $table) = @_; + my ($action, $int_name, $direction, $chain, $table, $iptables_cmd) = @_; my $interface = undef; + log_msg "update_ints: @_ \n"; + if (! defined $action || ! defined $int_name || ! defined $direction - || ! defined $chain || ! defined $table) { + || ! defined $chain || ! defined $table || ! defined $iptables_cmd) { return -1; } @@ -321,14 +408,32 @@ sub update_ints() { }; } + # In the update case, we want to see if the new rule will replace one + # that is already in the table. In the delete case, we need to find + # the rule in the table that we need to delete. Either way, we + # start by listing the rules rules already in the table. my $grep = "egrep ^[0-9] | grep $int_name"; my @lines - = `iptables -t $table -L $direction -n -v --line-numbers | $grep`; + = `$iptables_cmd -t $table -L $direction -n -v --line-numbers | $grep`; my ($cmd, $num, $oldchain, $in, $out, $ignore) = (undef, undef, undef, undef, undef, undef); + foreach (@lines) { - ($num, $ignore, $ignore, $oldchain, $ignore, $ignore, $in, $out, - $ignore, $ignore) = split /\s+/; + # Parse the line representing one rule in the table. Note that + # there is a slight difference in output format between the "iptables" + # and "ip6tables" comands. The "iptables" command displays "--" in + # the "opt" column, while the "ip6tables" command leaves that + # column blank. + if ($iptables_cmd eq "iptables") { + ($num, $ignore, $ignore, $oldchain, $ignore, $ignore, $in, $out, + $ignore, $ignore) = split /\s+/; + } else { + ($num, $ignore, $ignore, $oldchain, $ignore, $in, $out, + $ignore, $ignore) = split /\s+/; + + } + + # Look for a matching rule... if (($dir_str eq 'in' && $in eq $int_name) || ($dir_str eq 'out' && $out eq $int_name) || ($dir_str eq 'local' && $in eq $int_name)) { @@ -344,6 +449,7 @@ sub update_ints() { last; } } + if (!defined($cmd)) { # no matching rule if ($action eq 'update') { @@ -359,37 +465,43 @@ sub update_ints() { # no match. do nothing. return 0 if (!defined($cmd)); - system ("$logger Running: iptables -t $table $cmd"); - system("iptables -t $table $cmd"); + # system ("$logger Running: $iptables_cmd -t $table $cmd"); + # system("$iptables_cmd -t $table $cmd"); + run_cmd("$iptables_cmd -t $table $cmd", 0, 0); exit 1 if ($? >> 8); # the following delete_chain is probably no longer necessary since we # now disallow deleting a chain when it's still referenced if ($action eq 'replace' || $action eq 'delete') { if (!defined(chain_configured(2, $oldchain, undef))) { - if (!chain_referenced($table, $oldchain)) { - delete_chain($table, $oldchain); + if (!chain_referenced($table, $oldchain, $iptables_cmd)) { + delete_chain($table, $oldchain, $iptables_cmd); } } } return 0; } -sub enable_fw_conntrack { +sub enable_fw_conntrack($) { # potentially we can add rules in the FW_CONNTRACK chain to provide # finer-grained control over which packets are tracked. - system("$logger Running: iptables -t raw -R FW_CONNTRACK 1 -J ACCEPT"); - system("iptables -t raw -R FW_CONNTRACK 1 -j ACCEPT 2>&1 | $logger"); + my $iptables_cmd = shift; + # system("$logger Running: $iptables_cmd -t raw -R FW_CONNTRACK 1 -J ACCEPT"); + # system("$iptables_cmd -t raw -R FW_CONNTRACK 1 -j ACCEPT 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -R FW_CONNTRACK 1 -j ACCEPT", 1, 1); } -sub disable_fw_conntrack { - system("$logger Running: iptables -t raw -R FW_CONNTRACK 1 -j RETURN"); - system("iptables -t raw -R FW_CONNTRACK 1 -j RETURN 2>&1 | $logger"); +sub disable_fw_conntrack($) { + my $iptables_cmd = shift; + # system("$logger Running: $iptables_cmd -t raw -R FW_CONNTRACK 1 -j RETURN"); + # system("$iptables_cmd -t raw -R FW_CONNTRACK 1 -j RETURN 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -R FW_CONNTRACK 1 -j RETURN", 1, 1); } -sub teardown_iptables($) { - my $table = shift; - my @chains = `iptables -L -n -t $table`; +sub teardown_iptables($$) { + my ($table, $iptables_cmd) = @_; + log_msg "teardown_iptables executing: $iptables_cmd -L -n -t $table\n"; + my @chains = `$iptables_cmd -L -n -t $table`; my $chain; # $chain is going to look like this... @@ -402,7 +514,7 @@ sub teardown_iptables($) { if (($chain =~ /references/) && !($chain =~ /VYATTA_\w+_HOOK/)) { ($chain) = split /\(/, $chain; $chain =~ s/\s//g; - delete_chain($table, "$chain"); + delete_chain($table, "$chain", $iptables_cmd); } } } @@ -410,53 +522,69 @@ sub teardown_iptables($) { # remove the conntrack setup. return if ($update_zero_count != scalar(keys %table_hash)); my @lines - = `iptables -t raw -L PREROUTING -vn --line-numbers | egrep ^[0-9]`; + = `$iptables_cmd -t raw -L PREROUTING -vn --line-numbers | egrep ^[0-9]`; foreach (@lines) { my ($num, $ignore, $ignore, $chain, $ignore, $ignore, $in, $out, $ignore, $ignore) = split /\s+/; if ($chain eq "FW_CONNTRACK") { - system("iptables -t raw -D PREROUTING $num 2>&1 | $logger"); - system("iptables -t raw -D OUTPUT $num 2>&1 | $logger"); - system("iptables -t raw -F FW_CONNTRACK 2>&1 | $logger"); - system("iptables -t raw -X FW_CONNTRACK 2>&1 | $logger"); + # system("$iptables_cmd -t raw -D PREROUTING $num 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -D PREROUTING", 1, 1); + # system("$iptables_cmd -t raw -D OUTPUT $num 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -D OUTPUT $num", 1, 1); + # system("$iptables_cmd -t raw -F FW_CONNTRACK 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -F FW_CONNTRACK", 1, 1); + # system("$iptables_cmd -t raw -X FW_CONNTRACK 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -X FW_CONNTRACK", 1, 1); last; } } } -sub setup_iptables() { - foreach (keys %table_hash) { +sub setup_iptables($) { + my $iptables_cmd = shift; + foreach $table (filter, mangle) { $update_zero_count += 1; - teardown_iptables($table_hash{$_}); + teardown_iptables($table, $iptables_cmd); } + # by default, nothing is tracked (the last rule in raw/PREROUTING). - system("iptables -t raw -N FW_CONNTRACK 2>&1 | $logger"); - system("iptables -t raw -A FW_CONNTRACK -j RETURN 2>&1 | $logger"); - system("iptables -t raw -I PREROUTING 1 -j FW_CONNTRACK 2>&1 | $logger"); - system("iptables -t raw -I OUTPUT 1 -j FW_CONNTRACK 2>&1 | $logger"); + # system("$iptables_cmd -t raw -N FW_CONNTRACK 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -N FW_CONNTRACK", 1 , 1); + # system("$iptables_cmd -t raw -A FW_CONNTRACK -j RETURN 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -A FW_CONNTRACK -j RETURN", 1, 1); + # system("$iptables_cmd -t raw -I PREROUTING 1 -j FW_CONNTRACK 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -I PREROUTING 1 -j FW_CONNTRACK", 1, 1); + # system("$iptables_cmd -t raw -I OUTPUT 1 -j FW_CONNTRACK 2>&1 | $logger"); + run_cmd("$iptables_cmd -t raw -I OUTPUT 1 -j FW_CONNTRACK", 1, 1); return 0; } -sub add_default_drop_rule($$) { - my ($table, $chain) = @_; - system("iptables -t $table -A $chain -j DROP 2>&1 | $logger"); +sub add_default_drop_rule($$$) { + my ($table, $chain, $iptables_cmd) = @_; + # system("$iptables_cmd -t $table -A $chain -j DROP 2>&1 | $logger"); + run_cmd("$iptables_cmd -t $table -A $chain -j DROP", 1, 1); } -sub setup_chain($$) { - my ($table, $chain) = @_; - my $configured = `iptables -t $table -n -L $chain 2>&1 | head -1`; +sub setup_chain($$$) { + my ($table, $chain, $iptables_cmd) = @_; + + my $configured = `$iptables_cmd -t $table -n -L $chain 2>&1 | head -1`; $_ = $configured; if (!/^Chain $chain/) { - system("iptables -t $table --new-chain $chain"); + # system("$iptables_cmd -t $table --new-chain $chain"); + run_cmd("$iptables_cmd -t $table --new-chain $chain", 0, 0); die "iptables error: $table $chain --new-chain: $!" if ($? >> 8); - add_default_drop_rule($table, $chain); + add_default_drop_rule($table, $chain, $iptables_cmd); } } -sub chain_referenced($$) { - my ($table, $chain) = @_; - my $line = `iptables -t $table -n -L $chain 2>/dev/null |head -n1`; +sub chain_referenced($$$) { + my ($table, $chain, $iptables_cmd) = @_; + + log_msg "chain_referenced executing: $iptables_cmd -t $table -n -L $chain \n"; + + my $line = `$iptables_cmd -t $table -n -L $chain 2>/dev/null |head -n1`; if ($line =~ m/^Chain $chain \((\d+) references\)$/) { if ($1 > 0) { return 1; @@ -465,20 +593,31 @@ sub chain_referenced($$) { return 0; } -sub delete_chain($$) { - my ($table, $chain) = @_; - my $configured = `iptables -t $table -n -L $chain 2>&1 | head -1`; +sub delete_chain($$$) { + my ($table, $chain, $iptables_cmd) = @_; + + log_msg "delete_chain executing: $iptables_cmd -t $table -n -L $chain \n"; + + my $configured = `$iptables_cmd -t $table -n -L $chain 2>&1 | head -1`; if ($configured =~ /^Chain $chain/) { - system("iptables -t $table --flush $chain"); - die "iptables error: $table $chain --flush: $!" if ($? >> 8); - if (!chain_referenced($table, $chain)) { - system("iptables -t $table --delete-chain $chain"); - die "iptables error: $table $chain --delete-chain: $!" if ($? >> 8); + # system("$iptables_cmd -t $table --flush $chain"); + run_cmd("$iptables_cmd -t $table --flush $chain", 0, 0); + die "$iptables_cmd error: $table $chain --flush: $!" if ($? >> 8); + if (!chain_referenced($table, $chain, $iptables_cmd)) { + # system("$iptables_cmd -t $table --delete-chain $chain"); + run_cmd("$iptables_cmd -t $table --delete-chain $chain", 0, 0); + die "$iptables_cmd error: $table $chain --delete-chain: $!" if ($? >> 8); } else { - add_default_drop_rule($table, $chain); + add_default_drop_rule($table, $chain, $iptables_cmd); } } } sub numerically { $a <=> $b; } + +# Local Variables: +# mode: perl +# indent-tabs-mode: nil +# perl-indent-level: 2 +# End: |