diff --git a/src/contrib/warden-app/Modules/IPset.pm b/src/contrib/warden-app/Modules/IPset.pm
new file mode 100644
index 0000000000000000000000000000000000000000..0d065040e13cdcf29c4106f2e5a5fa4ddf0023b3
--- /dev/null
+++ b/src/contrib/warden-app/Modules/IPset.pm
@@ -0,0 +1,40 @@
+package IPset;
+use strict;
+use warnings;
+use Data::Dumper;
+
+my %CONSTANTS =    (
+                        enabled    =>  "no",
+                        outputfile =>  "tmp/ipset.txt",
+                        threshold  =>  250,
+                        excludedip =>  [],
+                        eventtype  =>  [],
+                        setname  =>  "BLOCK",
+                        maxage     =>  "1D",
+                     );
+
+my %FORMAT   =      (   maxage     => qr/\d+[hdmHDM]/, logging  => qr/enable|disable/,);
+
+sub run {
+    my (undef, $modprefix, $cfg, $dbh, $db_engine) = @_;
+   
+    my $v = Constants::mergeConfigs($cfg, $modprefix, \%CONSTANTS, \%FORMAT);
+    
+    my $eventtype_query = DB::joinIN("type", \@{$v->{'eventtype'}});
+    my $excluded_query  = DB::joinNotIN("source", \@{$v->{'excludedip'}});
+
+    my $condition = substr($excluded_query . $eventtype_query, 0, -5);
+    my @columns= ("source");
+    my @params = ($condition, DB::getOldDataDB($db_engine, "NEWER", $v->{'maxage'}));
+    my $query = DB::getQueryCondThreshold($db_engine, "events", \@columns, \@params, $v->{'threshold'});
+
+    my @rows = Utils::fetchall_array_hashref($dbh, $query);
+
+    sub header { my $v = shift; return "create $v->{'setname'}_tmp hash:ip\n"; };
+    sub record { my ($r, $v) = @_; return "add $v->{'setname'}_tmp $r->{'source'}\n" if ($r->{'source'}=~/\d+\.\d+\.\d+\.\d+/); };
+    sub footer { my ($v) = @_; return "swap $v->{'setname'}_tmp $v->{'setname'}\ndestroy $v->{'setname'}_tmp\nquit\n"; };
+
+    my $ret = Utils::generateOutput($v->{'outputfile'}, \@rows, \&header, \&record, \&footer, $v);
+    return $ret;
+}
+1;
diff --git a/src/contrib/warden-app/doc/WApp.README b/src/contrib/warden-app/doc/WApp.README
index 5b3e0b2211806a63de7e24624108807b82fb2175..6a12fbd988a9b4bf76714bb7a45ad71423c3e419 100644
--- a/src/contrib/warden-app/doc/WApp.README
+++ b/src/contrib/warden-app/doc/WApp.README
@@ -50,6 +50,7 @@ A. Overall Information
     |   |-- DNSblacklist.pm
     |   |-- IPblacklist.pm
     |   |-- IPtables.pm
+    |   |-- IPset.pm
     |   `-- MailReport.pm
     |-- sh
     |   |-- create_tables_mysql.sh
@@ -145,6 +146,7 @@ F. Modules
    DNSblacklist	- generates zone file for the most widely used DNS software on the Internet.
    IPblacklist	- generates traditional CSV file with IP addresses.
    IPtables 	- generates iptables rules.
+   IPset 	- generates ipset rules (use on big sets of addresses rather than iptables).
    MailReport	- generates reports which are sent to specific recipients.
 
    Section XX. describes how to write own module.
diff --git a/src/contrib/warden-app/etc/factory.conf b/src/contrib/warden-app/etc/factory.conf
index f0c23d02f9035f4ae3a13620362773f4b317303c..426859e997709eb27af055ebef6db8d8e7225f35 100644
--- a/src/contrib/warden-app/etc/factory.conf
+++ b/src/contrib/warden-app/etc/factory.conf
@@ -40,6 +40,24 @@ destchain="DROP"
 # Time interval for generating data [D - Days, M - Months, H - Hours]; 
 maxage="4M"
 
+[MOD_IPSET_1]
+# Enabling module [yes, no]
+enabled="yes"
+# Type of module; see 'moddir' directory
+module="IPset"
+# Where will be result stored
+outputfile="/opt/warden-app/var/ipset.txt"
+# Threshold for SQL query (events grouped by source IP) [number]
+threshold="10"
+# Which source IP we want to exclude from result [ip1, ip2, ipN]
+excludedip="1.1.1.1"
+# Which type of events we want to process [ see Warden manual ]
+eventtype=
+# Chain in which will be iptables rules inserted
+setname="BLOCK"
+# Time interval for generating data [D - Days, M - Months, H - Hours]; 
+maxage="4M"
+
 [MOD_DNSBL_1]
 # Enabling module [yes, no]
 enabled="yes"