summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAn-Cheng Huang <ancheng@vyatta.com>2008-05-09 18:26:22 -0700
committerAn-Cheng Huang <ancheng@vyatta.com>2008-05-09 18:26:22 -0700
commit648b2b2ac928461c8a83a43e0f455edb96552ddd (patch)
tree23bae9090956ce3a9ff184829831d6415272be44
parentbf5ed000329de5cccb9af7a8d46a3c4ef3079486 (diff)
downloadvyatta-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.
-rw-r--r--scripts/firewall/VyattaIpTablesRule.pm36
-rwxr-xr-xscripts/firewall/vyatta-firewall.pl228
-rw-r--r--templates/firewall/mangle/node.def4
-rw-r--r--templates/firewall/mangle/node.tag/description/node.def2
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.def4
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/action/node.def4
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/description/node.def2
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/destination/address/node.def9
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/destination/node.def1
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/destination/port/node.def8
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/icmp/code/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/icmp/node.def1
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/icmp/type/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/log/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/modify/dscp/node.def4
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/modify/mark/node.def2
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/modify/node.def1
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/protocol/node.def8
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/source/address/node.def9
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/source/mac-address/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/source/node.def1
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/source/port/node.def8
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/state/established/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/state/invalid/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/state/new/node.def3
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/state/node.def1
-rw-r--r--templates/firewall/mangle/node.tag/rule/node.tag/state/related/node.def3
27 files changed, 280 insertions, 77 deletions
diff --git a/scripts/firewall/VyattaIpTablesRule.pm b/scripts/firewall/VyattaIpTablesRule.pm
index a4ec902..04c82f0 100644
--- a/scripts/firewall/VyattaIpTablesRule.pm
+++ b/scripts/firewall/VyattaIpTablesRule.pm
@@ -20,6 +20,8 @@ my %fields = (
_log => undef,
_icmp_code => undef,
_icmp_type => undef,
+ _mod_mark => undef,
+ _mod_dscp => undef,
);
my %dummy_rule = (
@@ -35,6 +37,8 @@ my %dummy_rule = (
_log => undef,
_icmp_code => undef,
_icmp_type => undef,
+ _mod_mark => undef,
+ _mod_dscp => undef,
);
sub new {
@@ -75,6 +79,8 @@ sub setup {
$self->{_log} = $config->returnValue("log");
$self->{_icmp_code} = $config->returnValue("icmp code");
$self->{_icmp_type} = $config->returnValue("icmp type");
+ $self->{_mod_mark} = $config->returnValue("modify mark");
+ $self->{_mod_dscp} = $config->returnValue("modify dscp");
# TODO: need $config->exists("$level source") in VyattaConfig.pm
$src->setup("$level source");
@@ -104,6 +110,8 @@ sub setupOrig {
$self->{_log} = $config->returnOrigValue("log");
$self->{_icmp_code} = $config->returnOrigValue("icmp code");
$self->{_icmp_type} = $config->returnOrigValue("icmp type");
+ $self->{_mod_mark} = $config->returnOrigValue("modify mark");
+ $self->{_mod_dscp} = $config->returnOrigValue("modify dscp");
# TODO: need $config->exists("$level source") in VyattaConfig.pm
$src->setupOrig("$level source");
@@ -123,6 +131,8 @@ sub print {
print "log: $self->{_log}\n" if defined $self->{_log};
print "icmp code: $self->{_icmp_code}\n" if defined $self->{_icmp_code};
print "icmp type: $self->{_icmp_type}\n" if defined $self->{_icmp_type};
+ print "mod mark: $self->{_mod_mark}\n" if defined $self->{_mod_mark};
+ print "mod dscp: $self->{_mod_dscp}\n" if defined $self->{_mod_dscp};
$src->print();
$dst->print();
@@ -164,7 +174,8 @@ sub get_num_ipt_rules {
my $ipt_rules = 1;
if (("$self->{_log}" eq "enable") && (("$self->{_action}" eq "drop")
|| ("$self->{_action}" eq "accept")
- || ("$self->{_action}" eq "reject"))) {
+ || ("$self->{_action}" eq "reject")
+ || ("$self->{_action}" eq "modify"))) {
$ipt_rules += 1;
}
return $ipt_rules;
@@ -232,6 +243,29 @@ sub rule {
$rule .= "-j RETURN ";
} elsif ("$self->{_action}" eq "reject") {
$rule .= "-j REJECT ";
+ } elsif ("$self->{_action}" eq 'modify') {
+ # mangle actions
+ my $count = 0;
+ if (defined($self->{_mod_mark})) {
+ # MARK
+ $rule .= "-j MARK --set-mark $self->{_mod_mark} ";
+ $count++;
+ }
+ if (defined($self->{_mod_dscp})) {
+ # DSCP
+ $rule .= "-j DSCP --set-dscp $self->{_mod_dscp} ";
+ $count++;
+ }
+
+ # others
+
+ if ($count == 0) {
+ return ('Action "modify" requires more specific configuration under '
+ . 'the "modify" node', );
+ } elsif ($count > 1) {
+ return ('Cannot define more than one modification under '
+ . 'the "modify" node', );
+ }
} else {
return ("\"action\" must be defined", );
}
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);
}
}
}
diff --git a/templates/firewall/mangle/node.def b/templates/firewall/mangle/node.def
new file mode 100644
index 0000000..491fe71
--- /dev/null
+++ b/templates/firewall/mangle/node.def
@@ -0,0 +1,4 @@
+tag:
+type: txt
+syntax:expression: pattern $VAR(@) "^[^-]" ; "Firewall rule set name cannot start with \"-\""
+help: Set firewall rule set name
diff --git a/templates/firewall/mangle/node.tag/description/node.def b/templates/firewall/mangle/node.tag/description/node.def
new file mode 100644
index 0000000..678e325
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: Set firewall description
diff --git a/templates/firewall/mangle/node.tag/rule/node.def b/templates/firewall/mangle/node.tag/rule/node.def
new file mode 100644
index 0000000..010f808
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.def
@@ -0,0 +1,4 @@
+tag:
+type: u32
+help: Set firewall rule number (1-1024)
+syntax:expression: $VAR(@) > 0 && $VAR(@) < 1025; "firewall rule number must be between 1 and 1024"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/action/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/action/node.def
new file mode 100644
index 0000000..0842019
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/action/node.def
@@ -0,0 +1,4 @@
+type: txt
+help: Set firewall rule action
+syntax:expression: $VAR(@) in "drop", "reject", "accept", "modify";
+ "action must be one of drop, reject, accept, or modify"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/description/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/description/node.def
new file mode 100644
index 0000000..9c0c2bb
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: Set rule description
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/destination/address/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/destination/address/node.def
new file mode 100644
index 0000000..e25da77
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/destination/address/node.def
@@ -0,0 +1,9 @@
+type: txt
+help: Set destination IP address, subnet, or range
+comp_help: Possible completions:
+ <x.x.x.x> IP address to match
+ <x.x.x.x/x> Subnet to match
+ <x.x.x.x>-<x.x.x.x> IP range to match
+ !<x.x.x.x> Match everything except the specified address
+ !<x.x.x.x/x> Match everything except the specified subnet
+ !<x.x.x.x>-<x.x.x.x> Match everything except the specified range
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/destination/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/destination/node.def
new file mode 100644
index 0000000..500e0bb
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/destination/node.def
@@ -0,0 +1 @@
+help: Set firewall destination parameters
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/destination/port/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/destination/port/node.def
new file mode 100644
index 0000000..65170b2
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/destination/port/node.def
@@ -0,0 +1,8 @@
+type: txt
+help: Set destination port
+comp_help: Destination port(s) can be specified as a comma-separated list of:
+ <port name> Named port (any name in /etc/services, e.g., http)
+ <1-65535> Numbered port
+ <start>-<end> Numbered port range (e.g., 1001-1005)
+The whole list can also be "negated" using '!'. For example:
+ '!22,telnet,http,123,1001-1005'
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/icmp/code/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/code/node.def
new file mode 100644
index 0000000..71bacfc
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/code/node.def
@@ -0,0 +1,3 @@
+type: u32; "ICMP code must be between 0 and 255"
+help: Set ICMP code (0-255)
+syntax:expression: $VAR(@) >=0 && $VAR(@) <= 255; "ICMP code must be between 0 and 255"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/icmp/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/node.def
new file mode 100644
index 0000000..dcf9fcc
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/node.def
@@ -0,0 +1 @@
+help: Set rule ICMP type and code information
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/icmp/type/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/type/node.def
new file mode 100644
index 0000000..6275a64
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/icmp/type/node.def
@@ -0,0 +1,3 @@
+type: u32; "ICMP type must be between 0 and 255"
+help: Set ICMP type (0-255)
+syntax:expression: $VAR(@) >=0 && $VAR(@) <= 255; "ICMP type must be between 0 and 255"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/log/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/log/node.def
new file mode 100644
index 0000000..5023547
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/log/node.def
@@ -0,0 +1,3 @@
+type: txt; "firwall logging must be enable or disable"
+help: Set firewall logging
+syntax:expression: $VAR(@) in "enable", "disable"; "firwall logging must be enable or disable"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/modify/dscp/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/modify/dscp/node.def
new file mode 100644
index 0000000..b20f58c
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/modify/dscp/node.def
@@ -0,0 +1,4 @@
+type: u32
+help: Set packet Differentiated Services Codepoint (DSCP)
+syntax:expression: $VAR(@) >= 0 && $VAR(@) < 64;
+ "DSCP must be between 0 and 63"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/modify/mark/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/modify/mark/node.def
new file mode 100644
index 0000000..0830b9b
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/modify/mark/node.def
@@ -0,0 +1,2 @@
+type: u32
+help: Set packet marking
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/modify/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/modify/node.def
new file mode 100644
index 0000000..f629b92
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/modify/node.def
@@ -0,0 +1 @@
+help: Set packet modifications
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/protocol/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/protocol/node.def
new file mode 100644
index 0000000..3a912fb
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/protocol/node.def
@@ -0,0 +1,8 @@
+type: txt
+help: Set protocol to match (protocol name in /etc/protocols or protocol number or "all")
+syntax:expression: exec "/opt/vyatta/sbin/vyatta-validate-type.pl protocol_negate '$VAR(@)'" ; "invalid protocol \"$VAR(@)\""
+comp_help:Possible completions:
+ <text> An IP protocol name from /etc/protocols (e.g. "tcp" or "udp")
+ <0-255> An IP protocol number
+ all All IP protocols
+ !<protocol> All IP protocols except for the specified name or number (negation)
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/source/address/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/source/address/node.def
new file mode 100644
index 0000000..a11b2ba
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/source/address/node.def
@@ -0,0 +1,9 @@
+type: txt
+help: Set source IP address, subnet, or range
+comp_help: Possible completions:
+ <x.x.x.x> IP address to match
+ <x.x.x.x/x> Subnet to match
+ <x.x.x.x>-<x.x.x.x> IP range to match
+ !<x.x.x.x> Match everything except the specified address
+ !<x.x.x.x/x> Match everything except the specified subnet
+ !<x.x.x.x>-<x.x.x.x> Match everything except the specified range
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/source/mac-address/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/source/mac-address/node.def
new file mode 100644
index 0000000..fd10e26
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/source/mac-address/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: Set source MAC address
+syntax:expression: exec "/opt/vyatta/sbin/vyatta-validate-type.pl macaddr_negate '$VAR(@)'" ; "invalid MAC address \"$VAR(@)\""
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/source/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/source/node.def
new file mode 100644
index 0000000..16ab3ad
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/source/node.def
@@ -0,0 +1 @@
+help: Set firewall source parameters
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/source/port/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/source/port/node.def
new file mode 100644
index 0000000..e65cbfd
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/source/port/node.def
@@ -0,0 +1,8 @@
+type: txt
+help: Set source port
+comp_help: Source port(s) can be specified as a comma-separated list of:
+ <port name> Named port (any name in /etc/services, e.g., http)
+ <1-65535> Numbered port
+ <start>-<end> Numbered port range (e.g., 1001-1005)
+The whole list can also be "negated" using '!'. For example:
+ '!22,telnet,http,123,1001-1005'
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/state/established/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/state/established/node.def
new file mode 100644
index 0000000..802e35d
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/state/established/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: Set established state
+syntax:expression: $VAR(@) in "enable", "disable" ; "state value must be enable or disable"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/state/invalid/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/state/invalid/node.def
new file mode 100644
index 0000000..ddba99f
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/state/invalid/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: Set invalid state
+syntax:expression: $VAR(@) in "enable", "disable" ; "state value must be enable or disable"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/state/new/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/state/new/node.def
new file mode 100644
index 0000000..23854e7
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/state/new/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: Set new state
+syntax:expression: $VAR(@) in "enable", "disable" ; "state value must be enable or disable"
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/state/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/state/node.def
new file mode 100644
index 0000000..3b7b383
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/state/node.def
@@ -0,0 +1 @@
+help: Set session state
diff --git a/templates/firewall/mangle/node.tag/rule/node.tag/state/related/node.def b/templates/firewall/mangle/node.tag/rule/node.tag/state/related/node.def
new file mode 100644
index 0000000..acddc3b
--- /dev/null
+++ b/templates/firewall/mangle/node.tag/rule/node.tag/state/related/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: Set related state
+syntax:expression: $VAR(@) in "enable", "disable" ; "state value must be enable or disable"