summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/Vyatta/IpTables/AddressFilter.pm210
-rw-r--r--lib/Vyatta/IpTables/Rule.pm414
2 files changed, 624 insertions, 0 deletions
diff --git a/lib/Vyatta/IpTables/AddressFilter.pm b/lib/Vyatta/IpTables/AddressFilter.pm
new file mode 100755
index 0000000..1749dcd
--- /dev/null
+++ b/lib/Vyatta/IpTables/AddressFilter.pm
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+
+# Author: An-Cheng Huang <ancheng@vyatta.com>
+# Date: 2007
+# Description: IP tables address filter
+
+# **** License ****
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# This code was originally developed by Vyatta, Inc.
+# Portions created by Vyatta are Copyright (C) 2006, 2007, 2008 Vyatta, Inc.
+# All Rights Reserved.
+# **** End License ****
+
+package Vyatta::IpTables::AddressFilter;
+
+require Vyatta::Config;
+use Vyatta::Misc qw(getPortRuleString);
+use Vyatta::TypeChecker;
+
+use strict;
+use warnings;
+
+my %_protocolswithports = (
+ tcp => 1,
+ udp => 1,
+ 6 => 1,
+ 17 => 1,
+);
+
+my %fields = (
+ _srcdst => undef,
+ _range_start => undef,
+ _range_stop => undef,
+ _network => undef,
+ _address => undef,
+ _port => undef,
+ _protocol => undef,
+ _src_mac => 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 Vyatta::Config;
+
+ $config->setLevel("$level");
+
+ # setup needed parent nodes
+ $self->{_srcdst} = $config->returnParent("..");
+ $self->{_protocol} = $config->returnValue(".. protocol");
+
+ # setup address filter nodes
+ $self->{_address} = $config->returnValue("address");
+ $self->{_network} = undef;
+ $self->{_range_start} = undef;
+ $self->{_range_stop} = undef;
+ if (defined($self->{_address})) {
+ if ($self->{_address} =~ /\//) {
+ $self->{_network} = $self->{_address};
+ $self->{_address} = undef;
+ } elsif ($self->{_address} =~ /^([^-]+)-([^-]+)$/) {
+ $self->{_range_start} = $1;
+ $self->{_range_stop} = $2;
+ $self->{_address} = undef;
+ }
+ }
+
+ $self->{_port} = $config->returnValue("port");
+ $self->{_src_mac} = $config->returnValue("mac-address");
+
+ return 0;
+}
+
+sub setupOrig {
+ my ($self, $level) = @_;
+ my $config = new Vyatta::Config;
+
+ $config->setLevel("$level");
+
+ # setup needed parent nodes
+ $self->{_srcdst} = $config->returnParent("..");
+ $self->{_protocol} = $config->returnOrigValue(".. protocol");
+
+ # setup address filter nodes
+ $self->{_address} = $config->returnOrigValue("address");
+ $self->{_network} = undef;
+ $self->{_range_start} = undef;
+ $self->{_range_stop} = undef;
+ if (defined($self->{_address})) {
+ if ($self->{_address} =~ /\//) {
+ $self->{_network} = $self->{_address};
+ $self->{_address} = undef;
+ } elsif ($self->{_address} =~ /^([^-]+)-([^-]+)$/) {
+ $self->{_range_start} = $1;
+ $self->{_range_stop} = $2;
+ $self->{_address} = undef;
+ }
+ }
+
+ $self->{_port} = $config->returnOrigValue("port");
+ $self->{_src_mac} = $config->returnValue("mac-address");
+
+ return 0;
+}
+
+sub print {
+ my ($self) = @_;
+
+ print "srcdst: $self->{_srcdst}\n" if defined $self->{_srcdst};
+ print "range start: $self->{_range_start}\n" if defined $self->{_range_start};
+ print "range stop: $self->{_range_stop}\n" if defined $self->{_range_stop};
+ print "network: $self->{_network}\n" if defined $self->{_network};
+ print "address: $self->{_address}\n" if defined $self->{_address};
+ print "port: $self->{_port}\n" if defined $self->{_port};
+ print "protocol: $self->{_protocol}\n" if defined $self->{_protocol};
+ print "src-mac: $self->{_src_mac}\n" if defined $self->{_src_mac};
+
+ return 0;
+}
+
+sub rule {
+ my ($self) = @_;
+ my $rule = "";
+ my $can_use_port = 1;
+
+ if (!defined($self->{_protocol})
+ || !defined($_protocolswithports{$self->{_protocol}})) {
+ $can_use_port = 0;
+ }
+
+ if (($self->{_srcdst} eq "source") && (defined($self->{_src_mac}))) {
+ # handle src mac
+ my $str = $self->{_src_mac};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule .= "-m mac --mac-source $str ";
+ }
+
+ # set the address filter parameters
+ if (defined($self->{_network})) {
+ my $str = $self->{_network};
+ return (undef, "\"$str\" is not a valid IP subnet")
+ if (!VyattaTypeChecker::validateType('ipv4net_negate', $str, 1));
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule .= "--$self->{_srcdst} $str ";
+ } elsif (defined($self->{_address})) {
+ my $str = $self->{_address};
+ return (undef, "\"$str\" is not a valid IP address")
+ if (!VyattaTypeChecker::validateType('ipv4_negate', $str, 1));
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule .= "--$self->{_srcdst} $str ";
+ } elsif ((defined $self->{_range_start}) && (defined $self->{_range_stop})) {
+ my $start = $self->{_range_start};
+ my $stop = $self->{_range_stop};
+ return (undef, "\"$start-$stop\" is not a valid IP range")
+ if (!VyattaTypeChecker::validateType('ipv4_negate', $start, 1)
+ || !VyattaTypeChecker::validateType('ipv4', $stop, 1));
+ my $negate = '';
+ if ($self->{_range_start} =~ /^!(.*)$/) {
+ $start = $1;
+ $negate = '! '
+ }
+ if ("$self->{_srcdst}" eq "source") {
+ $rule .= ("-m iprange $negate--src-range $start-$self->{_range_stop} ");
+ }
+ elsif ("$self->{_srcdst}" eq "destination") {
+ $rule .= ("-m iprange $negate--dst-range $start-$self->{_range_stop} ");
+ }
+ }
+
+ my ($port_str, $port_err)
+ = getPortRuleString($self->{_port}, $can_use_port,
+ ($self->{_srcdst} eq "source") ? "s" : "d",
+ $self->{_protocol});
+ return (undef, $port_err) if (!defined($port_str));
+ $rule .= $port_str;
+ return ($rule, undef);
+}
+
+sub outputXmlElem {
+ my ($name, $value, $fh) = @_;
+ print $fh " <$name>$value</$name>\n";
+}
+
+sub outputXml {
+ my ($self, $prefix, $fh) = @_;
+ outputXmlElem("${prefix}_addr", $self->{_address}, $fh);
+ outputXmlElem("${prefix}_net", $self->{_network}, $fh);
+ outputXmlElem("${prefix}_addr_start", $self->{_range_start}, $fh);
+ outputXmlElem("${prefix}_addr_stop", $self->{_range_stop}, $fh);
+ outputXmlElem("${prefix}_port", $self->{_port}, $fh);
+}
+
diff --git a/lib/Vyatta/IpTables/Rule.pm b/lib/Vyatta/IpTables/Rule.pm
new file mode 100644
index 0000000..9cbd303
--- /dev/null
+++ b/lib/Vyatta/IpTables/Rule.pm
@@ -0,0 +1,414 @@
+package Vyatta::IpTables::Rule;
+
+use Vyatta::Config;
+require Vyatta::IpTables::AddressFilter;
+
+my $src = new Vyatta::IpTables::AddressFilter;
+my $dst = new Vyatta::IpTables::AddressFilter;
+
+my %fields = (
+ _name => undef,
+ _rule_number => undef,
+ _protocol => undef,
+ _state => {
+ _established => undef,
+ _new => undef,
+ _related => undef,
+ _invalid => undef,
+ },
+ _action => undef,
+ _log => undef,
+ _icmp_code => undef,
+ _icmp_type => undef,
+ _mod_mark => undef,
+ _mod_dscp => undef,
+ _ipsec => undef,
+ _non_ipsec => undef,
+ _frag => undef,
+ _non_frag => undef,
+ _recent_time => undef,
+ _recent_cnt => undef,
+ _p2p => {
+ _all => undef,
+ _apple => undef,
+ _bit => undef,
+ _dc => undef,
+ _edk => undef,
+ _gnu => undef,
+ _kazaa => undef,
+ },
+);
+
+my %dummy_rule = (
+ _rule_number => 1025,
+ _protocol => "all",
+ _state => {
+ _established => undef,
+ _new => undef,
+ _related => undef,
+ _invalid => undef,
+ },
+ _action => "DROP",
+ _log => undef,
+ _icmp_code => undef,
+ _icmp_type => undef,
+ _mod_mark => undef,
+ _mod_dscp => undef,
+ _ipsec => undef,
+ _non_ipsec => undef,
+ _frag => undef,
+ _non_frag => undef,
+ _recent_time => undef,
+ _recent_cnt => undef,
+ _p2p => {
+ _all => undef,
+ _apple => undef,
+ _bit => undef,
+ _dc => undef,
+ _edk => undef,
+ _gnu => undef,
+ _kazaa => undef,
+ },
+);
+
+sub new {
+ my $that = shift;
+ my $class = ref ($that) || $that;
+ my $self = {
+ %fields,
+ };
+
+ bless $self, $class;
+ return $self;
+}
+
+sub setupDummy {
+ my $self = shift;
+ %{$self} = %dummy_rule;
+ $src = new Vyatta::IpTables::AddressFilter;
+ $dst = new Vyatta::IpTables::AddressFilter;
+}
+
+sub setup {
+ my ( $self, $level ) = @_;
+ my $config = new Vyatta::Config;
+
+ $config->setLevel("$level");
+
+ # for documentation sake. nodes returns an array so must transform
+ # and ".. .. .." means go up three levels in the current hierarchy
+ $self->{_name} = $config->returnParent(".. .. ..");
+ $self->{_rule_number} = $config->returnParent("..");
+
+ $self->{_protocol} = $config->returnValue("protocol");
+ $self->{_state}->{_established} = $config->returnValue("state established");
+ $self->{_state}->{_new} = $config->returnValue("state new");
+ $self->{_state}->{_related} = $config->returnValue("state related");
+ $self->{_state}->{_invalid} = $config->returnValue("state invalid");
+ $self->{_action} = $config->returnValue("action");
+ $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");
+ $self->{_ipsec} = $config->exists("ipsec match-ipsec");
+ $self->{_non_ipsec} = $config->exists("ipsec match-none");
+ $self->{_frag} = $config->exists("fragment match-frag");
+ $self->{_non_frag} = $config->exists("fragment match-non-frag");
+ $self->{_recent_time} = $config->returnValue('recent time');
+ $self->{_recent_cnt} = $config->returnValue('recent count');
+
+ $self->{_p2p}->{_all} = $config->exists("p2p all");
+ $self->{_p2p}->{_apple} = $config->exists("p2p applejuice");
+ $self->{_p2p}->{_bit} = $config->exists("p2p bittorrent");
+ $self->{_p2p}->{_dc} = $config->exists("p2p directconnect");
+ $self->{_p2p}->{_edk} = $config->exists("p2p edonkey");
+ $self->{_p2p}->{_gnu} = $config->exists("p2p gnutella");
+ $self->{_p2p}->{_kazaa} = $config->exists("p2p kazaa");
+
+ # TODO: need $config->exists("$level source") in Vyatta::Config.pm
+ $src->setup("$level source");
+ $dst->setup("$level destination");
+
+ return 0;
+}
+
+sub setupOrig {
+ my ( $self, $level ) = @_;
+ my $config = new Vyatta::Config;
+
+ $config->setLevel("$level");
+
+ # for documentation sake. nodes returns an array so must transform
+ # and ".. .. .." means go up three levels in the current hierarchy
+ $self->{_name} = $config->returnParent(".. .. ..");
+ $self->{_rule_number} = $config->returnParent("..");
+
+ $self->{_protocol} = $config->returnOrigValue("protocol");
+ $self->{_state}->{_established}
+ = $config->returnOrigValue("state established");
+ $self->{_state}->{_new} = $config->returnOrigValue("state new");
+ $self->{_state}->{_related} = $config->returnOrigValue("state related");
+ $self->{_state}->{_invalid} = $config->returnOrigValue("state invalid");
+ $self->{_action} = $config->returnOrigValue("action");
+ $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");
+ $self->{_ipsec} = $config->existsOrig("ipsec match-ipsec");
+ $self->{_non_ipsec} = $config->existsOrig("ipsec match-none");
+ $self->{_frag} = $config->existsOrig("fragment match-frag");
+ $self->{_non_frag} = $config->existsOrig("fragment match-non-frag");
+ $self->{_recent_time} = $config->returnOrigValue('recent time');
+ $self->{_recent_cnt} = $config->returnOrigValue('recent count');
+
+ $self->{_p2p}->{_all} = $config->existsOrig("p2p all");
+ $self->{_p2p}->{_apple} = $config->existsOrig("p2p applejuice");
+ $self->{_p2p}->{_bit} = $config->existsOrig("p2p bittorrent");
+ $self->{_p2p}->{_dc} = $config->existsOrig("p2p directconnect");
+ $self->{_p2p}->{_edk} = $config->existsOrig("p2p edonkey");
+ $self->{_p2p}->{_gnu} = $config->existsOrig("p2p gnutella");
+ $self->{_p2p}->{_kazaa} = $config->existsOrig("p2p kazaa");
+
+ # TODO: need $config->exists("$level source") in Vyatta::Config.pm
+ $src->setupOrig("$level source");
+ $dst->setupOrig("$level destination");
+
+ return 0;
+}
+
+sub print {
+ my ( $self ) = @_;
+
+ print "name: $self->{_name}\n" if defined $self->{_name};
+ print "rulenum: $self->{_rule_number}\n" if defined $self->{_rule_number};
+ print "protocol: $self->{_protocol}\n" if defined $self->{_protocol};
+ print "state: $self->{_state}\n" if defined $self->{_state};
+ print "action: $self->{_action}\n" if defined $self->{_action};
+ 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();
+
+}
+
+sub is_stateful {
+ my $self = shift;
+ my @states = qw(established new related invalid);
+ foreach (@states) {
+ if (defined($self->{_state}->{"_$_"})
+ && $self->{_state}->{"_$_"} eq "enable") {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+sub get_state_str {
+ my $self = shift;
+ my @states = qw(established new related invalid);
+ my @add_states = ();
+ foreach (@states) {
+ if (defined($self->{_state}->{"_$_"})
+ && $self->{_state}->{"_$_"} eq "enable") {
+ push @add_states, $_;
+ }
+ }
+ if ($#add_states >= 0) {
+ my $str = join ',', @add_states;
+ return $str;
+ } else {
+ return "";
+ }
+}
+
+sub get_num_ipt_rules {
+ my $self = shift;
+ my $ipt_rules = 1;
+ if (("$self->{_log}" eq "enable") && (("$self->{_action}" eq "drop")
+ || ("$self->{_action}" eq "accept")
+ || ("$self->{_action}" eq "reject")
+ || ("$self->{_action}" eq "modify"))) {
+ $ipt_rules += 1;
+ }
+ if (defined($self->{_recent_time}) || defined($self->{_recent_cnt})) {
+ $ipt_rules += 1;
+ }
+ return $ipt_rules;
+}
+
+sub rule {
+ my ( $self ) = @_;
+ my $rule = undef;
+ my $srcrule = $dstrule = undef;
+ my $err_str = undef;
+
+ # set the protocol
+ if (defined($self->{_protocol})) {
+ my $str = $self->{_protocol};
+ $str =~ s/^\!(.*)$/! $1/;
+ $rule .= "--protocol $str ";
+ }
+
+ # set the session state if protocol tcp
+ my $state_str = uc (get_state_str($self));
+ if ($state_str ne "") {
+ $rule .= "-m state --state $state_str ";
+ }
+
+ # set the icmp code and type if applicable
+ if (($self->{_protocol} eq "icmp") || ($self->{_protocol} eq "1")) {
+ if (defined $self->{_icmp_type}) {
+ $rule .= "--icmp-type $self->{_icmp_type}";
+ if (defined $self->{_icmp_code}) {
+ $rule .= "/$self->{_icmp_code}";
+ }
+ $rule .= " ";
+ } elsif (defined $self->{_icmp_code}) {
+ return ("ICMP code can only be defined if ICMP type is defined", );
+
+ }
+ } elsif (defined($self->{_icmp_type}) || defined($self->{_icmp_code})) {
+ return ("ICMP type/code can only be defined if protocol is ICMP", );
+ }
+
+ # add the source and destination rules
+ ($srcrule, $err_str) = $src->rule();
+ return ($err_str, ) if (!defined($srcrule));
+ ($dstrule, $err_str) = $dst->rule();
+ return ($err_str, ) if (!defined($dstrule));
+ if ((grep /multiport/, $srcrule) || (grep /multiport/, $dstrule)) {
+ if ((grep /sport/, $srcrule) && (grep /dport/, $dstrule)) {
+ return ('Cannot specify multiple ports when both '
+ . 'source and destination ports are specified', );
+ }
+ }
+ $rule .= " $srcrule $dstrule ";
+
+ return ('Cannot specify both "match-frag" and "match-non-frag"', )
+ if (defined($self->{_frag}) && defined($self->{_non_frag}));
+ if (defined($self->{_frag})) {
+ $rule .= ' -f ';
+ } elsif (defined($self->{_non_frag})) {
+ $rule .= ' ! -f ';
+ }
+
+ # note: "out" is not valid in the INPUT chain.
+ return ('Cannot specify both "match-ipsec" and "match-none"', )
+ if (defined($self->{_ipsec}) && defined($self->{_non_ipsec}));
+ if (defined($self->{_ipsec})) {
+ $rule .= ' -m policy --pol ipsec --dir in ';
+ } elsif (defined($self->{_non_ipsec})) {
+ $rule .= ' -m policy --pol none --dir in ';
+ }
+
+ my $recent_rule = undef;
+ if (defined($self->{_recent_time}) || defined($self->{_recent_cnt})) {
+ $recent_rule = $rule;
+ $rule .= ' -m recent --update ';
+ $recent_rule .= ' -m recent --set ';
+ if (defined($self->{_recent_time})) {
+ $rule .= " --seconds $self->{_recent_time} ";
+ }
+ if (defined($self->{_recent_cnt})) {
+ $rule .= " --hitcount $self->{_recent_cnt} ";
+ }
+ }
+
+ my $p2p = undef;
+ if (defined($self->{_p2p}->{_all})) {
+ $p2p = '--apple --bit --dc --edk --gnu --kazaa ';
+ } else {
+ my @apps = qw(apple bit dc edk gnu kazaa);
+ foreach (@apps) {
+ if (defined($self->{_p2p}->{"_$_"})) {
+ $p2p .= "--$_ ";
+ }
+ }
+ }
+ if (defined($p2p)) {
+ $rule .= " -m ipp2p $p2p ";
+ }
+
+ my $chain = $self->{_name};
+ my $rule_num = $self->{_rule_number};
+ my $rule2 = undef;
+ # set the jump target. Depends on action and log
+ if ("$self->{_log}" eq "enable") {
+ $rule2 = $rule;
+ $rule2 .= "-j LOG --log-prefix '[$chain $rule_num $self->{_action}] ' ";
+ }
+ if ("$self->{_action}" eq "drop") {
+ $rule .= "-j DROP ";
+ } elsif ("$self->{_action}" eq "accept") {
+ $rule .= "-j RETURN ";
+ } elsif ("$self->{_action}" eq "reject") {
+ $rule .= "-j REJECT ";
+ } elsif ("$self->{_action}" eq 'inspect') {
+ $rule .= "-j QUEUE ";
+ } 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", );
+ }
+ if (defined($rule2)) {
+ my $tmp = $rule2;
+ $rule2 = $rule;
+ $rule = $tmp;
+ } elsif (defined($recent_rule)) {
+ $rule2 = $recent_rule;
+ $recent_rule = undef;
+ }
+ return (undef, $rule, $rule2, $recent_rule, );
+}
+
+sub outputXmlElem {
+ my ($name, $value, $fh) = @_;
+ print $fh " <$name>$value</$name>\n";
+}
+
+sub outputXml {
+ my ($self, $fh) = @_;
+ outputXmlElem("protocol", $self->{_protocol}, $fh);
+ my $state_str = get_state_str($self);
+ if ($state_str ne "") {
+ $state_str =~ s/,/%2C/g;
+ $state_str .= "+";
+ }
+ outputXmlElem("state", $state_str, $fh);
+ outputXmlElem("action", uc($self->{_action}), $fh);
+ outputXmlElem("log", $self->{_log}, $fh);
+ outputXmlElem("icmp_type", $self->{_icmp_type}, $fh);
+ outputXmlElem("icmp_code", $self->{_icmp_code}, $fh);
+
+ $src->outputXml("src", $fh);
+ $dst->outputXml("dst", $fh);
+}
+
+1;