File Coverage

blib/lib/Parser/IPTables/Save.pm
Criterion Covered Total %
statement 68 146 46.5
branch 29 100 29.0
condition 13 21 61.9
subroutine 7 14 50.0
pod 9 9 100.0
total 126 290 43.4


line stmt bran cond sub pod time code
1             package Parser::IPTables::Save;
2              
3 1     1   27195 use warnings;
  1         3  
  1         36  
4 1     1   7 use strict;
  1         3  
  1         36  
5              
6 1     1   17 use Carp qw/ croak /;
  1         2  
  1         56  
7 1     1   6 use Tie::File;
  1         1  
  1         2370  
8              
9              
10             =head1 NAME
11              
12             Parser::IPTables::Save - A parser for iptables-save output files.
13              
14             =head1 VERSION
15              
16             Version 0.04
17              
18             =cut
19              
20             our $VERSION = '0.04';
21              
22              
23             =head1 SYNOPSIS
24              
25             Quick summary of what the module does.
26              
27             Perhaps a little code snippet.
28              
29             use Parser::IPTables::Save;
30              
31             my $iptables_save = Parser::IPTables::Save->new('/tmp/iptables.out');
32             $iptables_save->table('filter');
33              
34             $iptables_save->create({ chain => 'POSTROUTING', source => '192.168.1.0/24', target => 'MASQUERADE', comment => 'Rule to masquerade' });
35              
36             $iptables_save->move(3, -1);
37              
38             $iptables_save->delete(8);
39             $iptables_save->delete(9);
40              
41             my @rules = $iptables_save->fetch_rules();
42              
43             $iptables_save->disable(10);
44             $iptables_save->enable(10);
45              
46             $iptables_save->save();
47              
48              
49             =head1 METHODS
50              
51             =head2 create
52              
53             Create a new rule
54              
55             $iptables_save->create({ chain => 'POSTROUTING', source => '192.168.1.0/24', target => 'MASQUERADE', comment => 'Rule to masquerade' });
56              
57             =cut
58              
59             sub create {
60 0     0 1 0 my($self, $rule, $index) = @_;
61              
62 0 0       0 if($index) {
63             # replace
64 0         0 splice(@{ $self->{rules}}, $index, 0, ($rule));
  0         0  
65             }
66             else {
67 0         0 unshift(@{ $self->{rules} }, $rule);
  0         0  
68             }
69             }
70              
71              
72             =head2 delete
73              
74             Delete a rule
75              
76             $iptables_save->delete(8);
77              
78             =cut
79              
80             sub delete {
81              
82 0     0 1 0 my($self, $index) = @_;
83              
84 0         0 delete @{ $self->{rules}}[$index];
  0         0  
85             }
86              
87              
88             =head2 disable
89              
90             disable a rule
91              
92             # disable rule with index 5
93             $iptables_save->disable(5);
94              
95             =cut
96              
97             sub disable {
98              
99 0     0 1 0 my($self, $index) = @_;
100              
101 0         0 @{ $self->{rules}}[$index]->{disabled} = 1;
  0         0  
102              
103 0         0 return;
104              
105             }
106              
107              
108             =head2 enable
109              
110             enable a rule
111              
112             # enable a rule previously disabled
113             $iptables_save->enable(5);
114              
115             =cut
116              
117             sub enable {
118              
119 0     0 1 0 my($self, $index) = @_;
120              
121 0         0 @{ $self->{rules}}[$index]->{disabled} = 0;
  0         0  
122              
123 0         0 return;
124              
125             }
126              
127              
128             =head2 new
129              
130             =cut
131              
132             sub new {
133              
134 1     1 1 423 my($proto, $iptables_save_file) = @_;
135              
136 1 50 33     11 unless( $proto && defined($iptables_save_file) ) { croak "Parser::IPTables::Save->new(): usage error"; }
  0         0  
137              
138 1   33     7 my $class = ref($proto) || $proto;
139              
140 1         3 my $self = {};
141              
142             # open iptables-save output file
143 1 50       8 tie my @file_array, 'Tie::File', $iptables_save_file or croak("Error opening file $iptables_save_file $!");
144              
145 1         200 $self->{file_array} = \@file_array;
146              
147 1         4 bless($self, $class);
148              
149 1         4 return $self;
150              
151             }
152              
153              
154             =head2 table
155              
156             Set table name
157              
158             $iptables_save->table('filter');
159              
160             =cut
161              
162             sub table {
163              
164 1     1 1 339 my($self, $table_name) = @_;
165              
166             # set table name
167 1         3 $self->{table_name} = $table_name;
168              
169 1         2 my $get_lines = 0;
170 1         2 my @rules;
171              
172             # set line of
173 1         2 my $i = 0;
174              
175 1         2 foreach my $line (@{ $self->{file_array} }) {
  1         8  
176              
177 7 100       304 if($line eq '*'.$self->{table_name}) {
178 1         100 $get_lines = 1;
179 1         2 $self->{initial_line} = ($i + 1);
180 1         5 next;
181             }
182              
183             # packet counters
184 6 100 100     701 if($get_lines == 1 && substr($line, 0, 1) eq ':') {
    100 66        
      66        
185 3         6 $self->{initial_line}++;
186             }
187             elsif($get_lines == 1 && (substr($line, 0, 2) eq '-A' || substr($line, 0, 3) eq '#-A')) {
188              
189 1         3 my $rule = {};
190              
191             # if rule is disabled
192 1 50       6 $rule->{disabled} = 1 if(substr($line, 0, 1) eq '#');
193              
194             # chain
195 1 50       10 $rule->{chain} = $1 if($line =~ /-A\s+([\w]+)/g);
196              
197             # protocol
198 1 50       13 if($line =~ /-p\s+([\!\w\d]+)/g) {
199 1         3 $rule->{proto} = $1;
200              
201             # when get only ! character
202 1 50       4 if($rule->{proto} =~ /^\!$/) {
203 0 0       0 $rule->{proto} = '! '.$1 if($line =~ /-p\s+\!\s+([\!\w\d]+)/);
204             }
205             }
206              
207             # module
208 1         2 my @modules;
209 1         5 while($line =~ /-m\s+([\w\d]+)/g) {
210 1         5 push(@modules, $1);
211             }
212 1         3 $rule->{module} = \@modules;
213              
214             # source
215 1 50       5 if($line =~ /-s\s+([\w\d\!\-\.\/]+)/g) {
216 1         3 $rule->{source} = $1;
217              
218             # when get only ! character
219 1 50       10 if($rule->{source} =~ /^\!$/) {
220 0 0       0 $rule->{source} = '! '.$1 if($line =~ /-s\s+\!\s+([\w\d\!\-\.\/]+)/);
221             }
222             }
223              
224             # destination
225 1 50       5 if($line =~ /-d\s+([\w\d\!\-\.\/]+)/g) {
226 1         4 $rule->{destination} = $1;
227              
228             # when get only ! character
229 1 50       5 if($rule->{destination} =~ /^\!$/) {
230 1 50       8 $rule->{destination} = '! '.$1 if($line =~ /-d\s+\!\s+([\w\d\!\-\.\/]+)/);
231             }
232             }
233              
234             # state
235 1 50       8 if($line =~ /--state\s+(\w+)/g) {
236 0         0 $rule->{state} = $1;
237             }
238              
239             # source port
240 1 50       6 if($line =~ /--sport\s+([\w\:]+)/g) {
241 0         0 $rule->{port_source} = $1;
242              
243             # when get only ! character
244 0 0       0 if($rule->{port_source} =~ /^\!$/) {
245 0 0       0 $rule->{port_source} = '! '.$1 if($line =~ /-d\s+\!\s+([\w\:]+)/);
246             }
247             }
248              
249             # destination port
250 1 50       9 if($line =~ /--dport\s+([\w\:]+)/g) {
251 1         4 $rule->{port_destination} = $1;
252              
253             # when get only ! character
254 1 50       5 if($rule->{port_destination} =~ /^\!$/) {
255 0 0       0 $rule->{port_destination} = '! '.$1 if($line =~ /-d\s+\!\s+([\w\:]+)/);
256             }
257             }
258              
259              
260             # target
261 1 50       8 $rule->{target} = $1 if($line =~ /-j\s+([\w]+)/);
262              
263             # target param1
264 1 50       10 $rule->{target_param1} = $2 if($line =~ /-j\s+(.*?)\s+([\w\-]*)/);
265              
266             # target param2
267 1 50       7 $rule->{target_param2} = $3 if($line =~ /-j\s+(.*?)\s+([\w\-]*)\s+([\w\-\.\:]*)/);
268              
269              
270             # prevent target_param1 and targer_param2 from get --comment
271 1 50 33     5 if($rule->{target_param1} && $rule->{target_param1} eq '--comment') {
272 0         0 $rule->{target_param1} = '';
273 0         0 $rule->{target_param2} = '';
274             }
275              
276              
277             # comment
278 1 50       4 $rule->{comment} = $1 if($line =~ /--comment\s+\"(.*)\"/);
279              
280              
281 1         3 push(@rules, $rule);
282              
283             }
284              
285 6 100 100     36 last if($get_lines == 1 && $line eq 'COMMIT');
286              
287 5         20 $i++;
288              
289             }
290              
291             # number of rules
292 1         9 $self->{number_of_rules} = @rules;
293              
294             # save rules on object
295 1         3 $self->{rules} = \@rules;
296              
297 1 50       10 return $self->{table_name} if($self->{table_name});
298              
299 0         0 return 0;
300              
301             }
302              
303              
304             =head2 fetch_rules
305              
306             =cut
307              
308             sub fetch_rules {
309              
310 0     0 1 0 my $self = shift;
311              
312 0 0       0 croak("You need set a table name: \$obj->table('tablename');") if(! $self->{table_name});
313              
314             # set index foreach row
315 0         0 my @trules;
316 0         0 my $i = 0;
317 0         0 foreach my $row (@{ $self->{rules} }) {
  0         0  
318              
319 0         0 $row->{id} = $i;
320 0         0 $i++;
321              
322 0         0 push(@trules, $row);
323              
324             }
325              
326             # save trules on object
327 0         0 $self->{rules} = \@trules;
328              
329             # if wants a array
330 0 0       0 return @{ $self->{rules} } if wantarray();
  0         0  
331              
332             # if wants a arrayref
333 0         0 return $self->{rules};
334              
335             }
336              
337              
338             =head2 DESTROY
339              
340             =cut
341              
342             sub DESTROY {
343 1     1   313 my $self = shift;
344 1         11 untie $self->{file_array};
345             }
346              
347              
348             =head2 move
349              
350             Move rules
351              
352             # Move rule of index 1, 3 positions down
353             $iptables_save->move(1, 3);
354              
355             =cut
356              
357             sub move {
358              
359 0     0 1   my($self, $index, $move) = @_;
360              
361             # Get rule
362 0           my $rule = @{ $self->{rules}}[$index];
  0            
363              
364             # Prevent eg: index 0, move -1
365 0 0         if($move < 0) {
366 0           $move *= -1;
367 0 0         return if(($index - $move) < 0);
368             }
369              
370             # delete rule on current position
371 0           delete @{ $self->{rules}}[$index];
  0            
372              
373             # prevent erro where $move > 0
374 0 0         $move++ if($move > 0);
375              
376             # replace
377 0           splice(@{ $self->{rules}}, ($index + $move), 0, ($rule));
  0            
378              
379             }
380              
381              
382             =head2 save
383              
384             $iptables_save->save();
385              
386             =cut
387              
388             sub save {
389 0     0 1   my $self = shift;
390              
391 0           my @trules;
392              
393 0           foreach my $rule (@{ $self->{rules} }) {
  0            
394              
395             # if deleted rule
396 0 0         next if(!$rule);
397              
398             # Mount rule
399 0           my $str_rule = '';
400              
401             # if rule is disabled
402 0 0         $str_rule .= '#' if($rule->{disabled});
403              
404             # chain
405 0           $str_rule .= '-A '.$rule->{chain}.' ';
406              
407             # protocol
408 0 0         $str_rule .= '-p '.$rule->{proto}.' ' if($rule->{proto});
409              
410             # modules
411 0           foreach my $module (@{ $rule->{module} }) {
  0            
412 0           $str_rule .= '-m '.$module.' ';
413             }
414              
415             # interface input
416 0 0         $str_rule .= '-i '.$rule->{iface_input}.' ' if($rule->{iface_input});
417              
418             # interface output
419 0 0         $str_rule .= '-o '.$rule->{iface_output}.' ' if($rule->{iface_output});
420              
421             # source
422 0 0         $str_rule .= '-s '.$rule->{source}.' ' if($rule->{source});
423              
424             # destination
425 0 0         $str_rule .= '-d '.$rule->{destination}.' ' if($rule->{destination});
426              
427             # state
428 0 0         $str_rule .= '--state '.$rule->{state}.' ' if($rule->{state});
429              
430             # source port
431 0 0         $str_rule .= '--sport '.$rule->{port_source}.' ' if($rule->{port_source});
432              
433             # destination port
434 0 0         $str_rule .= '--dport '.$rule->{port_destination}.' ' if($rule->{port_destination});
435              
436             # target
437 0 0         $str_rule .= '-j '.$rule->{target}.' ' if($rule->{target});
438              
439             # target param1
440 0 0         $str_rule .= $rule->{target_param1}.' ' if($rule->{target_param1});
441              
442             # target param2
443 0 0         $str_rule .= $rule->{target_param2}.' ' if($rule->{target_param2});
444              
445             # comment
446 0 0         $str_rule .= '--comment "'.$rule->{comment}.'" ' if($rule->{comment});
447              
448 0           push(@trules, $str_rule);
449             }
450              
451 0           splice(@{ $self->{file_array} }, $self->{initial_line}, $self->{number_of_rules}, @trules);
  0            
452              
453             }
454              
455              
456             =head1 AUTHOR
457              
458             Geovanny Junio, C<< >>
459              
460             =head1 BUGS
461              
462             Please report any bugs or feature requests to C, or through
463             the web interface at L. I will be notified, and then you'll
464             automatically be notified of progress on your bug as I make changes.
465              
466              
467              
468              
469             =head1 SUPPORT
470              
471             You can find documentation for this module with the perldoc command.
472              
473             perldoc Parser::IPTables::Save
474              
475              
476             You can also look for information at:
477              
478             =over 4
479              
480             =item * RT: CPAN's request tracker
481              
482             L
483              
484             =item * AnnoCPAN: Annotated CPAN documentation
485              
486             L
487              
488             =item * CPAN Ratings
489              
490             L
491              
492             =item * Search CPAN
493              
494             L
495              
496             =back
497              
498              
499             =head1 ACKNOWLEDGEMENTS
500              
501              
502             =head1 LICENSE AND COPYRIGHT
503              
504             Copyright 2010 Geovanny Junio.
505              
506             This program is free software; you can redistribute it and/or modify it
507             under the terms of either: the GNU General Public License as published
508             by the Free Software Foundation; or the Artistic License.
509              
510             See http://dev.perl.org/licenses/ for more information.
511              
512              
513             =cut
514              
515             1; # End of Parser::IPTables::Save