]>
git.ipfire.org Git - people/stevee/guardian.git/blob - modules/Events.pm
1 package Guardian
::Events
;
5 use Exporter
qw(import);
7 our @EXPORT_OK = qw(Init CheckAction GenerateIgnoreList Update);
9 # Hash which stores all supported commands from the queue.
12 'block' => \
&CallBlock
,
13 'unblock' => \
&CallUnblock
,
14 'flush' => \
&CallFlush
,
17 # Hash to store addresses and their current count.
20 # Hash to store all currentyl blocked addresses and a timestamp
21 # when the block for this address can be released.
24 # Hash to store user-defined IP addresses and/or subnets which should be
25 # ignored in case any events should be repored for them.
28 # This object will contain the reference to the logger object after calling Init.
32 ## The "Init" (Block) function.
34 ## This function is responsible to initialize Block as a class based object.
35 ## It has to be called once before any blocking event can be processed.
37 ## The following arguments must be passed, during initialization:
38 ## "BlockCount" and "BlockTime" which both have to be natural numbers.
41 my ( $class, %args ) = @_;
44 # Fail, if some critical arguments are missing.
45 unless ((exists($self->{BlockCount
})) && (exists($self->{BlockTime
}))) {
46 die "Could not initialize Block: Too less arguments are given.\n";
49 # Use bless to make "$self" to an object of class "$class".
52 # Assign logger object.
53 $logger = $self->{Logger
};
55 # Log used firewall engine.
56 $logger->Log("debug", "Using firewall engine: $self->{FirewallEngine}");
58 # Try to load the specified firewall engine or die.
59 my $module_name = "Guardian::" . $self->{FirewallEngine
};
60 eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
62 # Check if an IgnoreFile has been configured.
63 if (exists($self->{IgnoreFile
})) {
64 # Call function to handle the ignore mechanism.
65 &GenerateIgnoreList
($self->{IgnoreFile
});
68 # Return the class object.
73 ## The main "CheckAction" function.
75 ## This function is used to handle each recived event from the main event queue of guardian.
77 ## It will check if the given command is valid and will pass it to the responsible function.
79 sub CheckAction
($$) {
81 my @event = split(/ /, $_[0], 4);
82 my ($command, $address, $module, $message) = @event;
84 # Check if we got an invalid command.
85 unless(exists($commands{$command})) {
86 $logger->Log("err", "The CheckAction function has been called with an unsupported command ($command)!");
90 # Check if the given event contains an address.
92 # Convert and validate the given address.
93 my $bin_address = &Guardian
::Base
::IPOrNet2Int
($address);
95 # Abort if the given address could not be converted because it is not valid.
96 unless ($bin_address) {
97 $logger->Log("err", "Invalid IP address: $address");
101 # Check if address should be ignored.
102 if(&_IsOnIgnoreList
($bin_address)) {
104 $logger->Log("info", "Ignoring event for $address, because it is part of the ignore list.");
109 # Call required handler.
110 my $error = $commands{$command}->($self, $address, $module, $message);
112 # If we got any return code, something went wrong and should be logged.
114 $logger->Log("err", "Got error: $error");
120 ## The main "Counter" function.
122 ## This function is used to handle each count message + address, which has been sent by the main event
125 ## It stores the address and the current count into the counthash and increase the count each time when
126 ## the same address should be counted again. When the current count reaches the configured BlockCount,
127 ## the responsible function will be called to block the address.
131 my ($address, $module, $message) = @_;
134 $logger->Log("debug", "$module reported $message for address: $address");
136 # Increase existing count or start counting for new source addresses.
137 if (exists($counthash{$address})) {
138 # Skip already blocked addresses.
139 if (exists($blockhash{$address})) {
143 # Increase count of the existing entry.
144 $counthash{$address} = $counthash{$address} + 1;
146 # Log altered count of the address.
147 $logger->Log("debug", "Source $address now has count $counthash{$address}/$self->{BlockCount}...");
149 # Log that counting for the address has been started.
150 $logger->Log("debug", "Start counting for $address...");
153 $counthash{$address} = 1;
156 # Check if the address has reached the configured count limit.
157 if ($counthash{$address} >= $self->{BlockCount
}) {
158 # Write out log message.
159 $logger->Log("info", "Blocking $address for $self->{BlockTime} seconds...");
161 # Call block subroutine to block the address.
162 my $error = &CallBlock
($self, $address, $module, $message);
164 # Return the message if an error occurs.
168 # Everything worked well, return nothing.
173 ## The RemoveBlocks function.
175 ## This function periodly will be called and is responsible for releasing the block if the Blocktime
176 ## on an address has expired.
178 ## To do this, the code will loop through all entries of the blockhash and check
179 ## if the estimiated BlockTime of each address has reached and so the block can be released.
181 sub RemoveBlocks
() {
184 # Get the current time.
187 # Loop through the blockhash.
188 foreach my $address (keys %blockhash) {
189 # Check if the blocktime for the address has expired.
190 if ($blockhash{$address} <= $time) {
191 # Call unblock subroutine.
192 my $error = &CallUnblock
($self, $address, "BlockTime", "has expired for $address");
194 # Log error messages if returned.
196 $logger->Log("err", "$error");
201 # Everything okay, return nothing.
206 ## The CallBlock function.
208 ## This function is called, if the BlockCount for an address is reached or a direct
209 ## request for blocking an address has been recieved.
213 my ($address, $module, $message) = @_;
215 # Log the call for blocking an address.
216 $logger->Log("info", "$module - $message");
218 # Check if an entry for this address exists
219 # in the blockhash. If not, the address has
220 # not been blocked yet, call the responisible
221 # function to do this now.
222 unless (exists($blockhash{$address})) {
223 # Obtain the configured FirewallAction.
224 my $action = $self->{FirewallAction
};
226 # Block the given address.
227 my $error = &DoBlock
($address, $action);
229 # If we got back an error message something went wrong.
231 # Exit function and return the used FirewallEngine and the error message.
232 return "$self->{FirewallEngine} - $error";
234 # Address has been successfully blocked, print a log message.
235 $logger->Log("debug", "Address $address successfully has been blocked...");
239 # Generate time when the block will expire.
240 my $expire = time() + $self->{BlockTime
};
242 # Store the blocked address and the expire time
244 $blockhash{$address} = $expire;
246 # Return nothing "undef" if everything is okay.
251 ## CallUnblock function.
253 ## This function is responsible for unblocking and deleting a given
254 ## address from the blockhash.
256 sub CallUnblock
($) {
258 my ($address, $module, $message) = @_;
260 # Log the call for unblocking an address.
261 $logger->Log("info", "$module - $message");
263 # Return an error if no entry for the given address
264 # is present in the blockhash.
265 unless (exists($blockhash{$address})) {
266 return "Address $address was not blocked!";
269 # Unblock the address.
270 my $error = &DoUnblock
($address);
272 # If an error message is returned, something went wrong.
274 # Exit function and return the error message.
277 # Address successfully has been unblocked.
278 $logger->Log("debug", "Address $address successfully has been unblocked...");
281 # Drop address from blockhash.
282 delete ($blockhash{$address});
284 # Everything worked well, return nothing.
289 ## GenerateIgnoreList function.
291 ## This function is responsible for generating/updating the
292 ## IgnoreHash which contains all ignored IP addresses and
295 sub GenerateIgnoreList
($) {
298 # Check if the given IgnoreFile could be opened.
300 $logger->Log("err", "The configured IgnoreFile \($file\) could not be opened. Skipped!");
304 # Open the given IgnoreFile.
305 open (IGNORE
, $file);
307 # Read-in the file line by line.
315 # Remove any newlines.
318 # Check if the line contains a valid single address or network and
319 # convert it into binary format. Store the result/start and
320 # end values in a temporary array.
321 my @values = &Guardian
::Base
::IPOrNet2Int
($_);
323 # If the function returned any values, the line contained a valid
324 # single address or network which successfully has been converted into
327 # Assign the array as value to the ignorehash.
328 $ignorehash{$_} = [@values];
331 $logger->Log("err", "IgnoreFile contains an invalid address/network: $_");
338 # Close filehandle for the IgnoreFile.
343 ## Private function to check if an address is part of the Ignore Hash.
345 ## This function checks if a passed IP address in binary format (!),
346 ## directly or as part of an ignored network is stored in the ignore hash.
348 sub _IsOnIgnoreList
($) {
349 my $bin_address = shift;
351 # Loop through the ignore hash and grab the stored values.
352 foreach my $key ( keys %ignorehash ) {
353 # Dereference values array.
354 my @values = @
{$ignorehash{$key}};
356 # Obtain amount of items for the current value array.
357 my $items = scalar @values;
359 # Whether the amount equals one, the given binary address just
360 # needs to be compared against a single address.
362 my ($ignored_address) = @values;
364 # Simple check if the stored and the given binary address
366 if ($bin_address eq $ignored_address) {
367 # The given address is part of the ignore list.
368 $logger->Log("debug", "Address $key found on the ignore list.");
375 # If the amount equals two, for passed binary address needs to
376 # be checked if it is part of the ignored network range.
377 elsif ($items eq "2") {
378 my ($first_address, $last_address) = @values;
380 # Check if the passed binary address is bigger than
381 # the first address and smaler than the last address
382 # (between) the stored network range.
383 if (($bin_address >= $first_address) && ($bin_address <= $last_address)) {
384 # The address is part of an ignored network.
385 $logger->Log("debug", "Address is found inside the ignored network $key.");
391 # If the amount is not eighter one or two, the current entry of the ignorehash seems
392 # to be corrupted. Skip and log it.
394 # Write log message about this corruped item in the ignore hash.
395 $logger->Log("err", "Invalid item in the Ignore Hash: $key - @values");
397 # Skip this element of the ignore hash.
402 # If we got here, the given address is not part of the ignore hash.
403 # Return nothing (False).