summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/VyattaNatRule.pm532
-rwxr-xr-xscripts/vyatta-show-nat.pl64
-rwxr-xr-xscripts/vyatta-update-nat.pl164
3 files changed, 760 insertions, 0 deletions
diff --git a/scripts/VyattaNatRule.pm b/scripts/VyattaNatRule.pm
new file mode 100644
index 0000000..5bcfac6
--- /dev/null
+++ b/scripts/VyattaNatRule.pm
@@ -0,0 +1,532 @@
+package VyattaNatRule;
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfig;
+
+my %fields = (
+ _type => undef,
+ _orig_type => undef,
+ _inbound_if => undef,
+ _outbound_if => undef,
+ _proto => undef,
+ _source => {
+ _addr => undef,
+ _net => undef,
+ _port_num => undef,
+ _port_name => undef,
+ _port_range => {
+ _start => undef,
+ _stop => undef,
+ },
+ },
+ _destination => {
+ _addr => undef,
+ _net => undef,
+ _port_num => undef,
+ _port_name => undef,
+ _port_range => {
+ _start => undef,
+ _stop => undef,
+ },
+ },
+ _inside_addr => {
+ _addr => undef,
+ _range => {
+ _start => undef,
+ _stop => undef,
+ },
+ _port_num => undef,
+ _port_range => {
+ _start => undef,
+ _stop => undef,
+ },
+ },
+ _outside_addr => {
+ _addr => undef,
+ _range => {
+ _start => undef,
+ _stop => undef,
+ },
+ _port_num => undef,
+ _port_range => {
+ _start => undef,
+ _stop => undef,
+ },
+ },
+);
+
+sub new {
+ my $that = shift;
+ my $class = ref ($that) || $that;
+ my $self = {
+ %fields,
+ };
+
+ bless $self, $class;
+ return $self;
+}
+
+sub setup {
+ my ( $self, $level ) = @_;
+ my $config = new VyattaConfig;
+
+ $config->setLevel("$level");
+
+ $self->{_type} = $config->returnValue("type");
+ $self->{_orig_type} = $config->returnOrigValue("type");
+ $self->{_inbound_if} = $config->returnValue("inbound-interface");
+ $self->{_outbound_if} = $config->returnValue("outbound-interface");
+ $self->{_proto} = $config->returnValue("protocols");
+
+ $self->{_source}->{_addr} = $config->returnValue("source address");
+ $self->{_source}->{_net} = $config->returnValue("source network");
+ my @tmp = $config->returnValues("source port-number");
+ $self->{_source}->{_port_num} = [ @tmp ];
+ @tmp = $config->returnValues("source port-name");
+ $self->{_source}->{_port_name} = [ @tmp ];
+ $self->{_source}->{_port_range}->{_start}
+ = $config->returnValue("source port-range start");
+ $self->{_source}->{_port_range}->{_stop}
+ = $config->returnValue("source port-range stop");
+
+ $self->{_destination}->{_addr} = $config->returnValue("destination address");
+ $self->{_destination}->{_net} = $config->returnValue("destination network");
+ @tmp = $config->returnValues("destination port-number");
+ $self->{_destination}->{_port_num} = [ @tmp ];
+ @tmp = $config->returnValues("destination port-name");
+ $self->{_destination}->{_port_name} = [ @tmp ];
+ $self->{_destination}->{_port_range}->{_start}
+ = $config->returnValue("destination port-range start");
+ $self->{_destination}->{_port_range}->{_stop}
+ = $config->returnValue("destination port-range stop");
+
+ $self->{_inside_addr}->{_addr}
+ = $config->returnValue("inside-address address");
+ $self->{_inside_addr}->{_range}->{_start}
+ = $config->returnValue("inside-address range start");
+ $self->{_inside_addr}->{_range}->{_stop}
+ = $config->returnValue("inside-address range stop");
+ $self->{_inside_addr}->{_port_num}
+ = $config->returnValue("inside-address port-number");
+ $self->{_inside_addr}->{_port_range}->{_start}
+ = $config->returnValue("inside-address port-range start");
+ $self->{_inside_addr}->{_port_range}->{_stop}
+ = $config->returnValue("inside-address port-range stop");
+
+ $self->{_outside_addr}->{_addr}
+ = $config->returnValue("outside-address address");
+ $self->{_outside_addr}->{_range}->{_start}
+ = $config->returnValue("outside-address range start");
+ $self->{_outside_addr}->{_range}->{_stop}
+ = $config->returnValue("outside-address range stop");
+ $self->{_outside_addr}->{_port_num}
+ = $config->returnValue("outside-address port-number");
+ $self->{_outside_addr}->{_port_range}->{_start}
+ = $config->returnValue("outside-address port-range start");
+ $self->{_outside_addr}->{_port_range}->{_stop}
+ = $config->returnValue("outside-address port-range stop");
+
+ return 0;
+}
+
+sub setupOrig {
+ my ( $self, $level ) = @_;
+ my $config = new VyattaConfig;
+
+ $config->setLevel("$level");
+
+ $self->{_type} = $config->returnOrigValue("type");
+ $self->{_orig_type} = $config->returnOrigValue("type");
+ $self->{_inbound_if} = $config->returnOrigValue("inbound-interface");
+ $self->{_outbound_if} = $config->returnOrigValue("outbound-interface");
+ $self->{_proto} = $config->returnOrigValue("protocols");
+
+ $self->{_source}->{_addr} = $config->returnOrigValue("source address");
+ $self->{_source}->{_net} = $config->returnOrigValue("source network");
+ my @tmp = $config->returnOrigValues("source port-number");
+ $self->{_source}->{_port_num} = [ @tmp ];
+ @tmp = $config->returnOrigValues("source port-name");
+ $self->{_source}->{_port_name} = [ @tmp ];
+ $self->{_source}->{_port_range}->{_start}
+ = $config->returnOrigValue("source port-range start");
+ $self->{_source}->{_port_range}->{_stop}
+ = $config->returnOrigValue("source port-range stop");
+
+ $self->{_destination}->{_addr}
+ = $config->returnOrigValue("destination address");
+ $self->{_destination}->{_net}
+ = $config->returnOrigValue("destination network");
+ @tmp = $config->returnOrigValues("destination port-number");
+ $self->{_destination}->{_port_num} = [ @tmp ];
+ @tmp = $config->returnOrigValues("destination port-name");
+ $self->{_destination}->{_port_name} = [ @tmp ];
+ $self->{_destination}->{_port_range}->{_start}
+ = $config->returnOrigValue("destination port-range start");
+ $self->{_destination}->{_port_range}->{_stop}
+ = $config->returnOrigValue("destination port-range stop");
+
+ $self->{_inside_addr}->{_addr}
+ = $config->returnOrigValue("inside-address address");
+ $self->{_inside_addr}->{_range}->{_start}
+ = $config->returnOrigValue("inside-address range start");
+ $self->{_inside_addr}->{_range}->{_stop}
+ = $config->returnOrigValue("inside-address range stop");
+ $self->{_inside_addr}->{_port_num}
+ = $config->returnOrigValue("inside-address port-number");
+ $self->{_inside_addr}->{_port_range}->{_start}
+ = $config->returnOrigValue("inside-address port-range start");
+ $self->{_inside_addr}->{_port_range}->{_stop}
+ = $config->returnOrigValue("inside-address port-range stop");
+
+ $self->{_outside_addr}->{_addr}
+ = $config->returnOrigValue("outside-address address");
+ $self->{_outside_addr}->{_range}->{_start}
+ = $config->returnOrigValue("outside-address range start");
+ $self->{_outside_addr}->{_range}->{_stop}
+ = $config->returnOrigValue("outside-address range stop");
+ $self->{_outside_addr}->{_port_num}
+ = $config->returnOrigValue("outside-address port-number");
+ $self->{_outside_addr}->{_port_range}->{_start}
+ = $config->returnOrigValue("outside-address port-range start");
+ $self->{_outside_addr}->{_port_range}->{_stop}
+ = $config->returnOrigValue("outside-address port-range stop");
+
+ return 0;
+}
+
+sub handle_ports {
+ my $num_ref = shift;
+ my $name_ref = shift;
+ my $pstart = shift;
+ my $pstop = shift;
+ my $can_use_port = shift;
+ my $prefix = shift;
+ my $proto = shift;
+
+ my $rule_str = "";
+ my ($ports, $prange) = (0, 0);
+ my @pnums = @{$num_ref};
+ my @pnames = @{$name_ref};
+ $ports = ($#pnums + 1) + ($#pnames + 1);
+
+ if (defined($pstart) && defined($pstop)) {
+ if ($pstop < $pstart) {
+ return (undef, "invalid port range $pstart-$pstop");
+ }
+ $ports += ($pstop - $pstart + 1);
+ $prange = ($pstop - $pstart - 1);
+ }
+ if (($ports > 0) && (!$can_use_port)) {
+ return (undef, "ports can only be specified when protocol is \"tcp\" "
+ . "or \"udp\" (currently \"$proto\")");
+ }
+ if (($ports - $prange) > 15) {
+ return (undef, "source/destination port specification only supports "
+ . "up to 15 ports (port range counts as 2)");
+ }
+ if ($ports > 1) {
+ $rule_str .= " -m multiport --${prefix}ports ";
+ my $first = 1;
+ if ($#pnums >= 0) {
+ my $pstr = join(',', @pnums);
+ $rule_str .= "$pstr";
+ $first = 0;
+ }
+ if ($#pnames >= 0) {
+ if ($first == 0) {
+ $rule_str .= ",";
+ }
+ my $pstr = join(',', @pnames);
+ $rule_str .= "$pstr";
+ $first = 0;
+ }
+ if (defined($pstart) && defined($pstop)) {
+ if ($first == 0) {
+ $rule_str .= ",";
+ }
+ if ($pstart == $pstop) {
+ $rule_str .= "$pstart";
+ } else {
+ $rule_str .= "$pstart:$pstop";
+ }
+ $first = 0;
+ }
+ } elsif ($ports > 0) {
+ $rule_str .= " --${prefix}port ";
+ if ($#pnums >= 0) {
+ $rule_str .= "$pnums[0]";
+ } elsif ($#pnames >= 0) {
+ $rule_str .= "$pnames[0]";
+ } else {
+ # no number, no name, range of 1
+ $rule_str .= "$pstart";
+ }
+ }
+
+ return ($rule_str, undef);
+}
+
+# returns (rule, error)
+sub rule_str {
+ my ($self) = @_;
+ my $rule_str = "";
+ my $can_use_port = 1;
+
+ if (!defined($self->{_proto}) ||
+ (($self->{_proto} ne "tcp") && ($self->{_proto} ne "6")
+ && ($self->{_proto} ne "udp") && ($self->{_proto} ne "17"))) {
+ $can_use_port = 0;
+ }
+ if (($self->{_type} eq "source") || ($self->{_type} eq "masquerade")) {
+ if ($self->{_type} eq "masquerade") {
+ $rule_str .= "-j MASQUERADE";
+ } else {
+ $rule_str .= "-j SNAT";
+ }
+ if (defined($self->{_outbound_if})) {
+ $rule_str .= " -o $self->{_outbound_if}";
+ } else {
+ # "masquerade" requires outbound_if.
+ # also make this a requirement for "source" to prevent users from
+ # inadvertently NATing loopback traffic.
+ return (undef, "outbound-interface not specified");
+ }
+
+ if (defined($self->{_proto})) {
+ my $str = $self->{_proto};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule_str .= " -p $str";
+ }
+
+ #my $to_src = " --to-source ";
+ my $to_src = "";
+ if (defined($self->{_outside_addr}->{_addr})) {
+ $to_src .= "$self->{_outside_addr}->{_addr}";
+ } elsif (defined($self->{_outside_addr}->{_range}->{_start})
+ && defined($self->{_outside_addr}->{_range}->{_stop})) {
+ $to_src .= "$self->{_outside_addr}->{_range}->{_start}";
+ $to_src .= "-$self->{_outside_addr}->{_range}->{_stop}";
+ }
+
+ if (($to_src ne "") && ($self->{_type} eq "masquerade")) {
+ return (undef, "cannot specify outside IP address with \"masquerade\"");
+ }
+
+ if (defined($self->{_outside_addr}->{_port_num})) {
+ if (!$can_use_port) {
+ return (undef, "ports can only be specified when protocol is \"tcp\" "
+ . "or \"udp\" (currently \"$self->{_proto}\")");
+ }
+ if ($self->{_type} ne "masquerade") {
+ $to_src .= ":";
+ }
+ $to_src .= "$self->{_outside_addr}->{_port_num}";
+ } elsif (defined($self->{_outside_addr}->{_port_range}->{_start})
+ && defined($self->{_outside_addr}->{_port_range}->{_stop})) {
+ if (!$can_use_port) {
+ return (undef, "ports can only be specified when protocol is \"tcp\" "
+ . "or \"udp\" (currently \"$self->{_proto}\")");
+ }
+ if ($self->{_type} ne "masquerade") {
+ $to_src .= ":";
+ }
+ $to_src .= "$self->{_outside_addr}->{_port_range}->{_start}";
+ $to_src .= "-$self->{_outside_addr}->{_port_range}->{_stop}";
+ }
+
+ if ($to_src ne "") {
+ if ($self->{_type} eq "masquerade") {
+ $rule_str .= " --to-ports $to_src";
+ } else {
+ $rule_str .= " --to-source $to_src";
+ }
+ } elsif ($self->{_type} ne "masquerade") {
+ return (undef, "outside-address not specified");
+ }
+ } else {
+ # type is destination
+ $rule_str .= "-j DNAT";
+
+ if (defined($self->{_inbound_if})) {
+ $rule_str .= " -i $self->{_inbound_if}";
+ } else {
+ # make this a requirement to prevent users from
+ # inadvertently NATing loopback traffic.
+ return (undef, "inbound-interface not specified");
+ }
+
+ if (defined($self->{_proto})) {
+ $rule_str .= " -p $self->{_proto}";
+ }
+
+ my $to_dst = " --to-destination ";
+ if (defined($self->{_inside_addr}->{_addr})) {
+ $to_dst .= "$self->{_inside_addr}->{_addr}";
+ } elsif (defined($self->{_inside_addr}->{_range}->{_start})
+ && defined($self->{_inside_addr}->{_range}->{_stop})) {
+ $to_dst .= "$self->{_inside_addr}->{_range}->{_start}";
+ $to_dst .= "-$self->{_inside_addr}->{_range}->{_stop}";
+ }
+ if (defined($self->{_inside_addr}->{_port_num})) {
+ if (!$can_use_port) {
+ return (undef, "ports can only be specified when protocol is \"tcp\" "
+ . "or \"udp\" (currently \"$self->{_proto}\")");
+ }
+ $to_dst .= ":$self->{_inside_addr}->{_port_num}";
+ } elsif (defined($self->{_inside_addr}->{_port_range}->{_start})
+ && defined($self->{_inside_addr}->{_port_range}->{_stop})) {
+ if (!$can_use_port) {
+ return (undef, "ports can only be specified when protocol is \"tcp\" "
+ . "or \"udp\" (currently \"$self->{_proto}\")");
+ }
+ $to_dst .= ":$self->{_inside_addr}->{_port_range}->{_start}";
+ $to_dst .= "-$self->{_inside_addr}->{_port_range}->{_stop}";
+ }
+ if ($to_dst ne " --to-destination ") {
+ $rule_str .= $to_dst;
+ } else {
+ return (undef, "inside-address not specified");
+ }
+ }
+
+ # source port(s)
+ my ($port_str, $port_err)
+ = handle_ports($self->{_source}->{_port_num},
+ $self->{_source}->{_port_name},
+ $self->{_source}->{_port_range}->{_start},
+ $self->{_source}->{_port_range}->{_stop},
+ $can_use_port, "s", $self->{_proto});
+ return (undef, $port_err) if (!defined($port_str));
+ $rule_str .= $port_str;
+
+ # destination port(s)
+ ($port_str, $port_err)
+ = handle_ports($self->{_destination}->{_port_num},
+ $self->{_destination}->{_port_name},
+ $self->{_destination}->{_port_range}->{_start},
+ $self->{_destination}->{_port_range}->{_stop},
+ $can_use_port, "d", $self->{_proto});
+ return (undef, $port_err) if (!defined($port_str));
+ $rule_str .= $port_str;
+
+ if (defined($self->{_source}->{_addr})) {
+ my $str = $self->{_source}->{_addr};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule_str .= " -s $str";
+ } elsif (defined($self->{_source}->{_net})) {
+ my $str = $self->{_source}->{_net};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule_str .= " -s $str";
+ }
+
+ if (defined($self->{_destination}->{_addr})) {
+ my $str = $self->{_destination}->{_addr};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule_str .= " -d $str";
+ } elsif (defined($self->{_destination}->{_net})) {
+ my $str = $self->{_destination}->{_net};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule_str .= " -d $str";
+ }
+
+ return ($rule_str, "");
+}
+
+sub orig_type {
+ my ($self) = @_;
+ return "source" if ($self->{_orig_type} eq "masquerade");
+ return $self->{_orig_type};
+}
+
+sub new_type {
+ my ($self) = @_;
+ return "source" if ($self->{_type} eq "masquerade");
+ return $self->{_type};
+}
+
+sub print_str {
+ my ($self) = @_;
+ my $str =
+ "type[$self->{_type}] " .
+ "in_if[$self->{_inbound_if}] out_if[$self->{_outbound_if}] " .
+ "proto[$self->{_proto}] saddr[$self->{_source}->{_addr}] ".
+ "snet[$self->{_source}->{_net}] spnum[@{$self->{_source}->{_port_num}}] ".
+ "spname[@{$self->{_source}->{_port_name}}] " .
+ "sprange[$self->{_source}->{_port_range}->{_start}" .
+ "-$self->{_source}->{_port_range}->{_stop}] " .
+ "daddr[$self->{_destination}->{_addr}] " .
+ "dnet[$self->{_destination}->{_net}] " .
+ "dpnum[@{$self->{_destination}->{_port_num}}] " .
+ "dpname[@{$self->{_destination}->{_port_name}}] " .
+ "dprange[$self->{_destination}->{_port_range}->{_start}-" .
+ "$self->{_destination}->{_port_range}->{_stop}] " .
+ "inaddr[$self->{_inside_addr}->{_addr}] " .
+ "inrange[$self->{_inside_addr}->{_range}->{_start}-" .
+ "$self->{_inside_addr}->{_range}->{_stop}] " .
+ "inp[$self->{_inside_addr}->{_port_num}] " .
+ "inprange[$self->{_inside_addr}->{_port_range}->{_start}-" .
+ "$self->{_inside_addr}->{_port_range}->{_stop}] " .
+ "outaddr[$self->{_outside_addr}->{_addr}] " .
+ "outrange[$self->{_outside_addr}->{_range}->{_start}-" .
+ "$self->{_outside_addr}->{_range}->{_stop}]";
+ "outp[$self->{_outside_addr}->{_port_num}] " .
+ "outprange[$self->{_outside_addr}->{_port_range}->{_start}-" .
+ "$self->{_outside_addr}->{_port_range}->{_stop}] " .
+
+ return $str;
+}
+
+sub outputXmlElem {
+ my ($name, $value, $fh) = @_;
+ print $fh " <$name>$value</$name>\n";
+}
+
+sub outputXml {
+ my ($self, $fh) = @_;
+ outputXmlElem("type", $self->{_type}, $fh);
+ outputXmlElem("in_interface", $self->{_inbound_if}, $fh);
+ outputXmlElem("out_interface", $self->{_outbound_if}, $fh);
+ outputXmlElem("src_addr", $self->{_source}->{_addr}, $fh);
+ outputXmlElem("src_network", $self->{_source}->{_net}, $fh);
+ outputXmlElem("src_ports", join(',', @{$self->{_source}->{_port_num}}), $fh);
+ outputXmlElem("src_ports_apps", join(',', @{$self->{_source}->{_port_name}}),
+ $fh);
+ outputXmlElem("src_port_start", $self->{_source}->{_port_range}->{_start},
+ $fh);
+ outputXmlElem("src_port_stop", $self->{_source}->{_port_range}->{_stop},
+ $fh);
+ outputXmlElem("dst_addr", $self->{_destination}->{_addr}, $fh);
+ outputXmlElem("dst_network", $self->{_destination}->{_net}, $fh);
+ outputXmlElem("dst_ports", join(',', @{$self->{_destination}->{_port_num}}),
+ $fh);
+ outputXmlElem("dst_ports_apps",
+ join(',', @{$self->{_destination}->{_port_name}}), $fh);
+ outputXmlElem("dst_port_start",
+ $self->{_destination}->{_port_range}->{_start}, $fh);
+ outputXmlElem("dst_port_stop",
+ $self->{_destination}->{_port_range}->{_stop}, $fh);
+ outputXmlElem("in_addr", $self->{_inside_addr}->{_addr}, $fh);
+ outputXmlElem("in_addr_start", $self->{_inside_addr}->{_range}->{_start},
+ $fh);
+ outputXmlElem("in_addr_stop", $self->{_inside_addr}->{_range}->{_stop},
+ $fh);
+ outputXmlElem("in_port", $self->{_inside_addr}->{_port_num}, $fh);
+ outputXmlElem("in_port_start",
+ $self->{_inside_addr}->{_port_range}->{_start}, $fh);
+ outputXmlElem("in_port_stop",
+ $self->{_inside_addr}->{_port_range}->{_stop}, $fh);
+ outputXmlElem("out_addr", $self->{_outside_addr}->{_addr}, $fh);
+ outputXmlElem("out_addr_start", $self->{_outside_addr}->{_range}->{_start},
+ $fh);
+ outputXmlElem("out_addr_stop", $self->{_outside_addr}->{_range}->{_stop},
+ $fh);
+ outputXmlElem("out_port", $self->{_outside_addr}->{_port_num}, $fh);
+ outputXmlElem("out_port_start",
+ $self->{_outside_addr}->{_port_range}->{_start}, $fh);
+ outputXmlElem("out_port_stop",
+ $self->{_outside_addr}->{_port_range}->{_stop}, $fh);
+
+ # no proto? ($self->{_proto})
+}
+
diff --git a/scripts/vyatta-show-nat.pl b/scripts/vyatta-show-nat.pl
new file mode 100755
index 0000000..5ae67b8
--- /dev/null
+++ b/scripts/vyatta-show-nat.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfig;
+use VyattaNatRule;
+
+sub numerically { $a <=> $b; }
+
+exit 1 if ($#ARGV != 0);
+my $xsl_file = $ARGV[0];
+
+my %stats = (
+ source => [ ],
+ destination => [ ],
+ );
+open(STATS, "iptables -t nat -L -vn |") or exit 1;
+my $skey = "";
+while (<STATS>) {
+ if (m/^Chain PREROUTING/) {
+ $skey = "destination";
+ } elsif (m/^Chain POSTROUTING/) {
+ $skey = "source";
+ } elsif (m/^Chain /) {
+ $skey = "";
+ }
+
+ if ($skey ne "" && (m/SNAT/ || m/DNAT/ || m/MASQUERADE/)) {
+ m/^\s*(\d+)\s+(\d+)\s/;
+ push @{$stats{$skey}}, ($1, $2);
+ }
+}
+close STATS;
+
+open(RENDER, "| /opt/vyatta/libexec/xorp/render_xml $xsl_file") or exit 1;
+
+# begin
+print RENDER "<opcommand name='natrules'><format type='row'>\n";
+
+my $config = new VyattaConfig;
+$config->setLevel("service nat rule");
+my @rules_pre = $config->listOrigNodes();
+my $rule;
+my @rules = sort numerically @rules_pre;
+for $rule (@rules) {
+ my $nrule = new VyattaNatRule;
+ $nrule->setupOrig("service nat rule $rule");
+ my $ntype = $nrule->orig_type();
+ print RENDER " <row>\n";
+ print RENDER " <rule_num>$rule</rule_num>\n";
+ my $pkts = shift @{$stats{$ntype}};
+ my $bytes = shift @{$stats{$ntype}};
+ print RENDER " <pkts>$pkts</pkts>\n";
+ print RENDER " <bytes>$bytes</bytes>\n";
+ $nrule->outputXml(*RENDER{IO});
+ print RENDER " </row>\n";
+}
+
+# end
+print RENDER "</format></opcommand>\n";
+
+close RENDER;
+exit 0;
+
diff --git a/scripts/vyatta-update-nat.pl b/scripts/vyatta-update-nat.pl
new file mode 100755
index 0000000..19255ed
--- /dev/null
+++ b/scripts/vyatta-update-nat.pl
@@ -0,0 +1,164 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfig;
+use VyattaNatRule;
+
+sub numerically { $a <=> $b; }
+
+sub raw_cleanup {
+ # remove the conntrack setup.
+ my @lines
+ = `iptables -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 "NAT_CONNTRACK") {
+ system("iptables -t raw -D PREROUTING $num");
+ system("iptables -t raw -D OUTPUT $num");
+ system("iptables -t raw -F NAT_CONNTRACK");
+ system("iptables -t raw -X NAT_CONNTRACK");
+ last;
+ }
+ }
+}
+
+# for "clear nat translations"
+if ($ARGV[0] eq "clearnattranslations") {
+ system("iptables-save -t nat | iptables-restore");
+ exit 0;
+}
+
+my $config = new VyattaConfig;
+$config->setLevel("service nat rule");
+my %rules = $config->listNodeStatus();
+my $rule;
+open(OUT, ">>/dev/null") or exit 1;
+my %ipt_rulenum = (
+ source => 1,
+ destination => 1,
+ );
+my %chain_name = (
+ source => "POSTROUTING",
+ destination => "PREROUTING",
+ );
+print OUT "========= nat list =========\n";
+my @rule_keys = sort numerically keys %rules;
+if ($#rule_keys < 0) {
+ # no rules (everything is deleted) => flush the nat table & return
+ print OUT "iptables -t nat -F\n";
+ if (system("iptables -t nat -F")) {
+ exit 1;
+ }
+
+ raw_cleanup();
+
+ exit 0;
+}
+
+## it seems that "multiport" does not like port range (p1:p2) if nobody has
+## touched the nat table yet after reboot!?
+system("iptables -t nat -L -n >& /dev/null");
+
+# we have some nat rule(s). make sure conntrack is enabled.
+system("iptables -t raw -L NAT_CONNTRACK -n >& /dev/null");
+if ($? >> 8) {
+ # NAT_CONNTRACK chain does not exist yet. set up conntrack.
+ system("iptables -t raw -N NAT_CONNTRACK");
+ # this enables conntrack for all packets. potentially we can add more rules
+ # to the NAT_CONNTRACK chain for finer-grained control over which packets
+ # are tracked.
+ system("iptables -t raw -A NAT_CONNTRACK -j ACCEPT");
+ system("iptables -t raw -I PREROUTING 1 -j NAT_CONNTRACK");
+ system("iptables -t raw -I OUTPUT 1 -j NAT_CONNTRACK");
+}
+
+my $all_deleted = 1;
+for $rule (@rule_keys) {
+ print OUT "$rule: $rules{$rule}\n";
+ my $nrule = new VyattaNatRule;
+ $nrule->setup("service nat rule $rule");
+ my $otype = $nrule->orig_type();
+ my $ntype = $nrule->new_type();
+ if ((defined($otype) && $otype ne "source" && $otype ne "destination")
+ || (defined($ntype) && $ntype ne "source" && $ntype ne "destination")) {
+ exit 2;
+ }
+
+ if ($rules{$rule} ne "deleted") {
+ $all_deleted = 0;
+ }
+
+ my $cmd;
+ if ($rules{$rule} eq "static") {
+ # $otype and $ntype should be the same
+ if (!defined($ntype)) {
+ exit 3;
+ }
+ $ipt_rulenum{$ntype} += 1;
+ next;
+ } elsif ($rules{$rule} eq "deleted") {
+ # $ntype should be empty
+ if (!defined($otype)) {
+ exit 4;
+ }
+ $cmd = "iptables -t nat -D $chain_name{$otype} $ipt_rulenum{$otype}";
+ print OUT "$cmd\n";
+ if (system($cmd)) {
+ exit 1;
+ }
+ next;
+ }
+
+ my ($str, $err) = $nrule->rule_str();
+ if (!defined($str)) {
+ # rule check failed => return error
+ print OUT "ERROR: $err\n";
+ print STDERR "ERROR: $err\n";
+ exit 5;
+ }
+
+ if ($rules{$rule} eq "added") {
+ # $otype should be empty
+ if (!defined($ntype)) {
+ exit 6;
+ }
+ $cmd = "iptables -t nat -I $chain_name{$ntype} $ipt_rulenum{$ntype} " .
+ "$str";
+ print OUT "$cmd\n";
+ if (system($cmd)) {
+ exit 1;
+ }
+ $ipt_rulenum{$ntype} += 1;
+ } elsif ($rules{$rule} eq "changed") {
+ # $otype and $ntype may not be the same
+ if (!defined($otype) || !defined($ntype)) {
+ exit 7;
+ }
+ $cmd = "iptables -t nat -I $chain_name{$ntype} $ipt_rulenum{$ntype} " .
+ "$str";
+ print OUT "$cmd\n";
+ if (system($cmd)) {
+ exit 1;
+ }
+ my $idx = $ipt_rulenum{$otype};
+ if ($otype eq $ntype) {
+ $idx += 1;
+ }
+ $cmd = "iptables -t nat -D $chain_name{$otype} $idx";
+ print OUT "$cmd\n";
+ if (system($cmd)) {
+ exit 1;
+ }
+ $ipt_rulenum{$ntype} += 1;
+ }
+}
+
+if ($all_deleted) {
+ raw_cleanup();
+}
+
+close OUT;
+exit 0;
+