diff options
author | An-Cheng Huang <ancheng@vyatta.com> | 2008-05-09 18:26:22 -0700 |
---|---|---|
committer | An-Cheng Huang <ancheng@vyatta.com> | 2008-05-09 18:26:22 -0700 |
commit | 648b2b2ac928461c8a83a43e0f455edb96552ddd (patch) | |
tree | 23bae9090956ce3a9ff184829831d6415272be44 /scripts/firewall/vyatta-firewall.pl | |
parent | bf5ed000329de5cccb9af7a8d46a3c4ef3079486 (diff) | |
download | vyatta-cfg-firewall-648b2b2ac928461c8a83a43e0f455edb96552ddd.tar.gz vyatta-cfg-firewall-648b2b2ac928461c8a83a43e0f455edb96552ddd.zip |
add mangle table support to firewall configuration. initial implementation
allows MARK and DSCP jump targets.
Diffstat (limited to 'scripts/firewall/vyatta-firewall.pl')
-rwxr-xr-x | scripts/firewall/vyatta-firewall.pl | 228 |
1 files changed, 152 insertions, 76 deletions
diff --git a/scripts/firewall/vyatta-firewall.pl b/scripts/firewall/vyatta-firewall.pl index e01f30c..4ca5104 100755 --- a/scripts/firewall/vyatta-firewall.pl +++ b/scripts/firewall/vyatta-firewall.pl @@ -19,23 +19,64 @@ GetOptions("setup" => \$setup, "update-interfaces=s{4}" => \@updateints, ); +# mapping from config node to iptables table +my %table_hash = ( 'name' => 'filter', + 'mangle' => 'mangle', ); + +sub other_table { + my $this = shift; + return (($this eq 'filter') ? 'mangle' : 'filter'); +} + if (defined $setup) { setup_iptables(); exit 0; } if (defined $updaterules) { - update_rules(); + foreach (keys %table_hash) { + update_rules($_); + } exit 0; } if ($#updateints == 3) { - update_ints(@updateints); + my ($action, $int_name, $direction, $chain) = @updateints; + my $tree = chain_configured(0, $chain, undef); + my $table = $table_hash{$tree}; + if ($action eq "update") { + # make sure chain exists + if (!defined($tree)) { + # require chain to be configured in "firewall" first + print STDERR 'Firewall config error: ' . + "Rule set \"$chain\" is not configured\n"; + exit 1; + } + # 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)); + # do update action. + update_ints(@updateints, $table); + } else { + # delete + if (defined($tree)) { + update_ints(@updateints, $table); + } else { + # chain not configured. try both tables. + foreach (keys %table_hash) { + update_ints(@updateints, $table_hash{$_}); + } + } + } + exit 0; } if (defined $teardown) { - teardown_iptables(); + foreach (keys %table_hash) { + teardown_iptables($table_hash{$_}); + } exit 0; } @@ -52,19 +93,21 @@ sub help() { print "\n"; } -sub update_rules() { +sub update_rules($) { + my $tree = shift; + my $table = $table_hash{$tree}; my $config = new VyattaConfig; my $name = undef; my %nodes = (); system ("$logger Executing update_rules."); - $config->setLevel("firewall name"); + $config->setLevel("firewall $tree"); %nodes = $config->listNodeStatus(); if ((scalar (keys %nodes)) == 0) { # no names. teardown the user chains and return. - teardown_iptables(); + teardown_iptables($table); return; } @@ -74,11 +117,11 @@ sub update_rules() { for $name (keys %nodes) { if ($nodes{$name} eq "static") { # not changed. check if stateful. - $config->setLevel("firewall name $name rule"); + $config->setLevel("firewall $tree $name rule"); my @rules = $config->listOrigNodes(); foreach (sort numerically @rules) { my $node = new VyattaIpTablesRule; - $node->setupOrig("firewall name $name rule $_"); + $node->setupOrig("firewall $tree $name rule $_"); if ($node->is_stateful()) { $stateful = 1; last; @@ -87,18 +130,31 @@ sub update_rules() { next; } elsif ($nodes{$name} eq "added") { # create the chain - setup_chain("$name"); + my $ctree = chain_configured(2, $name, $tree); + if (defined($ctree)) { + # chain name must be unique in both trees + print STDERR 'Firewall config error: ' + . "Rule set name \"$name\" already used in \"$ctree\"\n"; + exit 1; + } + setup_chain($table, "$name"); # handle the rules below. } elsif ($nodes{$name} eq "deleted") { # delete the chain - delete_chain("$name"); + if (chain_referenced($table, $name)) { + # 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"); next; } elsif ($nodes{$name} eq "changed") { # handle the rules below. } # set our config level to rule and get the rule numbers - $config->setLevel("firewall name $name rule"); + $config->setLevel("firewall $tree $name rule"); # Let's find the status of the rule nodes my %rulehash = (); @@ -108,8 +164,8 @@ sub update_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 -F $name 2>&1 | $logger"); - add_default_drop_rule($name); + system("iptables -t $table -F $name 2>&1 | $logger"); + add_default_drop_rule($table, $name); next; } @@ -117,7 +173,7 @@ sub update_rules() { foreach $rule (sort numerically keys %rulehash) { if ("$rulehash{$rule}" eq "static") { my $node = new VyattaIpTablesRule; - $node->setupOrig("firewall name $name rule $rule"); + $node->setupOrig("firewall $tree $name rule $rule"); if ($node->is_stateful()) { $stateful = 1; } @@ -126,7 +182,7 @@ sub update_rules() { } elsif ("$rulehash{$rule}" eq "added") { # create a new iptables object of the current rule my $node = new VyattaIpTablesRule; - $node->setup("firewall name $name rule $rule"); + $node->setup("firewall $tree $name rule $rule"); if ($node->is_stateful()) { $stateful = 1; } @@ -140,17 +196,17 @@ sub update_rules() { if (!defined) { last; } - system ("$logger Running: iptables --insert $name $iptablesrule $_"); - system ("iptables --insert $name $iptablesrule $_"); + system ("$logger Insert iptables $table $name $iptablesrule $_"); + system ("iptables -t $table --insert $name $iptablesrule $_"); die "iptables error: $! - $_" if ($? >> 8); $iptablesrule++; } } elsif ("$rulehash{$rule}" eq "changed") { # create a new iptables object of the current rule my $oldnode = new VyattaIpTablesRule; - $oldnode->setupOrig("firewall name $name rule $rule"); + $oldnode->setupOrig("firewall $tree $name rule $rule"); my $node = new VyattaIpTablesRule; - $node->setup("firewall name $name rule $rule"); + $node->setup("firewall $tree $name rule $rule"); if ($node->is_stateful()) { $stateful = 1; } @@ -163,8 +219,8 @@ sub update_rules() { my $ipt_rules = $oldnode->get_num_ipt_rules(); for (1 .. $ipt_rules) { - system ("$logger Running: iptables --delete $name $iptablesrule"); - system ("iptables --delete $name $iptablesrule"); + system ("$logger Delete iptables $table $name $iptablesrule"); + system ("iptables -t $table --delete $name $iptablesrule"); die "iptables error: $! - $rule" if ($? >> 8); } @@ -172,19 +228,19 @@ sub update_rules() { if (!defined) { last; } - system ("$logger Running: iptables --insert $name $iptablesrule $_"); - system ("iptables --insert $name $iptablesrule $_"); + system ("$logger Insert iptables $table $name $iptablesrule $_"); + system ("iptables -t $table --insert $name $iptablesrule $_"); die "iptables error: $! - $rule_str" if ($? >> 8); $iptablesrule++; } } elsif ("$rulehash{$rule}" eq "deleted") { my $node = new VyattaIpTablesRule; - $node->setupOrig("firewall name $name rule $rule"); + $node->setupOrig("firewall $tree $name rule $rule"); my $ipt_rules = $node->get_num_ipt_rules(); for (1 .. $ipt_rules) { - system ("$logger Running: iptables --delete $name $iptablesrule"); - system ("iptables --delete $name $iptablesrule"); + system ("$logger Delete iptables $table $name $iptablesrule"); + system ("iptables -t $table --delete $name $iptablesrule"); die "iptables error: $! - $rule" if ($? >> 8); } } @@ -197,33 +253,44 @@ sub update_rules() { } } -sub chain_configured($) { - my $chain = shift; +# returns the "tree" in which the chain is configured; undef if not configured. +# mode: 0: check if the chain is configured in either tree. +# 1: check if it is configured in the specified tree. +# 2: check if it is configured in the "other" tree. +sub chain_configured($$$) { + my ($mode, $chain, $tree) = @_; my $config = new VyattaConfig; my %chains = (); - $config->setLevel("firewall name"); - %chains = $config->listNodeStatus(); + foreach (keys %table_hash) { + next if ($mode == 1 && $_ ne $tree); + next if ($mode == 2 && $_ eq $tree); + + $config->setLevel("firewall $_"); + %chains = $config->listNodeStatus(); - if (grep(/^$chain$/, (keys %chains))) { - if ($chains{$chain} ne "deleted") { - return 1; + if (grep(/^$chain$/, (keys %chains))) { + if ($chains{$chain} ne "deleted") { + return $_; + } } } - return 0; + return undef; } sub update_ints() { - my ($action, $int_name, $direction, $chain) = @_; + my ($action, $int_name, $direction, $chain, $table) = @_; my $interface = undef; - if (! defined $action || ! defined $int_name || ! defined $direction || ! defined $chain) { + if (! defined $action || ! defined $int_name || ! defined $direction + || ! defined $chain || ! defined $table) { return -1; } - - if ($action eq "update") { - # make sure chain exists - setup_chain($chain); + + if ($action ne 'delete' && $table eq 'mangle' && $direction =~ /^local/) { + print STDERR 'Firewall config error: ' . + "Mangle rule set \"$chain\" cannot be used for \"local\"\n"; + exit 1; } $_ = $direction; @@ -231,27 +298,28 @@ sub update_ints() { CASE: { /^in/ && do { - $direction = "FORWARD"; + $direction = ($table eq 'mangle') ? 'PREROUTING' : 'FORWARD'; $interface = "--in-interface $int_name"; last CASE; }; /^out/ && do { - $direction = "FORWARD"; + $direction = ($table eq 'mangle') ? 'POSTROUTING' : 'FORWARD'; $interface = "--out-interface $int_name"; last CASE; }; /^local/ && do { + # mangle disallowed above $direction = "INPUT"; $interface = "--in-interface $int_name"; last CASE; }; } - my $grep = "| grep $int_name"; + my $grep = "egrep ^[0-9] | grep $int_name"; my @lines - = `iptables -L $direction -n -v --line-numbers | egrep ^[0-9] $grep`; + = `iptables -t $table -L $direction -n -v --line-numbers | $grep`; my ($cmd, $num, $oldchain, $in, $out, $ignore) = (undef, undef, undef, undef, undef, undef); foreach (@lines) { @@ -280,18 +348,23 @@ sub update_ints() { $cmd = "--insert $direction 1 $interface --jump $chain"; } else { # delete non-existent rule! - die 'Error updating interfaces: no matching rule to delete'; + # not an error. rule may be in the other table. } } - system ("$logger Running: iptables $cmd"); - system("iptables $cmd"); + # no match. do nothing. + return 0 if (!defined($cmd)); + + system ("$logger Running: iptables -t $table $cmd"); + system("iptables -t $table $cmd"); 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 (!chain_configured($oldchain)) { - if (!chain_referenced($oldchain)) { - delete_chain($oldchain); + if (!defined(chain_configured(2, $oldchain, undef))) { + if (!chain_referenced($table, $oldchain)) { + delete_chain($table, $oldchain); } } } @@ -310,8 +383,9 @@ sub disable_fw_conntrack { system("iptables -t raw -R FW_CONNTRACK 1 -j RETURN 2>&1 | $logger"); } -sub teardown_iptables() { - my @chains = `iptables -L -n`; +sub teardown_iptables($) { + my $table = shift; + my @chains = `iptables -L -n -t $table`; my $chain; # $chain is going to look like this... @@ -324,7 +398,7 @@ sub teardown_iptables() { if (($chain =~ /references/) && !($chain =~ /VYATTA_\w+_HOOK/)) { ($chain) = split /\(/, $chain; $chain =~ s/\s//g; - delete_chain("$chain"); + delete_chain($table, "$chain"); } } } @@ -346,7 +420,9 @@ sub teardown_iptables() { } sub setup_iptables() { - teardown_iptables(); + foreach (keys %table_hash) { + teardown_iptables($table_hash{$_}); + } # 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"); @@ -355,26 +431,26 @@ sub setup_iptables() { return 0; } -sub add_default_drop_rule { - my $chain = shift; - system("iptables -A $chain -j DROP 2>&1 | $logger"); +sub add_default_drop_rule($$) { + my ($table, $chain) = @_; + system("iptables -t $table -A $chain -j DROP 2>&1 | $logger"); } -sub setup_chain($) { - my $chain = shift; - my $configured = `iptables -n -L $chain 2>&1 | head -1`; +sub setup_chain($$) { + my ($table, $chain) = @_; + my $configured = `iptables -t $table -n -L $chain 2>&1 | head -1`; $_ = $configured; if (!/^Chain $chain/) { - system("iptables --new-chain $chain"); - die "iptables error: $chain --new-chain: $!" if ($? >> 8); - add_default_drop_rule($chain); + system("iptables -t $table --new-chain $chain"); + die "iptables error: $table $chain --new-chain: $!" if ($? >> 8); + add_default_drop_rule($table, $chain); } } -sub chain_referenced($) { - my $chain = shift; - my $line = `iptables -n -L $chain |head -n1`; +sub chain_referenced($$) { + my ($table, $chain) = @_; + my $line = `iptables -t $table -n -L $chain 2>/dev/null |head -n1`; if ($line =~ m/^Chain $chain \((\d+) references\)$/) { if ($1 > 0) { return 1; @@ -383,18 +459,18 @@ sub chain_referenced($) { return 0; } -sub delete_chain($) { - my $chain = shift; - my $configured = `iptables -n -L $chain 2>&1 | head -1`; +sub delete_chain($$) { + my ($table, $chain) = @_; + my $configured = `iptables -t $table -n -L $chain 2>&1 | head -1`; if ($configured =~ /^Chain $chain/) { - system("iptables --flush $chain"); - die "iptables error: $chain --flush: $!" if ($? >> 8); - if (!chain_referenced($chain)) { - system("iptables --delete-chain $chain"); - die "iptables error: $chain --delete-chain: $!" if ($? >> 8); + 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); } else { - add_default_drop_rule($chain); + add_default_drop_rule($table, $chain); } } } |