Introduce "Events" module
authorStefan Schantl <stefan.schantl@ipfire.org>
Tue, 19 Jan 2016 11:32:32 +0000 (12:32 +0100)
committerStefan Schantl <stefan.schantl@ipfire.org>
Tue, 19 Jan 2016 11:32:32 +0000 (12:32 +0100)
This module is responsible for reading the the events which will be enqued by
the various parsers and perform various actions, based on the type of the event.

The default action is to count all events for a given IP address and if the configured
"BlockCount" has been reached to call the configured "FirewallEngine" to perform a
block for this address.

The Events module also will be used, to perform any event which will be sent through the
Socket.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
modules/Events.pm [new file with mode: 0644]

diff --git a/modules/Events.pm b/modules/Events.pm
new file mode 100644 (file)
index 0000000..67bef60
--- /dev/null
@@ -0,0 +1,263 @@
+package Guardian::Events;
+use strict;
+use warnings;
+
+use Exporter qw(import);
+
+our @EXPORT_OK = qw(Init CheckAction Update);
+
+# Hash which stores all supported commands from the queue.
+my %commands = (
+       'count' => \&Counter,
+       'block' => \&CallBlock,
+       'unblock' => \&CallUnblock,
+       'flush' => \&CallFlush,
+);
+
+# Hash to store addresses and their current count.
+my %counthash = ();
+
+# Hash to store all currentyl blocked addresses and a timestamp
+# when the block for this address can be released.
+my %blockhash = ();
+
+# This object will contain the reference to the logger object after calling Init.
+my $logger;
+
+#
+## The "Init" (Block) function.
+#
+## This function is responsible to initialize Block as a class based object.
+## It has to be called once before any blocking event can be processed.
+#
+## The following arguments must be passed, during initialization:
+## "BlockCount" and "BlockTime" which both have to be natural numbers.
+#
+sub Init (%) {
+       my ( $class, %args ) = @_;
+       my $self = \%args;
+
+       # Fail, if some critical arguments are missing.
+       unless ((exists($self->{BlockCount})) && (exists($self->{BlockTime}))) {
+               die "Could not initialize Block: Too less arguments are given.\n";
+       }
+
+       # Use bless to make "$self" to an object of class "$class".
+       bless($self, $class);
+
+       # Assign logger object.
+       $logger = $self->{Logger};
+
+       # Log used firewall engine.
+       $logger->Log("debug", "Using firewall engine: $self->{FirewallEngine}");
+
+       # Try to load the specified firewall engine or die.
+       my $module_name = "Guardian::" . $self->{FirewallEngine};
+       eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
+
+       # Return the class object.
+       return $self;
+}
+
+#
+## The main "CheckAction" function.
+#
+## This function is used to handle each recived event from the main event queue of guardian.
+#
+## It will check if the given command is valid and will pass it to the responsible function.
+#
+sub CheckAction ($$) {
+       my $self = shift;
+       my @event = split(/ /, $_[0], 4);
+       my ($command, $address, $module, $message) = @event;
+
+       # Check if we got an invalid command.
+       unless(exists($commands{$command})) {
+                $logger->Log("err", "The CheckAction function has been called with an unsupported command ($command)!");
+                return;
+        }
+
+       # XXX
+       # Check if the given address is valid.
+
+       # Call required handler.
+       my $error = $commands{$command}->($self, $address, $module, $message);
+
+       # If we got any return code, something went wrong and should be logged.
+       if ($error) {
+               $logger->Log("err", "Got error: $error");
+               return;
+       }
+}
+
+#
+## The main "Counter" function.
+#
+## This function is used to handle each count message + address, which has been sent by the main event
+## loop of guardian.
+#
+## It stores the address and the current count into the counthash and increase the count each time when
+## the same address should be counted again. When the current count reaches the configured BlockCount,
+## the responsible function will be called to block the address.
+#
+sub Counter ($@) {
+       my $self = shift;
+       my ($address, $module, $message) = @_;
+
+       # Log event.
+       $logger->Log("debug", "$module reported $message for address: $address");
+
+       # Increase existing count or start counting for new source addresses.
+       if (exists($counthash{$address})) {
+               # Skip already blocked addresses.
+               if (exists($blockhash{$address})) {
+                       return undef;
+               }
+
+               # Increase count of the existing entry.
+               $counthash{$address} = $counthash{$address} + 1;
+
+               # Log altered count of the address.
+               $logger->Log("debug", "Source $address now has count $counthash{$address}/$self->{BlockCount}...");
+       } else {
+               # Log that counting for the address has been started.
+               $logger->Log("debug", "Start counting for $address...");
+
+               # Set count to "1".
+               $counthash{$address} = 1;
+       }
+
+       # Check if the address has reached the configured count limit.
+       if ($counthash{$address} >= $self->{BlockCount}) {
+               # Write out log message.
+               $logger->Log("info", "Blocking $address for $self->{BlockTime} seconds...");
+
+               # Call block subroutine to block the address.
+               my $error = &CallBlock($self, $address, $module, $message);
+
+               # Return the message if an error occurs.
+               return $error;
+       }
+
+       # Everything worked well, return nothing.
+       return undef;
+}
+
+#
+## The RemoveBlocks function.
+#
+## This function periodly will be called and is responsible for releasing the block if the Blocktime
+## on an address has expired.
+#
+## To do this, the code will loop through all entries of the blockhash and check
+## if the estimiated BlockTime of each address has reached and so the block can be released.
+#
+sub RemoveBlocks () {
+       my $self = shift;
+
+       # Get the current time.
+       my $time = time();
+
+       # Loop through the blockhash.
+       foreach my $address (keys %blockhash) {
+               # Check if the blocktime for the address has expired.
+               if ($blockhash{$address} <= $time) {
+                       # Call unblock subroutine.
+                       my $error = &CallUnblock($self, $address, "BlockTime", "has expired for $address");
+
+                       # Log error messages if returned.
+                       if ($error) {
+                               $logger->Log("err", "$error");
+                       }
+               }
+       }
+
+       # Everything okay, return nothing.
+       return undef;
+}
+
+#
+## The CallBlock function.
+#
+## This function is called, if the BlockCount for an address is reached or a direct
+## request for blocking an address has been recieved.
+#
+sub CallBlock ($@) {
+       my $self = shift;
+       my ($address, $module, $message) = @_;
+
+       # Log the call for blocking an address.
+       $logger->Log("info", "$module - $message");
+
+       # Check if an entry for this address exists
+       # in the blockhash. If not, the address has
+       # not been blocked yet, call the responisible
+       # function to do this now.
+       unless (exists($blockhash{$address})) {
+               # XXX
+               # Add posibility to use a configure-able action.
+               my $action;
+
+               # Block the given address.
+               my $error = &DoBlock($address, $action);
+
+               # If we got back an error message something went wrong.
+               if ($error) {
+                       # Exit function and return the error message.
+                       return $error;
+               } else {
+                       # Address has been successfully blocked, print a log message.
+                       $logger->Log("debug", "Address $address successfully has been blocked...");
+               }
+       }
+
+       # Generate time when the block will expire.
+       my $expire = time() + $self->{BlockTime};
+
+       # Store the blocked address and the expire time
+       # in the blockhash.
+       $blockhash{$address} = $expire;
+
+       # Return nothing "undef" if everything is okay.
+       return undef;
+}
+
+#
+## CallUnblock function.
+#
+## This function is responsible for unblocking and deleting a given
+## address from the blockhash.
+#
+sub CallUnblock ($) {
+       my $self = shift;
+       my ($address, $module, $message) = @_;
+
+       # Log the call for unblocking an address.
+       $logger->Log("info", "$module - $message");
+
+       # Return an error if no entry for the given address
+       # is present in the blockhash.
+       unless (exists($blockhash{$address})) {
+               return "Address $address was not blocked!";
+       }
+
+       # Unblock the address.
+       my $error = &DoUnblock($address);
+
+       # If an error message is returned, something went wrong.
+       if ($error) {
+               # Exit function and return the error message.
+               return $error;
+       } else {
+               # Address successfully has been unblocked.
+               $logger->Log("debug", "Address $address successfully has been unblocked...");
+       }
+
+       # Drop address from blockhash.
+       delete ($blockhash{$address});
+
+       # Everything worked well, return nothing.
+       return undef;
+}
+
+1;