I am using the following script to ban users with iptables. I keep getting an error that says the following: 06.07.2003 14:38:41: Creating chain proftpdban to store blacklisted IPs 06.07.2003 14:38:41: Adding INPUT-rule which redirects to proftpdban 06.07.2003 14:38:41: Shutting down: Insertion of new rule in INPUT chain failed: Index of insertion too big I have no idea what the error means or how to solve the problem. Any help would be appreciated. I have attached the perl script that I am using. Mark
#!/usr/bin/perl # anti-hammer v. 0.1 # Copyright (c) 2003 Michael Renner <robe@xxxxxxxxx> #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 2 of the License, or #(at your option) any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # #TODO : # #Statefile/permbans/checking of hitcounters use strict; use warnings; use IPTables::IPv4; use File::Tail; use Time::ParseDate; use POSIX; use vars qw (%ips %config); my (%config) = ( logtoparse => "/usr/local/sbin/proftpd.log", outputlog => "/usr/local/sbin/hammer.log", port => "21", chainname => "proftpdban", duration => "60", maxhits => "4", bantime => "86400", bofh => "1", logdebug => "1", logstatus => "1", logbans => "1", loginfos => "1", ); daemonize(); open_log(); init_sighandlers(); init_iptable(); parselog(); die("parselog() returned... this should not happen!\n"); ############ # SUBS # ############ sub parselog { my ($log) = File::Tail->new(name => $config{'logtoparse'}, resetafter=>120, interval => 1); my ($line); my ($ctime) = time(); while (defined($line = $log->read)) { chomp $line; if ((time() - $ctime) >= 60) { cleanup(\%ips); $ctime = time(); } my ($logtime, $ip) = parseproftpd($line); if ($logtime > 0) { if (! exists($ips{$ip})) { $ips{$ip} = []; } my ($iparef) = \@{$ips{$ip}}; if (($#$iparef < 0) || ($$iparef[0] ne "BANNED")) { storeip($iparef, $logtime, $ip); clearold($iparef, $ip); try_ban($iparef, $ip); } else { logit("debug", "%s: is already banned, probably lines from the logbuffer", $ip); } } } } sub daemonize { chdir('/') || die("Can't chdir to /: $!"); open(STDIN, '/dev/null') || die("Failed to set stdin to /dev/null: $!"); open(STDOUT, '>/dev/null') || die("Failed to set stdout to /dev/null: $!"); defined(my $pid = fork()) || die("Failed to fork: $!"); exit if $pid; setsid() || die("Failed to start a new session: $!"); open(STDERR, '>&STDOUT') || die("Failed to redirect STDERR: $!"); } sub cleanup { my ($chain, $timeout) = ($config{'chainname'}, $config{'duration'}); my ($now) = time(); my ($ip, @unban); logit("status", "!!!!!! Cleanup-Run-Start !!!!!!"); foreach $ip (keys %ips) { my ($iparef) = $ips{$ip}; if ($#$iparef < 0) { logit("status", "%-15s: Deleting: Empty.", $ip); delete($ips{$ip}); } elsif (($$iparef[0] eq "BANNED")) { if ($$iparef[1] < $now) { logit("status", "%-15s: Unbanning: %d seconds old", $ip, $now - $$iparef[1]); do_unban($ip, $chain); push @unban, sprintf("IP %s got unbanned, Ban expired %d seconds ago", $ip, $now - $$iparef[1]); delete($ips{$ip}); } else { logit("status", "%-15s: Banned: %d seconds left", $ip, $$iparef[1] - $now); } } elsif ($$iparef[-1] < ($now - $timeout)) { logit("status", "%-15s: Deleting: %d seconds old", $ip, $now - $$iparef[-1]); delete($ips{$ip}); } else { logit("status", "%-15s: Still active: %d seconds old, %d hit(s) recorded", $ip, $now - $$iparef[-1], $#$iparef + 1); } } logit("status", "!!!!!! Cleanup-Run-End !!!!!!"); logit("ban", $_) foreach (@unban); } sub do_unban { my ($ip, $chain) = @_; my ($it) = IPTables::IPv4::init('filter'); my (@banr) = $it->list_rules($chain); my ($rule); foreach $rule (@banr) { if ($rule->{'source'} eq $ip) { $it->delete_entry($chain, $rule) || shut_down("Deletion of banrule for $ip in $chain failed: $!"); } } $it->commit(); } sub storeip { my ($iparef, $ctime, $ip) = @_; if ($#$iparef >= 0) { if (${$iparef}[-1] <= $ctime) { logit("debug", "%-15s: Storing hit %d", $ip, $ctime); push(@$iparef, $ctime); } else { logit("error", "Array for %s has a newer element than %d (possible clock jump?), clearing it", $ip, $ctime); @$iparef = ($ctime); } } else { push(@$iparef, $ctime); logit("debug", "%-15s: First hit: %d", $ip, $ctime); } } sub clearold { my ($iparef, $ip) = @_; my ($timeout) = ($config{'duration'}); my ($now) = time(); while (($#$iparef >= 0) && (${$iparef}[0] < ($now - $timeout))) { my ($removed) = shift(@$iparef); logit("debug", "%-15s: Removed $removed", $ip); } logit("debug", "%-15s: %d hit(s) stored", $ip, ($#$iparef + 1)); } sub try_ban { my ($iparef, $ip) = @_; my ($treshold, $chain, $bantime) = ($config{'maxhits'}, $config{'chainname'}, $config{'bantime'}); if ($#$iparef >= ($treshold - 1)) { logit("ban", "IP %s gets banned now for %d seconds (%d hits in %d seconds)", $ip, $bantime, $treshold, $$iparef[-1] - $$iparef[0]); do_ban($ip, $chain, $bantime); $#$iparef = 1; $$iparef[0] = "BANNED"; $$iparef[1] = time() + $bantime; } } sub do_ban { my ($ip, $chain, $bantime) = @_; my ($it) = IPTables::IPv4::init('filter'); my (%newrule) = ( source => $ip, jump => 'DROP', ); $it->append_entry($chain, \%newrule) || shut_down("Insertion of ban-rule into $chain failed: $!"); $it->commit(); } sub init_iptable { my ($it) = IPTables::IPv4::init('filter'); my ($chain, $port, $bofh) = ($config{'chainname'}, $config{'port'}, $config{'bofh'}); my ($state, $rule); if ($it->is_chain($chain)) { logit("error", "Chain %s exists - flushing entries", $chain); $it->flush_entries($chain); } else { logit("info", "Creating chain %s to store blacklisted IPs", $chain); $it->create_chain($chain); } my ($cicrc) = clear_input_chain($it, $chain); shut_down($cicrc) if ($cicrc ne ""); if ($bofh) { $state = ['NEW', 'ESTABLISHED', 'RELATED'] } else { $state = ['NEW'] } my (%newrule) = ( protocol => "TCP", 'destination-port' => $port, matches => ['state'], state => $state, jump => $chain, ); logit("info", "Adding INPUT-rule which redirects to %s", $chain); $it->insert_entry('INPUT', \%newrule, 1) || shut_down("Insertion of new rule in INPUT chain failed: $!"); $it->commit(); } sub clear_input_chain { my ($it, $chain) = @_; my (@inputr) = $it->list_rules('INPUT'); my ($rule); foreach $rule (@inputr) { if ($rule->{jump} eq $chain) { logit("debug", "Deleting rule in INPUT chain which jumps to %s", $chain); $it->delete_entry('INPUT', $rule) || return("Deletion of rule in INPUT chain failed: $!"); } } return(""); } sub logit { my ($priority, $message, @strings) = @_; my ($debug, $status, $bans, $info) = ($config{'logdebug'}, $config{'logstatus'}, $config{'logbans'}, $config{'loginfos'}); my ($timestamp) = strftime("%d.%m.%Y %H:%M:%S", localtime(time())); if ((($priority eq "debug") && ($debug)) || (($priority eq "status") && ($status)) || (($priority eq "ban") && ($bans)) || (($priority eq "info") && ($info)) || ($priority eq "error")) { $message = "$timestamp: $message\n"; printf(LOG $message, @strings); } } sub open_log { my ($logfile) = ($config{'outputlog'}); open(LOG, ">>$logfile") || die("Failed to open logfile $logfile: $!\n"); select(LOG); $|=1; } sub parseproftpd { my ($line) = @_; #Datetime: $1, IP: $3 if ($line =~ /^([a-zA-Z]{3}\s+[0-9]{1,2}\s+([0-9]{2}:){2}[0-9]{2}).*\([a-zA-Z0-9\-\.]+\[(([0-9]{1,3}\.){3}[0-9]{1,3})\]\):\ FTP\ session\ opened\.$/) { my ($ctime, $ip) = ($1, $3); $ctime = parsedate($ctime); return($ctime, $ip); } } sub shut_down { my ($reason) = @_; my ($chain) = $config{'chainname'}; my ($it) = IPTables::IPv4::init('filter'); $reason = "Shutting down: $reason"; logit("error", $reason); my ($cicrc) = clear_input_chain($it, $chain); logit("error", "Shutting down: %s", $cicrc) if ($cicrc ne ""); if ($it->is_chain($chain)) { $it->flush_entries($chain) || logit("error", "Shutting down: Failed to flush entries of %s: %s", $chain, $!); $it->delete_chain($chain) || logit("error", "Shutting down: Failed to delete chain %s: %s", $chain, $!); } $it->commit(); close(LOG); exit; } sub handle_signal { my ($signame) = @_; shut_down("Signal SIG$signame received"); } sub init_sighandlers { $SIG{INT} = \&handle_signal; $SIG{ABRT} = \&handle_signal; $SIG{TERM} = \&handle_signal; }