]> git.ipfire.org Git - people/stevee/guardian.git/blob - modules/IPtables.pm
Add ability to reload the ignore list.
[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 # Path where the IPtables related binaries are stored.
13 my $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.
18 my %binaries = (
19 "4" => "iptables",
20 "6" => "ip6tables",
21 );
22
23 # The used firewall chain.
24 my $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 #
36 sub DoBlock (@) {
37 my ($address, $action) = @_;
38
39 # If no action has been given, default to "DROP".
40 unless($action) {
41 $action = "DROP";
42 }
43
44 # Check if the given action is supported.
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);
52
53 # Abort if no suitable binary could be obtained.
54 unless ($iptables) {
55 return "No suitable binary found.";
56 }
57
58 # Call IPtables binary to block the given address.
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 #
77 sub DoUnblock ($) {
78 my ($address) = @_;
79
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
88 # Get rulepositions for the specified address.
89 my @rules = &_get_rules_positions_by_address($address, $iptables);
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 #
101 ## Call this subroutine to entirely flush the IPtables chains for each
102 ## supported protocol version.
103 #
104 sub DoFlush () {
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 }
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 #
125 sub _get_rules_positions_by_address ($$) {
126 my ($address, $iptables) = @_;
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
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 #
166 sub _check_action ($) {
167 my $action = shift;
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) {
173 # Return "1" (True).
174 return 1;
175 }
176 }
177
178 # If we got here, the given action is not part of the array of supported
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 #
189 sub _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;
214 }
215
216 1;