Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 317 lines (205 sloc) 5.7 KB
#!/usr/local/bin/perl -w
use strict;
=head1 PROGRAM
C<blip.pl> - Blocks and unblocks IPs using C<iptables>.
=head2 Usage Examples
Using the Unix iptables can be confusing, blip is a simple interface
that un/blocks IPs and put them in a designated chain.
./blip.pl -block 114.80.97.88
./blip.pl -unblock 114.80.97.88
You can pass to it multiple IPs by separating them with commas.
List the blocked IPs:
./blip.pl -list
When you're tired of it all:
./blip.pl -wipe
=head2 Options
=head3 -verbose <level>
Turns on vebosity.
=head3 -bin <path-to-iptables>
Defaults to C</sbin/iptables> if it's not there, set it yourself.
=head3 -chain <chain>
Chain or target to add IPs to. Defaults to C<BLIP>.
=cut
use vars qw/$BLIP/;
use constant HELP => qq{blip.pl v1.1 - Block IP
./blip.pl -[un]block <ip-address> -verbose
./blip.pl -[wipe|list]
};
die HELP unless scalar @ARGV;
$BLIP = BlockIP->new(@ARGV);
$BLIP->commands;
exit (0);
package BlockIP;
use constant {
IPTABLES => '/sbin/iptables',
CHAIN => 'BLIP'
};
=head1 NAME
C<BlockIP.pm> - Simple interface for C<iptables>.
=head1 SYNOPSIS
$BLIP = BlockIP->new(@ARGV);
$BLIP->commands;
=head1 DESCRIPTION
Blocks and unblocks IPs to a special chain called C<BLIP>.
=head2 Public Methods
=head3 new
Creates the object, subsequently, creates the C<CHAIN> if doesn't exist.
=cut
sub new {
my $class = shift;
my $self = {};
for (my $i=0;$i<=$#_;$i++) {
if (substr($_[$i],0,1) eq '-') {
$self->{$_[$i]} = 1;
} else {
$self->{$_[$i-1]} = $_[$i];
}
}
bless $self, $class;
$self->_init;
return $self;
}
=head3 commands (I<void>)
Accepts these command-line args:
-verbose = 2 levels of outputs
-list = shows current table
-wipe = remove chain
-block = block this IP
-unblock = unblock this IP
=cut
sub commands {
my $self = shift;
$self->{-data} or die "ABORT: Cannot find chain BLIP in iptables\n";
if ($self->{-list}) {
printf "Blocked IPs:\n%s\n", join "\n", $self->_get_ips;
} elsif ($self->{-unblock}) {
my @ips = split ',', $self->{-unblock};
map { $self->unblock($_) } @ips;
} elsif ($self->{-block}) {
my @ips = split ',', $self->{-block};
map { $self->block($_) } @ips;
} elsif ($self->{-wipe}) {
my $cmd = sprintf "%s -D INPUT -j %s",
$self->{-bin}, $self->{-chain};
$self->_execute($cmd) and
die "ERROR: unchain $cmd\n";
$cmd = sprintf "%s -X %s",
$self->{-bin}, $self->{-chain};
$self->_printf(1,"Removing chain %s\n", $self->{-chain});
map { $self->unblock($_) } $self->_get_ips;
$self->_execute($cmd) and
die "ERROR: Cannot wipe $cmd\n";
}
}
=head3 block (I<ip>)
Blocks an IP if not already blacklisted.
=cut
sub block {
my ($self,$ip) = (@_);
if ($self->{-data} =~ /$self->{-block}/) {
return print "ERROR: $ip already blocked\n";
}
my $cmd = sprintf "%s -I %s -s %s -j DROP",
$self->{-bin}, $self->{-chain}, $ip;
$self->_printf(1,"Blocking %s\n", $ip);
$self->_execute($cmd) and
die "ERROR: Cannot block $ip\n";
}
=head3 unblock (I<ip>)
Unblocks an IP in the C<chain>.
=cut
sub unblock {
my ($self,$ip) = (@_);
my $cmd = sprintf "%s -D %s -s %s -j DROP",
$self->{-bin}, $self->{-chain}, $ip;
$self->_printf(1,"Unblocking %s\n", $ip);
$self->_execute($cmd) and
die "ERROR: Cannot unblock $ip\n";
}
=head2 Private Methods
=head3 _init (I<void>)
Prepares C<iptables> to accept un/block of IPs.
=cut
sub _init {
my $self = shift;
$self->{-bin} ||= IPTABLES;
$self->{-chain} ||= CHAIN;
$self->{-verbose} ||= 0;
my $cmd = sprintf "%s -L %s -n", $self->{-bin}, $self->{-chain};
select STDOUT; $| = 1;
local $/ = undef;
my $info = $self->_execute($cmd);
$info =~ /denied/i
and die "ABORT: Need to be root to do this\n";
if ($info =~ /no chain/i) {
$self->_create_chain;
$info = $self->_execute($cmd);
}
if ($info =~ /^target/m) {
$self->{-data} = $info;
}
}
sub _get_ips {
my $self = shift;
return () unless $self->{-data};
my @list = ();
while ($self->{-data} =~ m/--\s+(\d+\.\d+\.\d+\.\d+)/g) {
push @list, $1;
}
return @list;
}
=head3 _create_chain (I<void>)
Creates a new C<chain> and chain this to C<INPUT>.
=cut
sub _create_chain {
my $self = shift;
my $cmd = sprintf "%s -N %s", $self->{-bin}, $self->{-chain};
$self->_printf(1, "$cmd\n");
my $info = $self->_execute($cmd);
$info and die "ABORT: $cmd failed ($info)\n";
$cmd = sprintf "%s -A INPUT -j %s",
$self->{-bin}, $self->{-chain};
$info = $self->_execute($cmd);
$info and die "ABORT: $cmd failed ($info)\n";
}
=head3 _execute (I<command>,[I<reg-pattern>])
Execute command, if passed a regexp, it'll try to match it with the output
returns true/false otherwise, return entire output for parsing.
=cut
sub _execute {
my ($self,$cmd,$re) = (@_);
$self->_printf(2, "CMD: $cmd\n");
open CMD, "$cmd 2>&1 |" or die "ABORT: Cannot execute $cmd\n";
my $info = <CMD>;
close CMD;
$self->_printf(2, "%sOUTPUT%s\n%s\n%s\n\n",
'-'x20,'-'x50,
$info||'<empty>',
'-'x76);
$re and return $info =~ $re;
return $info;
}
=head3 _printf (I<level>,I<format>,I<@array>)
Works just like C<printf> except has a built-in check for C<-verbose>.
=cut
sub _printf {
my $self = shift;
my $level = shift;
return 0 if $self->{-verbose} < $level;
return printf @_;
}
=head1 HISTORY
20120314 - v1.0 - Created.
v1.0.1 - Remove C<README>.
Link C<README.pod> to C<blip.pl>.
Change C<blip> to C<blip.pl>.
Adds C<-chain> option so user chan specify which C<chain/target> to use.
v1.1 - Add chaining to C<INPUT> for this thing to actually do the blocking. Oops.
=head1 AUTHOR
This module by Paul Pham.
=head1 COPYRIGHT AND LICENSE
Copyright 2012 by Paul Pham
This program and library is free software;
you can redistribute it and/or modify it under the same terms as Perl itself.
=cut
1;
Something went wrong with that request. Please try again.