package VyattaIpTablesRule; use VyattaConfig; use VyattaIpTablesAddressFilter; my $src = new VyattaIpTablesAddressFilter; my $dst = new VyattaIpTablesAddressFilter; 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, ); 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, ); 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 VyattaIpTablesAddressFilter; $dst = new VyattaIpTablesAddressFilter; } 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"); # 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"); # 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; } 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 "; # 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 $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; } return (undef, $rule, $rule2, ); } sub outputXmlElem { my ($name, $value, $fh) = @_; print $fh " <$name>$value\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); }