File Coverage

blib/lib/Farly/Rule/Expander.pm
Criterion Covered Total %
statement 114 129 88.3
branch 32 44 72.7
condition 9 9 100.0
subroutine 14 15 93.3
pod 3 4 75.0
total 172 201 85.5


line stmt bran cond sub pod time code
1             package Farly::Rule::Expander;
2            
3 3     3   1890 use 5.008008;
  3         10  
  3         107  
4 3     3   13 use strict;
  3         6  
  3         79  
5 3     3   13 use warnings;
  3         5  
  3         80  
6 3     3   14 use Carp;
  3         5  
  3         233  
7 3     3   16 use Log::Any qw($log);
  3         5  
  3         23  
8 3     3   209 use Farly::Object::Aggregate;
  3         6  
  3         4947  
9            
10             our $VERSION = '0.26';
11            
12             sub new {
13 3     3 1 22 my ( $class, $fw ) = @_;
14            
15 3 50       16 confess "configuration container object required"
16             unless ( defined($fw) );
17            
18 3 50       40 confess "Farly::Object::List object required"
19             unless ( $fw->isa('Farly::Object::List') );
20            
21 3         16 my $self = {
22             CONFIG => $fw,
23             AGGREGATE => undef,
24             };
25            
26 3         11 bless $self, $class;
27            
28 3         36 $log->info("$self new");
29 3         32 $log->info( "$self CONFIG " . $self->{CONFIG} );
30            
31 3         21 $self->_init();
32            
33 3         21 return $self;
34             }
35            
36 6     6 0 75 sub config { return $_[0]->{CONFIG}; }
37 53     53   250 sub _agg { return $_[0]->{AGGREGATE}; }
38            
39             sub _init {
40 3     3   9 my ($self) = @_;
41 3         16 $self->{AGGREGATE} = Farly::Object::Aggregate->new( $self->config );
42 3         24 $self->{AGGREGATE}->groupby( 'ENTRY', 'ID' );
43             }
44            
45             sub _set_defaults {
46 66     66   102 my ( $self, $ce ) = @_;
47            
48 66         222 my $RULE = Farly::Object->new();
49 66         250 $RULE->set( 'ENTRY', Farly::Value::String->new('RULE') );
50            
51 66         250 my $IP = Farly::Transport::Protocol->new('0');
52 66         282 my $TCP = Farly::Transport::Protocol->new('6');
53 66         210 my $UDP = Farly::Transport::Protocol->new('17');
54 66         228 my $ICMP = Farly::Transport::Protocol->new('1');
55            
56             #Check if the config entry is an access-list
57 66 50       205 if ( $ce->matches($RULE) ) {
58            
59 66 100       185 return if ( $ce->has_defined('COMMENT') );
60            
61             #Check if the access-list protocol is ip, tcp or udp
62 60 100 100     188 if ( $ce->get('PROTOCOL')->equals($IP)
      100        
63             || $ce->get('PROTOCOL')->equals($TCP)
64             || $ce->get('PROTOCOL')->equals($UDP) )
65             {
66            
67 42         208 $log->debug("defaulting ports for $ce");
68            
69             #if a srcport is not defined, define all ports
70 42 100       171 if ( !$ce->has_defined('SRC_PORT') ) {
71            
72 18         114 $ce->set( 'SRC_PORT', Farly::Transport::PortRange->new( 1, 65535 ) );
73 18         60 $log->debug( 'set SRC_PORT = ' . $ce->get('SRC_PORT') );
74             }
75            
76             #if a dst port is not defined, define all ports
77 42 100       153 if ( !$ce->has_defined('DST_PORT') ) {
78            
79 6         31 $ce->set( 'DST_PORT', Farly::Transport::PortRange->new( 1, 65535 ) );
80 6         20 $log->debug( "set DST_PORT = " . $ce->get('DST_PORT') );
81             }
82             }
83            
84 60 100 100     252 if ( $ce->get('PROTOCOL')->equals($IP)
85             || $ce->get('PROTOCOL')->equals($ICMP) )
86             {
87 21         105 $log->debug("defaulting ports for $ce");
88            
89             #if an icmp type is not defined, define all icmp types as -1
90 21 100       82 if ( !$ce->has_defined('ICMP_TYPE') ) {
91            
92 9         54 $ce->set( 'ICMP_TYPE', Farly::IPv4::ICMPType->new(-1) );
93 9         32 $log->debug('set ICMP_TYPE to -1 ');
94             }
95             }
96             }
97             else {
98 0         0 confess "_set_defaults is for RULE objects only";
99             }
100             }
101            
102             sub expand_all {
103 3     3 1 1305 my ($self) = @_;
104            
105 3         20 my $expanded = Farly::Object::List->new();
106            
107 3         25 my $RULE = Farly::Value::String->new('RULE');
108            
109 3         17 my $RULE_SEARCH = Farly::Object->new();
110 3         19 $RULE_SEARCH->set( 'ENTRY', $RULE );
111            
112 3         16 my $rules = Farly::Object::List->new();
113            
114 3         20 $self->config->matches( $RULE_SEARCH, $rules );
115            
116 3         117 foreach my $ce ( $rules->iter() ) {
117 51         88 eval {
118 51         166 my $clone = $ce->clone();
119 51         150 $self->expand( $clone, $expanded );
120             };
121 51 50       145 if ($@) {
122 0         0 confess "$@ \n expand failed for ", $ce->dump(), "\n";
123             }
124             }
125            
126 3         54 return $expanded;
127             }
128            
129             # { 'key' => ::HashRef } refers to one or more actual Objects
130             # Replace the ::HashRef with a ::Set of the actual objects
131             # the actual objects might hold a ::HashRef
132             # { 'key' => ::Set } is a list of config ::Hash or ::HashRef's.
133             # For every object in the Set clone the RULE object
134             # and replace the RULE value with the object from the ::Set
135             # { 'key' => Farly::Object }
136             # use "OBJECT" key/value in the raw RULE object
137            
138             sub expand {
139 51     51 1 82 my ( $self, $rule, $result ) = @_;
140            
141 51         65 my $is_expanded;
142             my @stack;
143 51         88 push @stack, $rule;
144            
145 51         171 my $COMMENT = Farly::Object->new();
146 51         266 $COMMENT->set( 'OBJECT_TYPE', Farly::Value::String->new('COMMENT') );
147            
148 51         167 my $SERVICE = Farly::Object->new();
149 51         4933 $SERVICE->set( 'OBJECT_TYPE', Farly::Value::String->new('SERVICE') );
150            
151 51         179 my $VIP = Farly::Object->new();
152 51         177 $VIP->set( 'OBJECT_TYPE', Farly::Value::String->new('VIP') );
153            
154 51         136 while (@stack) {
155 243         367 my $ce = pop @stack;
156            
157 243         652 foreach my $key ( $ce->get_keys() ) {
158            
159 1216         3174 my $value = $ce->get($key);
160            
161 1216         5671 $log->debug("entry $ce : key = $key : value = $value");
162            
163 1216         3072 $is_expanded = 1;
164            
165 1216 100       13978 if ( $value->isa('Farly::Object::Ref') ) {
    100          
    100          
166            
167 53         67 $is_expanded = 0;
168            
169 53         142 my $actual = $self->_agg->matches($value);
170            
171 53 50       199 if ( !defined $actual ) {
172 0         0 confess "actual not found for $key";
173             }
174            
175 53         158 $ce->set( $key, $actual );
176            
177 53         77 push @stack, $ce;
178            
179 53         109 last;
180             }
181             elsif ( $value->isa('Farly::Object::List') ) {
182            
183 53         68 $is_expanded = 0;
184            
185 53         265 $log->debug("$ce => $key isa $value");
186            
187 53         240 foreach my $object ( $value->iter() ) {
188            
189 68         191 my $clone = $ce->clone();
190            
191 68         227 $clone->set( $key, $object );
192            
193 68         183 push @stack, $clone;
194             }
195            
196 53         122 last;
197             }
198             elsif ( $value->isa('Farly::Object') ) {
199            
200 71         98 $is_expanded = 0;
201            
202 71         191 my $clone = $ce->clone();
203            
204 71 50       212 if ( $value->matches($COMMENT) ) {
205            
206 0         0 $log->debug( "skipped group comment :\n" . $ce->dump() . "\n" );
207            
208 0         0 last;
209             }
210 71 50       252 if ( $value->matches($VIP) ) {
    100          
    50          
211            
212 0         0 $self->_expand_vip( $key, $clone, $value );
213             }
214             elsif ( $value->matches($SERVICE) ) {
215            
216 10         41 $self->_expand_service( $clone, $value );
217             }
218             elsif ( $value->has_defined('OBJECT') ) {
219            
220 61         167 $clone->set( $key, $value->get('OBJECT') );
221             }
222             else {
223            
224 0         0 $log->warn( "skipped $ce property $key has no OBJECT\n" . $ce->dump() );
225            
226 0         0 last;
227             }
228            
229 71         216 push @stack, $clone;
230            
231 71         148 last;
232             }
233             }
234            
235 243 100       1186 if ($is_expanded) {
236 66         184 $self->_set_defaults($ce);
237 66         345 $result->add($ce);
238             }
239             }
240            
241 51         281 return $result;
242             }
243            
244             sub _expand_service {
245 10     10   23 my ( $self, $clone, $service_object ) = @_;
246 10         33 my @keys = qw(PROTOCOL SRC_PORT DST_PORT ICMP_TYPE);
247 10         58 foreach my $key (@keys) {
248 40 100       110 if ( $service_object->has_defined($key) ) {
249 20         59 $clone->set( $key, $service_object->get($key) );
250             }
251             }
252 10         31 return;
253             }
254            
255             sub _expand_vip {
256 0     0     my ( $self, $key, $clone, $vip_object ) = @_;
257            
258 0           $log->debug("processing VIP $vip_object : key = $key");
259            
260 0 0         if ( $key eq 'DST_IP' ) {
    0          
261 0           $clone->set( $key, $vip_object->get('REAL_IP') );
262             }
263             elsif ( $key eq 'DST_PORT' ) {
264 0           $clone->set( $key, $vip_object->get('REAL_PORT') );
265             }
266             else {
267 0           confess "invalid key for VIP\n", "key $key \n", "rule: ",
268             $clone->dump(), "\n", "vip: ", $vip_object->dump(), "\n";
269             }
270            
271 0           return;
272             }
273            
274             1;
275             __END__