diff options
author | Stephen Hemminger <stephen.hemminger@vyatta.com> | 2008-11-21 08:39:37 -0800 |
---|---|---|
committer | Stephen Hemminger <stephen.hemminger@vyatta.com> | 2008-11-21 09:33:37 -0800 |
commit | 6946d499c77aa022ca803a9a27db73f46e62d772 (patch) | |
tree | 10ddb057f4b505b5db8ad2e8eb6ef2da8250532e /lib/Vyatta/IpTables/Rule.pm | |
parent | f3d9c03906886375e48793e63403fbaa38327aae (diff) | |
download | vyatta-cfg-firewall-6946d499c77aa022ca803a9a27db73f46e62d772.tar.gz vyatta-cfg-firewall-6946d499c77aa022ca803a9a27db73f46e62d772.zip |
Rename VyattaIpTablesRule to Vyatta::IpTables::Rule
Diffstat (limited to 'lib/Vyatta/IpTables/Rule.pm')
-rw-r--r-- | lib/Vyatta/IpTables/Rule.pm | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/lib/Vyatta/IpTables/Rule.pm b/lib/Vyatta/IpTables/Rule.pm new file mode 100644 index 0000000..196a3ca --- /dev/null +++ b/lib/Vyatta/IpTables/Rule.pm @@ -0,0 +1,365 @@ +package Vyatta::IpTables::Rule; + +use VyattaConfig; +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, +); + +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, +); + +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 VyattaConfig; + + $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'); + + # TODO: need $config->exists("$level source") in VyattaConfig.pm + $src->setup("$level source"); + $dst->setup("$level destination"); + + return 0; +} + +sub setupOrig { + my ( $self, $level ) = @_; + my $config = new VyattaConfig; + + $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'); + + # TODO: need $config->exists("$level source") in VyattaConfig.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 $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; |