Listing 2 use strict; use File::Tail; use Crypt::CBC; use Schedule::At; use Math::VecStat qw(sum); use POSIX qw(strftime); use Getopt::Std; use constant PORTMIN => 745; use constant KNOCKLENGTH => 8; use constant KEY => "knock"; use constant CIPHER => "Blowfish"; use vars qw($opt_f); getopts("f:"); my $file = File::Tail->new(name=>"$opt_f",maxinterval=>2); my %QUEUE; while(defined(my $line=$file->read)) { # pay attention only to DENY packets next unless $line =~ /DENY/; if($line =~ /PROTO=6 ([\d.]+):\d+ [\d.]+:(\d+)/) { my ($ip,$port) = ($1,$2); # pay attention only to ports allocated for knocking next if ($port < PORTMIN || $port > PORTMIN+255); print "knock from $ip to port $port\n"; # push this port to each queue item $QUEUE{$ip} = [] if ! $QUEUE{$ip}; push(@{$QUEUE{$ip}},$port); print "current queue ",join(" ",@{$QUEUE{$ip}}),"\n"; # if the queue is of the expected length, process it if(@{$QUEUE{$ip}} == KNOCKLENGTH) { ProcessQueue($QUEUE{$ip}); print "current queue @{$QUEUE{$ip}}\n"; } } } sub ProcessQueue { my $queue = shift; # try to decrypt the queue contents my $cipher = Crypt::CBC->new({key => KEY, iv =>"01234567", prepend_iv => 0, cipher => CIPHER}); my @data = unpack("C*", $cipher->decrypt(pack("C*", map {$_ - PORTMIN } @$queue))); # decrypted list must have 7 elements, otherwise remove oldest item # and keep listening if(@data != 7) { print "ERROR could not decrypt properly\n"; shift(@$queue); return; } print "Got data ",join(" ",@data),"\n"; # check crc of knock sequence if ((sum(@data[0..@data-2]) % 255) == $data[-1]) { # extract information from the decrypted list my ($remoteip,$localport,$time) = (join(".",@data[0..3]),$data[4],$data[5]); print "$remoteip $localport $time\n"; # open port to remote ip system("/sbin/ipchains -I input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT") if $time != 255; # close the port if time = 255 if($time == 255) { system("/sbin/ipchains -D input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT "); } elsif ($time) { # schedule the port to be closed in $time minutes my $time = strftime "%Y%m%d%H%M", localtime(time+60*$time); my $command = "/sbin/ipchains -D input -p tcp -s $remoteip/32 -d 0/0 $localport -j ACCEPT"; print "scheduled $time $command\n"; Schedule::At::add(TIME=>$time,COMMAND=>$command); } # clear the queue @$queue = (); } else { print "ERROR bad crc\n"; shift(@$queue); } } ### end of Listing 2