7395d3e32c23855868764a9983cb2343ed17c4a4
[people/stevee/guardian.git] / modules / IPtables.pm
1 package Guardian::IPtables;
2 use strict;
3 use warnings;
4
5 use Exporter qw(import);
6
7 our @EXPORT = qw(DoBlock DoUnblock DoFlush);
8
9 # Array of supported block actions.
10 my @supported_actions = ("DROP", "REJECT");
11
12 # The path to the iptables executeable.
13 my $iptables = "/usr/sbin/iptables";
14
15 # The used firewall chain.
16 my $chain = "INPUT";
17
18 #
19 ## The DoBlock subroutine.
20 #
21 ## This subroutine is called, when a given address should be locked by
22 ## using IPtables.
23 #
24 ## Guardian is using the "append" option from IPtables, which will add the new rule
25 ## to the end of the chain to prevent from possible race-conditions when adding/deleting
26 ## rules at the same time.
27 #
28 sub DoBlock (@) {
29         my ($address, $action) = @_;
30
31         # If no action has been given, default to "DROP".
32         unless($action) {
33                 $action = "DROP";
34         }
35
36         # Check if the given action is supported.
37         my $error = &_check_action($action);
38
39         # Abort and return the recieved error.
40         if ($error) {
41                 return $error;
42         }
43
44         # Call iptables to block the given address.
45         system("$iptables --wait -A $chain -s $address -j $action");
46 }
47
48 #
49 ## The DoUnblock subroutine.
50 #
51 ## This subroutine can be used to delete all firewall rules (unblock)
52 ## of a previously blocked address.
53 #
54 ## To do this a subroutine will be called, which is collecting all rule
55 ## positions in the firewall chain for the given address, which returns
56 ## them in reversed order. This list of rules will be deleted one-by-one
57 ## so multiple entries (if present) for the address will be deleted.
58 #
59 ## This approach also eliminates the exact rule argument processing again
60 ## for dropping it. So it is not neccessary to know the additional arguments
61 ## like firewall action (DROP, REJECT) etc. 
62 #
63 sub DoUnblock ($) {
64         my ($address) = @_;
65
66         # Get rulepositions for the specified address.
67         my @rules = &_get_rules_positions_by_address($address);
68
69         # Loop through the rules array and drop the firewall rules.
70         foreach my $rule (@rules) {
71                 # Call iptables to delete the rule.
72                 system("$iptables --wait -D $chain $rule");
73         }
74 }
75
76 #
77 ## The DoFlush subroutine.
78 #
79 ## Call this subroutine to entirely flush the IPtables chain.
80 #
81 sub DoFlush () {
82         # Call iptalbes and flush the chain.
83         system("$iptables --wait -F $chain");
84 }
85
86 #
87 ## Get rules subroutine.
88 #
89 ## This subroutine is used to get the rule position of the active
90 ## firewall rules for a given address. Those position will be collected
91 ## and returned in reversed order. 
92 #
93 sub _get_rules_positions_by_address ($) {
94         my ($address) = @_;
95
96         # Array to store the rule positions.
97         my @rules;
98
99         # Call iptables and list all firewall rules.
100         open(RULES, "$iptables --wait -L $chain -n -v --line-numbers |");
101
102         # Read input line by line.
103         foreach my $line (<RULES>) {
104                 # Skip descriptive line.
105                 next if ($line =~ /^Chain/);
106                 next if ($line =~ /^ pkts/);
107
108                 # Generate array, based on the line content
109                 # (seperator is a single or multiple space's)
110                 my @comps = split(/\s{1,}/, $line);
111                 my ($pos, $pkts, $bytes, $target, $prot, $opt, $in, $out, $source, $destination) = @comps;
112
113                 # Compare the current source address with the given one.
114                 # If they are equal, the rule position will be added to the
115                 # rules array.
116                 if ($address eq $source) {
117                         push(@rules, $pos);
118                 }
119         }
120
121         # Reverse the array.
122         my @reversed_rules = reverse @rules;
123
124         # Return the reversed array.
125         return @reversed_rules;
126 }
127
128 #
129 ## The _check_action function.
130 #
131 ## This private function is used to check if the given action is supported by
132 ## the firewall engine.
133 #
134 sub _check_action ($) {
135         my $action = $_[0];
136
137         # Check if the recieved action is part of the supported_actions array.
138         foreach my $item (@supported_actions) {
139                 # Exit the loop and return "nothing" if we found a match.
140                 if($item eq $action) {
141                         return;
142                 }
143         }
144
145         # If we got here, the given action is not part of the array of supported
146         # actions. Return an error message.
147         return "Unsupported action: $action";
148 }
149
150 1;