Add IPv6 support to IPtables module.
[people/stevee/guardian.git] / modules / IPtables.pm
CommitLineData
b08d4dde
SS
1package Guardian::IPtables;
2use strict;
3use warnings;
4
5use Exporter qw(import);
6
7our @EXPORT = qw(DoBlock DoUnblock DoFlush);
8
e9c558fe
SS
9# Array of supported block actions.
10my @supported_actions = ("DROP", "REJECT");
11
345e62e2
SS
12# Path where the IPtables related binaries are stored.
13my $bindir = "/usr/sbin/";
14
15# Hash which contains the path to the IPtables binaries,
16# based on the used IP protocol version. IPtables is
17# designed to use different binaries for IPv4 and IPv6.
18my %binaries = (
19 "4" => "iptables",
20 "6" => "ip6tables",
21);
b08d4dde
SS
22
23# The used firewall chain.
24my $chain = "INPUT";
25
26#
27## The DoBlock subroutine.
28#
29## This subroutine is called, when a given address should be locked by
30## using IPtables.
31#
32## Guardian is using the "append" option from IPtables, which will add the new rule
33## to the end of the chain to prevent from possible race-conditions when adding/deleting
34## rules at the same time.
35#
36sub DoBlock (@) {
37 my ($address, $action) = @_;
38
39 # If no action has been given, default to "DROP".
40 unless($action) {
41 $action = "DROP";
42 }
43
e9c558fe 44 # Check if the given action is supported.
345e62e2
SS
45 unless(&_check_action($action)) {
46 # Abort and return an error message.
47 return "Unsupported action: $action";
48 }
49
50 # Obtain which binary should be executed.
51 my $iptables = &_omit_binary($address);
e9c558fe 52
345e62e2
SS
53 # Abort if no suitable binary could be obtained.
54 unless ($iptables) {
55 return "No suitable binary found.";
e9c558fe
SS
56 }
57
345e62e2 58 # Call IPtables binary to block the given address.
b08d4dde
SS
59 system("$iptables --wait -A $chain -s $address -j $action");
60}
61
62#
63## The DoUnblock subroutine.
64#
65## This subroutine can be used to delete all firewall rules (unblock)
66## of a previously blocked address.
67#
68## To do this a subroutine will be called, which is collecting all rule
69## positions in the firewall chain for the given address, which returns
70## them in reversed order. This list of rules will be deleted one-by-one
71## so multiple entries (if present) for the address will be deleted.
72#
73## This approach also eliminates the exact rule argument processing again
74## for dropping it. So it is not neccessary to know the additional arguments
75## like firewall action (DROP, REJECT) etc.
76#
77sub DoUnblock ($) {
78 my ($address) = @_;
79
345e62e2
SS
80 # Obtain which binary should be executed.
81 my $iptables = &_omit_binary($address);
82
83 # Abort if no suitable binary could be obtained.
84 unless($iptables) {
85 return "No suitable binary found.";
86 }
87
b08d4dde 88 # Get rulepositions for the specified address.
345e62e2 89 my @rules = &_get_rules_positions_by_address($address, $iptables);
b08d4dde
SS
90
91 # Loop through the rules array and drop the firewall rules.
92 foreach my $rule (@rules) {
93 # Call iptables to delete the rule.
94 system("$iptables --wait -D $chain $rule");
95 }
96}
97
98#
99## The DoFlush subroutine.
100#
345e62e2
SS
101## Call this subroutine to entirely flush the IPtables chains for each
102## supported protocol version.
b08d4dde
SS
103#
104sub DoFlush () {
345e62e2
SS
105 # Loop through the binaries hash to flush
106 foreach my $key (keys %binaries) {
107 # Grab binary from hash and generate the absolute path
108 # to the binary.
109 my $iptables = join ("/", $bindir,$binaries{$key});
110
111 # Execute the binary if avail- and executeable.
112 if (-x $iptables) {
113 system("$iptables --wait -F $chain");
114 }
115 }
b08d4dde
SS
116}
117
118#
119## Get rules subroutine.
120#
121## This subroutine is used to get the rule position of the active
122## firewall rules for a given address. Those position will be collected
123## and returned in reversed order.
124#
345e62e2
SS
125sub _get_rules_positions_by_address ($$) {
126 my ($address, $iptables) = @_;
b08d4dde
SS
127
128 # Array to store the rule positions.
129 my @rules;
130
131 # Call iptables and list all firewall rules.
132 open(RULES, "$iptables --wait -L $chain -n -v --line-numbers |");
133
134 # Read input line by line.
135 foreach my $line (<RULES>) {
136 # Skip descriptive line.
137 next if ($line =~ /^Chain/);
138 next if ($line =~ /^ pkts/);
139
140 # Generate array, based on the line content
141 # (seperator is a single or multiple space's)
142 my @comps = split(/\s{1,}/, $line);
143 my ($pos, $pkts, $bytes, $target, $prot, $opt, $in, $out, $source, $destination) = @comps;
144
145 # Compare the current source address with the given one.
146 # If they are equal, the rule position will be added to the
147 # rules array.
148 if ($address eq $source) {
149 push(@rules, $pos);
150 }
151 }
152
153 # Reverse the array.
154 my @reversed_rules = reverse @rules;
155
156 # Return the reversed array.
157 return @reversed_rules;
158}
159
e9c558fe
SS
160#
161## The _check_action function.
162#
163## This private function is used to check if the given action is supported by
164## the firewall engine.
165#
166sub _check_action ($) {
345e62e2 167 my $action = shift;
e9c558fe
SS
168
169 # Check if the recieved action is part of the supported_actions array.
170 foreach my $item (@supported_actions) {
171 # Exit the loop and return "nothing" if we found a match.
172 if($item eq $action) {
345e62e2
SS
173 # Return "1" (True).
174 return 1;
e9c558fe
SS
175 }
176 }
177
178 # If we got here, the given action is not part of the array of supported
345e62e2
SS
179 # actions. Return nothing (False).
180 return;
181}
182
183#
184## The _omit_binary function.
185#
186## This private function is responsible for selecting and returning the correct
187## IPtables binary, based on which kind of IP address has been given.
188#
189sub _omit_binary ($) {
190 my $address = shift;
191
192 # Obtain used protocol version, based on the
193 # given IP address.
194 my $proto_version = &Guardian::Base::DetectIPProtocolVersion($address);
195
196 # Abort if the protocol version could not proper be detected.
197 unless ($proto_version) {
198 # Return nothing (False).
199 return;
200 }
201
202 # Obtain which binary is responsible, based on the detected protocol version and
203 # generate the full path to it.
204 my $binary = join("/",$bindir,$binaries{$proto_version});
205
206 # Abort if the obtained binary is not avail- or executeable.
207 unless (-x $binary) {
208 # Return nothing (False).
209 return;
210 }
211
212 # Return the detected and validated binary.
213 return $binary;
e9c558fe
SS
214}
215
b08d4dde 2161;