package Farly::Rules;
use 5.008008;
use strict;
use warnings;
use Carp;
use Data::Dumper;
use Log::Log4perl qw(get_logger);
our $VERSION = '0.12';
sub new {
my ( $class, $fw ) = @_;
confess "configuration container object required"
unless ( defined($fw) );
confess "Object::KVC::List object required"
unless ( $fw->isa("Object::KVC::List") );
my $self = {
CONFIG => $fw,
INDEX => undef,
};
bless $self, $class;
my $logger = get_logger(__PACKAGE__);
$logger->info("$self NEW");
$logger->info("$self CONFIG ",$self->{CONFIG});
$self->_init();
return $self;
}
sub config { return $_[0]->{CONFIG}; }
sub _index { return $_[0]->{INDEX}; }
sub _init {
my ($self) = @_;
$self->{INDEX} = Object::KVC::Index->new( $self->config );
$self->{INDEX}->make_index( "ENTRY", "ID" );
}
sub _set_defaults {
my ( $self, $ce ) = @_;
my $logger = get_logger(__PACKAGE__);
my $RULE = Object::KVC::Hash->new();
$RULE->set( 'ENTRY', Object::KVC::String->new('RULE') );
my $IP = Farly::Transport::Protocol->new('0');
my $TCP = Farly::Transport::Protocol->new('6');
my $UDP = Farly::Transport::Protocol->new('17');
my $ICMP = Farly::Transport::Protocol->new('1');
#Check if the config entry is an access-list
if ( $ce->matches($RULE) ) {
return if ( $ce->has_defined('COMMENT') );
#Check if the access-list protocol is ip, tcp or udp
if ( $ce->get('PROTOCOL')->equals($IP)
|| $ce->get('PROTOCOL')->equals($TCP)
|| $ce->get('PROTOCOL')->equals($UDP) )
{
$logger->debug("defaulting ports for $ce");
#if a srcport is not defined, define all ports
if ( !$ce->has_defined('SRC_PORT') ) {
$ce->set( 'SRC_PORT', Farly::Transport::PortRange->new( 1, 65535 ) );
$logger->debug( 'SET SOURCE PORT ', $ce->get('SRC_PORT') );
}
#if a dst port is not defined, define all ports
if ( !$ce->has_defined('DST_PORT') ) {
$ce->set( 'DST_PORT', Farly::Transport::PortRange->new( 1, 65535 ) );
$logger->debug( "SET DST PORT ", $ce->get('DST_PORT') );
}
}
elsif( $ce->get('PROTOCOL')->equals($ICMP) ) {
$logger->debug("defaulting ports for $ce");
#if an icmp type is not defined, define all icmp types as 255
if ( !$ce->has_defined('ICMP_TYPE') ) {
$ce->set( 'ICMP_TYPE', Farly::IPv4::ICMPType->new( -1 ) );
$logger->debug( 'SET ICMP_TYPE to -1 ');
}
}
}
else {
confess "_set_defaults is for RULE objects only";
}
}
sub expand_all {
my ($self) = @_;
my $logger = get_logger(__PACKAGE__);
my $expanded = Object::KVC::List->new();
my $RULE = Object::KVC::String->new('RULE');
my $RULE_SEARCH = Object::KVC::Hash->new();
$RULE_SEARCH->set( 'ENTRY', $RULE );
my $rules = Object::KVC::List->new();
$self->config->matches( $RULE_SEARCH, $rules );
foreach my $ce ( $rules->iter() ) {
eval {
my $clone = $ce->clone();
$self->expand( $clone, $expanded );
};
if ($@) {
confess "$@ \n expand failed for ", $ce->dump(), "\n";
}
}
return $expanded;
}
# { 'key' => ::HashRef } refers to one or more actual Objects
# Replace the ::HashRef with a ::Set of the actual objects
# the actual objects might hold a ::HashRef
# { 'key' => ::Set } is a list of config ::Hash or ::HashRef's.
# For every object in the Set clone the RULE object
# and replace the RULE value with the object from the ::Set
# { 'key' => Object::KVC::Hash }
# use "OBJECT" key/value in the raw RULE object
sub expand {
my ( $self, $rule, $result ) = @_;
my $logger = get_logger(__PACKAGE__);
my $is_expanded;
my @stack;
push @stack, $rule;
my $COMMENT = Object::KVC::Hash->new();
$COMMENT->set( "OBJECT_TYPE", Object::KVC::String->new("COMMENT") );
my $SERVICE = Object::KVC::Hash->new();
$SERVICE->set( "OBJECT_TYPE", Object::KVC::String->new("SERVICE") );
my $VIP = Object::KVC::Hash->new();
$VIP->set( "OBJECT_TYPE", Object::KVC::String->new("VIP") );
while (@stack) {
my $ce = pop @stack;
foreach my $key ( $ce->get_keys() ) {
my $value = $ce->get($key);
$logger->debug("entry $ce - key : $key - value : $value");
$is_expanded = 1;
if ( $value->isa("Object::KVC::HashRef") ) {
$is_expanded = 0;
my $actual = $self->_index->fetch(
$value->get('ENTRY')->as_string(),
$value->get('ID')->as_string()
);
if ( !defined $actual ) {
confess "actual not found for $key";
}
$ce->set( $key, $actual );
push @stack, $ce;
last;
}
elsif ( $value->isa("Object::KVC::Set") ) {
$is_expanded = 0;
$logger->debug("$ce => $key isa $value");
foreach my $object ( $value->iter() ) {
my $clone = $ce->clone();
$clone->set( $key, $object );
push @stack, $clone;
}
last;
}
elsif ( $value->isa("Object::KVC::Hash") ) {
$is_expanded = 0;
my $clone = $ce->clone();
if ( $value->matches($COMMENT) ) {
$logger->debug( "skipped group comment :\n", $ce->dump(),"\n" );
last;
}
if ( $value->matches($VIP) ) {
$self->_expand_vip( $key, $clone, $value );
}
elsif ( $value->matches($SERVICE) ) {
$self->_expand_service( $clone, $value );
}
elsif ( $value->has_defined("OBJECT") ) {
$clone->set( $key, $value->get("OBJECT") );
}
else {
$logger->warn("skipped $ce property $key has no OBJECT\n", $ce->dump() );
last;
}
push @stack, $clone;
last;
}
}
if ($is_expanded) {
$self->_set_defaults($ce);
$result->add($ce);
}
}
return $result;
}
sub _expand_service {
my ( $self, $clone, $service_object ) = @_;
my @keys = qw(PROTOCOL SRC_PORT DST_PORT ICMP_TYPE);
foreach my $key (@keys) {
if ( $service_object->has_defined($key) ) {
$clone->set( $key, $service_object->get($key) );
}
}
return;
}
sub _expand_vip {
my ( $self, $key, $clone, $vip_object ) = @_;
my $logger = get_logger(__PACKAGE__);
$logger->debug("processing VIP : $vip_object - key : $key");
if ( $key eq "DST_IP" ) {
$clone->set( $key, $vip_object->get("REAL_IP") );
}
elsif ( $key eq "DST_PORT" ) {
$clone->set( $key, $vip_object->get("REAL_PORT") );
}
else {
confess "invalid key for VIP\n", "key $key \n",
"rule: ", $clone->dump(), "\n", "vip: ", $vip_object->dump(), "\n";
}
return;
}
1;
__END__
=head1 NAME
Farly::Rules - - Deprecated - is now Farly::Rule::Expander
=head1 DESCRIPTION
Farly::Rules converts a firewall rule configuration into a raw rule set.
The raw ruleset is an Object::KVC::List<Object::KVC::Hash> containing
all firewall rules.
A raw rule set has no references to other firewall objects. The expanded
firewall rule is for specific packet to firewall rule matching.
=head1 METHODS
=head2 new( $list )
The constructor. The firewall configuration is provided.
$rule_expander = Farly::Rules->new( <Object::KVC::List> );
=head2 expand_all()
Returns an Object::KVC::List<Object::KVC::Hash> container of all
raw expanded firewall rules in the current Farly firewall model.
$expanded_ruleset = $rule_expander->expand_all();
=head2 expand( $rule<Object::KVC::Hash>, $result<Object::KVC::List|Object::KVC::Set>)
Returns the expanded version of the given firewall rule in the
provided result container.
$expanded_rule = $rule_expander->expand( $rule );
=head1 COPYRIGHT AND LICENCE
Farly::Rules
Copyright (C) 2012 Trystan Johnson
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.