#
# Author: Michael Rash (mbr@cipherdyne.org)
#
-# Version: 0.9
+# Version: 1.2
#
##############################################################################
#
use warnings;
use vars qw($VERSION);
-$VERSION = '0.9';
+$VERSION = '1.2';
sub new() {
my $class = shift;
my %args = @_;
my $self = {
- _iptables => $args{'iptables'} || '/sbin/iptables',
+ _iptables => $args{'iptables'} || $args{'ip6tables'} || '/sbin/iptables',
_iptout => $args{'iptout'} || '/tmp/ipt.out',
_ipterr => $args{'ipterr'} || '/tmp/ipt.err',
_ipt_alarm => $args{'ipt_alarm'} || 30,
_ipt_exec_sleep => $args{'ipt_exec_sleep'} || 0,
_sigchld_handler => $args{'sigchld_handler'} || \&REAPER,
};
- croak "[*] $self->{'_iptables'} incorrect iptables path.\n"
+ $self->{'_ipt_bin_name'} = 'iptables';
+ $self->{'_ipt_bin_name'} = $1 if $self->{'_iptables'} =~ m|.*/(\S+)|;
+
+ croak "[*] $self->{'_iptables'} incorrect $self->{'_ipt_bin_name'} path.\n"
unless -e $self->{'_iptables'};
croak "[*] $self->{'_iptables'} not executable.\n"
unless -x $self->{'_iptables'};
+
bless $self, $class;
}
sub chain_exists() {
my $self = shift;
my $table = shift || croak '[*] Must specify a table, e.g. "filter".';
- my $chain = shift || croak '[*] Must specify a chain to create.';
+ my $chain = shift || croak '[*] Must specify a chain to check.';
my $iptables = $self->{'_iptables'};
### see if the chain exists
### could not flush the chain
return 0, $out_aref, $err_aref unless $rv;
+ my $ip_any_net = '0.0.0.0/0';
+ $ip_any_net = '::/0' if $self->{'_ipt_bin_name'} eq 'ip6tables';
+
### find and delete jump rules to this chain (we can't delete
### the chain until there are no references to it)
my ($rulenum, $num_chain_rules)
- = $self->find_ip_rule('0.0.0.0/0',
- '0.0.0.0/0', $table, $jump_from_chain, $del_chain, {});
+ = $self->find_ip_rule($ip_any_net, $ip_any_net,
+ $table, $jump_from_chain, $del_chain, {});
if ($rulenum) {
$self->run_ipt_cmd(
return $self->run_ipt_cmd("$iptables -t $table -X $del_chain");
}
+sub set_chain_policy() {
+ my $self = shift;
+ my $table = shift || croak '[*] Must specify a table, e.g. "filter".';
+ my $chain = shift || croak '[*] Must specify a chain.';
+ my $target = shift || croak qq|[-] Must specify an | .
+ qq|$self->{'_ipt_bin_name'} target, e.g. "DROP"|;
+ my $iptables = $self->{'_iptables'};
+
+ ### set the chain policy: note that $chain must be a built-in chain
+ return $self->run_ipt_cmd("$iptables -t $table -P $chain $target");
+}
+
sub append_ip_rule() {
my $self = shift;
my $src = shift || croak '[-] Must specify a src address/network.';
my $dst = shift || croak '[-] Must specify a dst address/network.';
my $table = shift || croak '[-] Must specify a table, e.g. "filter".';
my $chain = shift || croak '[-] Must specify a chain.';
- my $target = shift ||
- croak '[-] Must specify a iptables target, e.g. "DROP"';
+ my $target = shift || croak qq|[-] Must specify an | .
+ qq|$self->{'_ipt_bin_name'} target, e.g. "DROP"|;
### optionally add port numbers and protocols, etc.
my $extended_href = shift || {};
if ($rule_position) {
my $msg = '';
- if ($extended_href) {
+ if (keys %$extended_href) {
$msg = "Table: $table, chain: $chain, $normalized_src -> " .
"$normalized_dst ";
- for my $key qw(protocol s_port d_port mac_source) {
+ for my $key (qw(protocol s_port d_port mac_source)) {
$msg .= "$key $extended_href->{$key} "
if defined $extended_href->{$key};
}
my $msg = '';
my $idx_err = '';
- if ($extended_href) {
+ if (keys %$extended_href) {
$ipt_cmd = "$iptables -t $table -A $chain ";
$ipt_cmd .= "-p $extended_href->{'protocol'} "
if defined $extended_href->{'protocol'};
$msg = "Table: $table, chain: $chain, added $normalized_src " .
"-> $normalized_dst ";
- for my $key qw(protocol s_port d_port mac_source) {
+ for my $key (qw(protocol s_port d_port mac_source)) {
$msg .= "$key $extended_href->{$key} "
if defined $extended_href->{$key};
}
my $table = shift || croak '[-] Must specify a table, e.g. "filter".';
my $chain = shift || croak '[-] Must specify a chain.';
my $target = shift ||
- croak '[-] Must specify a iptables target, e.g. "DROP"';
+ croak qq|[-] Must specify an $self->{'_ipt_bin_name'} | .
+ qq|target, e.g. "DROP"|;
### optionally add port numbers and protocols, etc.
my $extended_href = shift || {};
my $iptables = $self->{'_iptables'};
if ($rule_position) {
my $msg = '';
- if ($extended_href) {
+ if (keys %$extended_href) {
$msg = "Table: $table, chain: $chain, $normalized_src -> " .
"$normalized_dst ";
- for my $key qw(protocol s_port d_port mac_source) {
+ for my $key (qw(protocol s_port d_port mac_source)) {
$msg .= "$key $extended_href->{$key} "
if defined $extended_href->{$key};
}
}
$rulenum = 1 if $rulenum == 0;
- if ($extended_href) {
+ if (keys %$extended_href) {
$ipt_cmd = "$iptables -t $table -I $chain $rulenum ";
$ipt_cmd .= "-p $extended_href->{'protocol'} "
if defined $extended_href->{'protocol'};
if defined $extended_href->{'d_port'};
$ipt_cmd .= "-m mac --mac-source $extended_href->{'mac_source'} "
if defined $extended_href->{'mac_source'};
+ $ipt_cmd .= "-m state --state $extended_href->{'state'} "
+ if defined $extended_href->{'state'};
+ $ipt_cmd .= "-m conntrack --ctstate $extended_href->{'ctstate'} "
+ if defined $extended_href->{'ctstate'};
$ipt_cmd .= "-j $target";
$msg = "Table: $table, chain: $chain, added $normalized_src " .
"-> $normalized_dst ";
- for my $key qw(protocol s_port d_port mac_source) {
+ for my $key (qw(protocol s_port d_port mac_source)) {
$msg .= "$key $extended_href->{$key} "
if defined $extended_href->{$key};
}
my $dst = shift || croak '[-] Must specify a dst address/network.';
my $table = shift || croak '[-] Must specify a table, e.g. "filter".';
my $chain = shift || croak '[-] Must specify a chain.';
- my $target = shift ||
- croak '[-] Must specify a iptables target, e.g. "DROP"';
+ my $target = shift || croak qq|[-] Must specify an | .
+ qq|$self->{'_ipt_bin_name'} target, e.g. "DROP"|;
### optionally add port numbers and protocols, etc.
my $extended_href = shift || {};
my $iptables = $self->{'_iptables'};
}
my $extended_msg = '';
- if ($extended_href) {
- for my $key qw(protocol s_port d_port mac_source) {
+ if (keys %$extended_href) {
+ for my $key (qw(protocol s_port d_port mac_source)) {
$extended_msg .= "$key: $extended_href->{$key} "
if defined $extended_href->{$key};
}
my $verbose = $self->{'_verbose'};
my $src = shift || croak '[*] Must specify source address.';
my $dst = shift || croak '[*] Must specify destination address.';
- my $table = shift || croak '[*] Must specify iptables table.';
- my $chain = shift || croak '[*] Must specify iptables chain.';
- my $target = shift ||
- croak '[*] Must specify iptables target (this may be a chain).';
+ my $table = shift || croak qq|[*] Must specify $self->{'_ipt_bin_name'} table.|;
+ my $chain = shift || croak qq|[*] Must specify $self->{'_ipt_bin_name'} chain.|;
+ my $target = shift || croak qq|[*] Must specify | .
+ qq|$self->{'_ipt_bin_name'} target (this may be a chain).|;
+
### optionally add port numbers and protocols, etc.
my $extended_href = shift || {};
my $iptables = $self->{'_iptables'};
my $chain_aref = $ipt_parse->chain_rules($table, $chain);
+ $src = $self->normalize_net($src) if defined $extended_href->{'normalize'}
+ and $extended_href->{'normalize'};
+ $dst = $self->normalize_net($dst) if defined $extended_href->{'normalize'}
+ and $extended_href->{'normalize'};
+
my $rulenum = 1;
for my $rule_href (@$chain_aref) {
if ($rule_href->{'target'} eq $target
and $rule_href->{'src'} eq $src
and $rule_href->{'dst'} eq $dst) {
- if ($extended_href) {
+ if (keys %$extended_href) {
my $found = 1;
- for my $key qw(
+ for my $key (qw(
protocol
s_port
d_port
to_ip
to_port
- ) {
+ state
+ ctstate
+ )) {
if (defined $extended_href->{$key}) {
- unless ($extended_href->{$key}
- eq $rule_href->{$key}) {
- $found = 0
+ if (defined $rule_href->{$key}) {
+ if ($key eq 'state' or $key eq 'ctstate') {
+ ### make sure that state ordering as reported
+ ### by iptables is accounted for vs. what was
+ ### supplied to the module
+ unless (&state_compare($extended_href->{$key},
+ $rule_href->{$key})) {
+ $found = 0;
+ last;
+ }
+ } else {
+ unless ($extended_href->{$key}
+ eq $rule_href->{$key}) {
+ $found = 0;
+ last;
+ }
+ }
+ } else {
+ $found = 0;
+ last;
}
}
}
return 0, $#$chain_aref+1;
}
+sub state_compare() {
+ my ($state_str1, $state_str2) = @_;
+
+ my @states1 = split /,/, $state_str1;
+ my @states2 = split /,/, $state_str2;
+
+ for my $state1 (@states1) {
+ my $found = 0;
+ for my $state2 (@states2) {
+ if ($state1 eq $state2) {
+ $found = 1;
+ last;
+ }
+ }
+ return 0 unless $found;
+ }
+
+ for my $state2 (@states2) {
+ my $found = 0;
+ for my $state1 (@states1) {
+ if ($state2 eq $state1) {
+ $found = 1;
+ last;
+ }
+ }
+ return 0 unless $found;
+ }
+
+ return 1;
+}
+
sub normalize_net() {
my $self = shift;
my $net = shift || croak '[*] Must specify net.';
- ### regex to match an IP address
- my $ip_re = '(?:\d{1,3}\.){3}\d{1,3}';
+ my $normalized_net = $net; ### establish default
- my $normalized_net = '';
- if ($net =~ m|$ip_re/$ip_re| or $net =~ m|$ip_re/\d+|) {
- my $n = new NetAddr::IP $net
- or croak "[*] Could not acquire NetAddr::IP object for $net";
- $normalized_net = $n->network()->cidr();
- } else {
- ### it is a hostname or an individual IP
- $normalized_net = $net;
+ ### regex to match an IPv4 address
+ my $ipv4_re = qr/(?:\d{1,3}\.){3}\d{1,3}/;
+
+ if ($net =~ m|/| and $net =~ $ipv4_re or $net =~ m|:|) {
+ if ($net =~ m|:|) { ### an IPv6 address
+ my $n = new6 NetAddr::IP $net
+ or croak "[*] Could not acquire NetAddr::IP object for $net";
+ $normalized_net = lc($n->network()->short()) . '/' . $n->masklen();
+ } else {
+ my $n = new NetAddr::IP $net
+ or croak "[*] Could not acquire NetAddr::IP object for $net";
+ $normalized_net = $n->network()->cidr();
+ }
}
return $normalized_net;
}
"not allowed."], [];
}
+ my $ip_any_net = '0.0.0.0/0';
+ $ip_any_net = '::/0' if $self->{'_ipt_bin_name'} eq 'ip6tables';
+
### first check to see if the jump rule already exists
my ($rule_position, $num_chain_rules)
- = $self->find_ip_rule('0.0.0.0/0', '0.0.0.0/0', $table,
+ = $self->find_ip_rule($ip_any_net, $ip_any_net, $table,
$from_chain, $to_chain, {});
### check to see if the insertion index ($rulenum) is too big
sub run_ipt_cmd() {
my $self = shift;
- my $cmd = shift || croak '[*] Must specify an iptables command to run.';
+ my $cmd = shift || croak qq|[*] Must specify an | .
+ qq|$self->{'_ipt_bin_name'} command to run.|;
my $iptables = $self->{'_iptables'};
my $iptout = $self->{'_iptout'};
my $ipterr = $self->{'_ipterr'};
my $ipt_exec_sleep = $self->{'_ipt_exec_sleep'};
my $sigchld_handler = $self->{'_sigchld_handler'};
- croak "[*] $cmd does not look like an iptables command."
- unless $cmd =~ m|^\s*iptables| or $cmd =~ m|^\S+/iptables|;
+
+ croak "[*] $cmd does not look like an $self->{'_ipt_bin_name'} command."
+ unless $cmd =~ m|^\s*iptables| or $cmd =~ m|^\S+/iptables|
+ or $cmd =~ m|^\s*ip6tables| or $cmd =~ m|^\S+/ip6tables|;
my $rv = 1;
my @stdout = ();
if ($debug or $verbose) {
print $fh localtime() . " [+] IPTables::ChainMgr: ",
"sleeping for $ipt_exec_sleep seconds before ",
- "executing iptables command.\n";
+ "executing $self->{'_ipt_bin_name'} command.\n";
}
sleep $ipt_exec_sleep;
}
### iptables should never take longer than 30 seconds to execute,
### unless there is some absolutely enormous policy or the kernel
### is exceedingly busy
- local $SIG{'ALRM'} = sub {die "[*] iptables command timeout.\n"};
+ local $SIG{'ALRM'} = sub {die "[*] $self->{'_ipt_bin_name'} " .
+ "command timeout.\n"};
alarm $ipt_alarm;
waitpid($ipt_pid, 0);
alarm 0;
kill 9, $ipt_pid unless kill 15, $ipt_pid;
}
} else {
- croak "[*] Could not fork iptables: $!"
+ croak "[*] Could not fork $self->{'_ipt_bin_name'}: $!"
unless defined $ipt_pid;
### exec the iptables command and preserve stdout and stderr
}
if ($debug or $verbose) {
- print $fh localtime() . " iptables command stdout:\n";
+ print $fh localtime() . " $self->{'_ipt_bin_name'} " .
+ "command stdout:\n";
for my $line (@stdout) {
if ($line =~ /\n$/) {
print $fh $line;
print $fh $line, "\n";
}
}
- print $fh localtime() . " iptables command stderr:\n";
+ print $fh localtime() . " $self->{'_ipt_bin_name'} " .
+ "command stderr:\n";
for my $line (@stderr) {
if ($line =~ /\n$/) {
print $fh $line;
=head1 NAME
-IPTables::ChainMgr - Perl extension for manipulating iptables policies
+IPTables::ChainMgr - Perl extension for manipulating iptables and ip6tables policies
=head1 SYNOPSIS
use IPTables::ChainMgr;
+ my $ipt_bin = '/sbin/iptables'; # can set this to /sbin/ip6tables
+
my %opts = (
- 'iptables' => '/sbin/iptables',
+ 'iptables' => $ipt_bin, # can specify 'ip6tables' hash key instead
'iptout' => '/tmp/iptables.out',
'ipterr' => '/tmp/iptables.err',
'debug' => 0,
- 'verbose' => 0
+ 'verbose' => 0,
### advanced options
'ipt_alarm' => 5, ### max seconds to wait for iptables execution.
$ipt_obj->delete_chain('filter', 'INPUT', 'CUSTOM');
}
+ # set the policy on the FORWARD table to DROP
+ $ipt_obj->set_chain_policy('filter', 'FORWARD', 'DROP');
+
# create new iptables chain in the 'filter' table
$ipt_obj->create_chain('filter', 'CUSTOM');
+ # translate a network into the same representation that iptables or
+ # ip6tables uses (e.g. '10.1.2.3/24' is properly represented as '10.1.2.0/24',
+ # and '0000:0000:00AA:0000:0000:AA00:0000:0001/64' = '0:0:aa::/64')
+ $normalized_net = $ipt_obj->normalize_net('10.1.2.3/24');
+
# add rule to jump packets from the INPUT chain into CUSTOM at the
# 4th rule position
$ipt_obj->add_jump_rule('filter', 'INPUT', 4, 'CUSTOM');
- # find rule that allows all traffic from 10.1.2.3 to 192.168.1.2
- ($rv, $rule_num) = $ipt_obj->find_ip_rule('10.1.2.3', '192.168.1.2',
- 'filter', 'INPUT', 'ACCEPT', {});
+ # find rule that allows all traffic from 10.1.2.0/24 to 192.168.1.2
+ ($rv, $rule_num) = $ipt_obj->find_ip_rule('10.1.2.0/24', '192.168.1.2',
+ 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1});
- # find rule that allows all TCP port 80 traffic from 10.1.2.3 to
+ # find rule that allows all TCP port 80 traffic from 10.1.2.0/24 to
# 192.168.1.1
- ($rv, $rule_num) = $ipt_obj->find_ip_rule('10.1.2.3', '192.168.1.2',
- 'filter', 'INPUT', 'ACCEPT', {'protocol' => 'tcp', 's_port' => 0,
- 'd_port' => 80});
+ ($rv, $rule_num) = $ipt_obj->find_ip_rule('10.1.2.0/24', '192.168.1.2',
+ 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1, 'protocol' => 'tcp',
+ 's_port' => 0, 'd_port' => 80});
# add rule at the 5th rule position to allow all traffic from
- # 10.1.2.3 to 192.168.1.2 via the INPUT chain in the filter table
- ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('10.1.2.3',
+ # 10.1.2.0/24 to 192.168.1.2 via the INPUT chain in the filter table
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('10.1.2.0/24',
'192.168.1.2', 5, 'filter', 'INPUT', 'ACCEPT', {});
# add rule at the 4th rule position to allow all traffic from
- # 10.1.2.3 to 192.168.1.2 over TCP port 80 via the CUSTOM chain
+ # 10.1.2.0/24 to 192.168.1.2 over TCP port 80 via the CUSTOM chain
# in the filter table
- ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('10.1.2.3',
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('10.1.2.0/24',
'192.168.1.2', 4, 'filter', 'CUSTOM', 'ACCEPT',
{'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
# append rule at the end of the CUSTOM chain in the filter table to
- # allow all traffic from 10.1.2.3 to 192.168.1.2 via port 80
- ($rv, $out_ar, $errs_ar) = $ipt_obj->append_ip_rule('10.1.2.3',
+ # allow all traffic from 10.1.2.0/24 to 192.168.1.2 via port 80
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->append_ip_rule('10.1.2.0/24',
'192.168.1.2', 'filter', 'CUSTOM', 'ACCEPT',
{'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
+ # for each of the examples above, here are ip6tables analogs
+ # (requires instantiating the IPTables::ChainMgr object with
+ # /sbin/ip6tables): find rule that allows all traffic from fe80::200:f8ff:fe21:67cf
+ # to 0:0:aa::/64
+ ($rv, $rule_num) = $ipt_obj->find_ip_rule('fe80::200:f8ff:fe21:67cf', '0:0:aa::/64',
+ 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1});
+
+ # find rule that allows all TCP port 80 traffic from fe80::200:f8ff:fe21:67c to 0:0:aa::/64
+ ($rv, $rule_num) = $ipt_obj->find_ip_rule('fe80::200:f8ff:fe21:67cf', '0:0:aa::/64',
+ 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1, 'protocol' => 'tcp',
+ 's_port' => 0, 'd_port' => 80});
+
+ # add rule at the 5th rule position to allow all traffic from
+ # fe80::200:f8ff:fe21:67c to 0:0:aa::/64 via the INPUT chain in the filter table
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('fe80::200:f8ff:fe21:67cf',
+ '0:0:aa::/64', 5, 'filter', 'INPUT', 'ACCEPT', {});
+
+ # add rule at the 4th rule position to allow all traffic from
+ # fe80::200:f8ff:fe21:67c to 0:0:aa::/64 over TCP port 80 via the CUSTOM chain
+ # in the filter table
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->add_ip_rule('fe80::200:f8ff:fe21:67cf',
+ '0:0:aa::/64', 4, 'filter', 'CUSTOM', 'ACCEPT',
+ {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
+
+ # append rule at the end of the CUSTOM chain in the filter table to
+ # allow all traffic from fe80::200:f8ff:fe21:67c to 0:0:aa::/64 via port 80
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->append_ip_rule('fe80::200:f8ff:fe21:67cf',
+ '0:0:aa::/64', 'filter', 'CUSTOM', 'ACCEPT',
+ {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
+
# run an arbitrary iptables command and collect the output
($rv, $out_ar, $errs_ar) = $ipt_obj->run_ipt_cmd(
'/sbin/iptables -v -n -L');
=head1 DESCRIPTION
-The C<IPTables::ChainMgr> package provide an interface to manipulate iptables
-policies on Linux systems through the direct execution of iptables commands.
-Although making a perl extension of libiptc provided by the iptables project is
-possible (and has been done by the IPTables::libiptc module available from CPAN),
-it is also easy enough to just execute iptables commands directly in order to
-both parse and change the configuration of the policy. Further, this simplifies
-installation since the only external requirement is (in the spirit of scripting)
-to be able to point IPTables::ChainMgr at an installed iptables binary instead
+The C<IPTables::ChainMgr> package provides an interface to manipulate iptables
+and ip6tables policies on Linux systems through the direct execution of
+iptables/ip6tables commands. Although making a perl extension of libiptc
+provided by the Netfilter project is possible (and has been done by the
+IPTables::libiptc module available from CPAN), it is also easy enough to just
+execute iptables/ip6tables commands directly in order to both parse and change
+the configuration of the policy. Further, this simplifies installation since
+the only external requirement is (in the spirit of scripting) to be able to
+point IPTables::ChainMgr at an installed iptables or ip6tables binary instead
of having to compile against a library.
=head1 FUNCTIONS
This function tests whether or not a chain (e.g. 'INPUT') exists within the
specified table (e.g. 'filter'). This is most useful to test whether
-a custom chain has been added to the running iptables policy. The return values
-are (as with many IPTables::ChainMgr functions) an array of three things: a
-numeric value, and both the stdout and stderr of the iptables command in the
-form of array references. So, an example invocation of the chain_exists()
-function would be:
+a custom chain has been added to the running iptables/ip6tables policy. The
+return values are (as with many IPTables::ChainMgr functions) an array of
+three things: a numeric value, and both the stdout and stderr of the iptables
+or ip6tables command in the form of array references. So, an example
+invocation of the chain_exists() function would be:
($rv, $out_ar, $errs_ar) = $ipt_obj->chain_exists('filter', 'CUSTOM');
If $rv is 1, then the CUSTOM chain exists in the filter table, and 0 otherwise.
The $out_ar array reference contains the output of the command "/sbin/iptables -t filter -v -n -L CUSTOM",
which will contain the rules in the CUSTOM chain (if it exists) or nothing (if not).
-The $errs_ar array reference contains the stderr of the iptables command.
+The $errs_ar array reference contains the stderr of the iptables command. As
+with all IPTables::ChainMgr functions, if the IPTables::ChainMgr object was
+instantiated with the ip6tables binary path, then the above command would
+become "/sbin/ip6tables -t filter -v -n -L CUSTOM".
=item create_chain($table, $chain)
($rv, $out_ar, $errs_ar) = $ipt_obj->create_chain('filter', 'CUSTOM');
Behind the scenes, the create_chain() function in the example above runs the
-iptables command "/sbin/iptables -t filter -N CUSTOM".
+iptables command "/sbin/iptables -t filter -N CUSTOM", or for ip6tables
+"/sbin/ip6tables -t filter -N CUSTOM".
=item flush_chain($table, $chain)
($rv, $out_ar, $errs_ar) = $ipt_obj->flush_chain('filter', 'CUSTOM');
-The flush_chain() function in the example above executes the iptables command
-"/sbin/iptables -t filter -F CUSTOM"
+The flush_chain() function in the example above executes the command
+"/sbin/iptables -t filter -F CUSTOM" or "/sbin/ip6tables -t filter -F CUSTOM".
+
+=item set_chain_policy($table, $chain, $target)
+
+This function sets the policy of a built-in chain (iptables/ip6tables does not allow
+this for non built-in chains) to the specified target:
+
+ ($rv, $out_ar, $errs_ar) = $ipt_obj->set_chain_policy('filter', 'FORWARD', 'DROP');
+
+In this example, the following command is executed behind the scenes:
+"/sbin/iptables -t filter -P FORWARD DROP" or "/sbin/ip6tables -t filter -P FORWARD DROP".
=item delete_chain($table, $jump_from_chain, $chain)
matches the $src, $dst, $target, and (optionally) any %extended_info
criteria. The return values are the rule number in the chain (or zero
if it doesn't exist), and the total number of rules in the chain. Below
-are two examples; the first is to find an ACCEPT rule for 10.1.2.3 to
+are four examples; the first is to find an ACCEPT rule for 10.1.2.0/24 to
communicate with 192.168.1.2 in the INPUT chain, and the second is the
-same except that the rule is restricted to TCP port 80:
+same except that the rule is restricted to TCP port 80. The third and
+forth examples illustrate ip6tables analogs of the first two examples
+with source IP fe80::200:f8ff:fe21:67cf/128 and destination network: 0:0:aa::/64
- ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('10.1.2.3',
- '192.168.1.2', 'filter', 'INPUT', 'ACCEPT', {});
+ ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('10.1.2.0/24',
+ '192.168.1.2', 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1});
if ($rulenum) {
print "matched rule $rulenum out of $chain_rules rules\n";
}
- ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('10.1.2.3',
+ ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('10.1.2.0/24',
'192.168.1.2', 'filter', 'INPUT', 'ACCEPT',
- {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
+ {'normalize' => 1, 'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
+ if ($rulenum) {
+ print "matched rule $rulenum out of $chain_rules rules\n";
+ }
+
+ ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('fe80::200:f8ff:fe21:67cf/128',
+ '0:0:aa::/64', 'filter', 'INPUT', 'ACCEPT', {'normalize' => 1});
+ if ($rulenum) {
+ print "matched rule $rulenum out of $chain_rules rules\n";
+ }
+
+ ($rulenum, $chain_rules) = $ipt_obj->find_ip_rule('fe80::200:f8ff:fe21:67cf/128',
+ '0:0:aa::/64', 'filter', 'INPUT', 'ACCEPT',
+ {'normalize' => 1, 'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
if ($rulenum) {
print "matched rule $rulenum out of $chain_rules rules\n";
}
($rv, $out_ar, $errs_ar) = $ipt_obj->add_jump_rule('filter', 'INPUT', 4, 'CUSTOM');
+=item normalize_net($net)
+
+This function translates an IP/network into the same representation that iptables
+or ip6tables uses upon listing a policy. The first example shows an IPv4 network
+and how iptables lists it, and the second is an IPv6 network:
+
+ print $ipt_obj->normalize_net('10.1.2.3/24'), "\n" # prints '10.1.2.0/24'
+ print $ipt_obj->normalize_net('0000:0000:00AA:0000:0000:AA00:0000:0001/64'), "\n" # prints '0:0:aa::/64'
+
=item run_ipt_cmd($cmd)
This function is a generic work horse function for executing iptables commands,
=head1 SEE ALSO
The IPTables::ChainMgr extension is closely associated with the IPTables::Parse
-extension, and both are heavily used by the psad, fwsnort, and fwknop projects
-to manipulate iptables policies based on various criteria (see the psad(8),
-fwsnort(8), and fwknop(8) man pages). As always, the iptables(8) man page
-provides the best information on command line execution and theory behind
-iptables.
+extension, and both are heavily used by the psad and fwsnort projects to
+manipulate iptables policies based on various criteria (see the psad(8) and
+fwsnort(8) man pages). As always, the iptables(8) man page provides the best
+information on command line execution and theory behind iptables.
Although there is no mailing that is devoted specifically to the IPTables::ChainMgr
extension, questions about the extension will be answered on the following
lists:
The psad mailing list: http://lists.sourceforge.net/lists/listinfo/psad-discuss
- The fwknop mailing list: http://lists.sourceforge.net/lists/listinfo/fwknop-discuss
The fwsnort mailing list: http://lists.sourceforge.net/lists/listinfo/fwsnort-discuss
-The latest version of the IPTables::ChainMgr extension can be found at:
+The latest version of the IPTables::ChainMgr extension can be found on CPAN and
+also here:
+
+ http://www.cipherdyne.org/modules/
+
+Source control is provided by git:
-http://www.cipherdyne.org/modules/
+ http://www.cipherdyne.org/git/IPTables-ChainMgr.git
+ http://www.cipherdyne.org/cgi-bin/gitweb.cgi?p=IPTables-ChainMgr.git;a=summary
=head1 CREDITS
=head1 AUTHOR
The IPTables::ChainMgr extension was written by Michael Rash F<E<lt>mbr@cipherdyne.orgE<gt>>
-to support the psad, fwknop, and fwsnort projects. Please send email to
-this address if there are any questions, comments, or bug reports.
+to support the psad and fwsnort projects. Please send email to this address if
+there are any questions, comments, or bug reports.
=head1 COPYRIGHT AND LICENSE
-Copyright (C) 2005-2008 by Michael Rash
+Copyright (C) 2005-2012 Michael Rash. All rights reserved.
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.8.5 or,
-at your option, any later version of Perl 5 you may have available.
+This module is free software. You can redistribute it and/or
+modify it under the terms of the Artistic License 2.0. More information
+can be found here: http://www.perl.com/perl/misc/Artistic.html
+This program is distributed "as is" 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.
=cut
#
# File: IPTables::Parse.pm
#
-# Purpose: Perl interface to parse iptables rulesets.
+# Purpose: Perl interface to parse iptables and ip6tables rulesets.
#
# Author: Michael Rash (mbr@cipherdyne.org)
#
-# Version: 0.7
+# Version: 1.1
#
##################################################################
#
use warnings;
use vars qw($VERSION);
-$VERSION = '0.7';
+$VERSION = '1.1';
sub new() {
my $class = shift;
my %args = @_;
my $self = {
- _iptables => $args{'iptables'} || '/sbin/iptables',
+ _iptables => $args{'iptables'} || $args{'ip6tables'} || '/sbin/iptables',
_iptout => $args{'iptout'} || '/tmp/ipt.out',
_ipterr => $args{'ipterr'} || '/tmp/ipt.err',
_ipt_alarm => $args{'ipt_alarm'} || 30,
unless -e $self->{'_iptables'};
croak "[*] $self->{'_iptables'} not executable.\n"
unless -x $self->{'_iptables'};
+
+ $self->{'_ipt_bin_name'} = 'iptables';
+ $self->{'_ipt_bin_name'} = $1 if $self->{'_iptables'} =~ m|.*/(\S+)|;
+
bless $self, $class;
}
my $found_chain = 0;
my @ipt_lines = ();
+
+ ### only used for IPv4 + NAT
my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|;
### array of hash refs
'to_port' => '',
'extended' => '',
'state' => '',
+ 'ctstate' => '',
'raw' => $line
);
if ($ipt_verbose) {
+
+ ### iptables:
### 0 0 ACCEPT tcp -- eth1 * 192.168.10.3 0.0.0.0/0 tcp dpt:80
### 0 0 ACCEPT tcp -- eth1 * 192.168.10.15 0.0.0.0/0 tcp dpt:22
### 33 2348 ACCEPT tcp -- eth1 * 192.168.10.2 0.0.0.0/0 tcp dpt:22
### 0 0 ACCEPT tcp -- eth1 * 192.168.10.2 0.0.0.0/0 tcp dpt:80
### 0 0 DNAT tcp -- * * 123.123.123.123 0.0.0.0/0 tcp dpt:55000 to:192.168.12.12:80
- if ($line =~ m|^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\-\-\s+
- (\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)|x) {
+
+ ### ip6tables:
+ ### 0 0 ACCEPT tcp * * ::/0 fe80::aa:0:1/128 tcp dpt:12345
+ ### 0 0 LOG all * * ::/0 ::/0 LOG flags 0 level 4
+
+ my $match_re = qr/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+\-\-\s+
+ (\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)/x;
+
+ if ($self->{'_ipt_bin_name'} eq 'ip6tables') {
+ $match_re = qr/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+
+ (\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)/x;
+ }
+
+ if ($line =~ $match_re) {
$rule{'packets'} = $1;
$rule{'bytes'} = $2;
$rule{'target'} = $3;
$rule{'to_ip'} = $1;
$rule{'to_port'} = $2;
}
-
- for my $state_hr (@global_accept_state) {
- next unless $state_hr->{'src'} eq '0.0.0.0/0';
- next unless $state_hr->{'dst'} eq '0.0.0.0/0';
- next unless $state_hr->{'proto'} eq 'all' or
- $state_hr->{'proto'} = $rule{'proto'};
- next unless $state_hr->{'intf_in'} eq '*' or
- $state_hr->{'intf_in'} eq $rule{'intf_in'};
- next unless $state_hr->{'intf_out'} eq '*' or
- $state_hr->{'intf_out'} eq $rule{'intf_out'};
- ### if we make it here, then the state rule
- ### applies to this rule
- $rule{'state'} = $state_hr->{'state'};
- }
}
- if ($rule{'target'} eq 'ACCEPT'
- and $rule{'extended'} =~ m|^state\s+(\S+)|) {
- my $state_str = $1;
- if ($state_str =~ /ESTABLISHED/
- or $state_str =~ /RELATED/) {
-
- push @global_accept_state, {
- 'state' => $state_str,
- 'src' => $rule{'src'},
- 'dst' => $rule{'dst'},
- 'intf_in' => $rule{'intf_in'},
- 'intf_out' => $rule{'intf_out'},
- 'proto' => $rule{'protocol'}
- };
- my %state_hash = ();
- }
+ if ($rule{'extended'} =~ /\bctstate\s+(\S+)/) {
+ $rule{'ctstate'} = $1;
+ } elsif ($rule{'extended'} =~ /\bstate\s+(\S+)/) {
+ $rule{'state'} = $1;
}
}
}
} else {
+
+ ### iptables:
### ACCEPT tcp -- 164.109.8.0/24 0.0.0.0/0 tcp dpt:22 flags:0x16/0x02
### ACCEPT tcp -- 216.109.125.67 0.0.0.0/0 tcp dpts:7000:7500
### ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpts:7000:7500
### LOG all -- 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix `DROP '
### LOG all -- 127.0.0.2 0.0.0.0/0 LOG flags 0 level 4
- ### ### DNAT tcp -- 123.123.123.123 0.0.0.0/0 tcp dpt:55000 to:192.168.12.12:80
+ ### DNAT tcp -- 123.123.123.123 0.0.0.0/0 tcp dpt:55000 to:192.168.12.12:80
+
+ ### ip6tables:
+ ### ACCEPT tcp ::/0 fe80::aa:0:1/128 tcp dpt:12345
+ ### LOG all ::/0 ::/0 LOG flags 0 level 4
- if ($line =~ m|^\s*(\S+)\s+(\S+)\s+\-\-\s+(\S+)\s+(\S+)\s*(.*)|) {
+ my $match_re = qr/^\s*(\S+)\s+(\S+)\s+\-\-\s+(\S+)\s+(\S+)\s*(.*)/;
+
+ if ($self->{'_ipt_bin_name'} eq 'ip6tables') {
+ $match_re = qr/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)/;
+ }
+
+ if ($line =~ $match_re) {
$rule{'target'} = $1;
my $proto = $2;
$proto = 'all' if $proto eq '0';
$rule{'to_ip'} = $1;
$rule{'to_port'} = $2;
}
+
+ if ($rule{'extended'} =~ /\bctstate\s+(\S+)/) {
+ $rule{'ctstate'} = $1;
+ } elsif ($rule{'extended'} =~ /\bstate\s+(\S+)/) {
+ $rule{'state'} = $1;
+ }
}
}
}
@ipt_lines = @$out_ar;
}
- return '[-] Could not get iptables output!', 0
+ return "[-] Could not get $self->{'_ipt_bin_name'} output!", 0
unless @ipt_lines;
my %protocols = ();
my $found_chain = 0;
+ my $found_default_drop = 0;
my $rule_ctr = 1;
my $prefix;
my $policy = 'ACCEPT';
- my $any_ip_re = '(?:0\.){3}0/0';
+ my $any_ip_re = qr/(?:0\.){3}0\x2f0|\x3a{2}\x2f0/;
LINE: for my $line (@ipt_lines) {
chomp $line;
next LINE unless $found_chain;
### include ULOG target as well
- if ($line =~ m|^\s*U?LOG\s+(\w+)\s+\-\-\s+.*
- $any_ip_re\s+$any_ip_re\s+(.*)|x) {
+ my $log_re = qr/^\s*U?LOG\s+(\w+)\s+\-\-\s+.*
+ $any_ip_re\s+$any_ip_re\s+(.*)/x;
+ my $drop_re = qr/^DROP\s+(\w+)\s+\-\-\s+.*
+ $any_ip_re\s+$any_ip_re\s*$/x;
+
+ if ($self->{'_ipt_bin_name'} eq 'ip6tables') {
+ $log_re = qr/^\s*U?LOG\s+(\w+)\s+
+ $any_ip_re\s+$any_ip_re\s+(.*)/x;
+ $drop_re = qr/^DROP\s+(\w+)\s+
+ $any_ip_re\s+$any_ip_re\s*$/x;
+ }
+
+ ### might as well pick up any default logging rules as well
+ if ($line =~ $log_re) {
my $proto = $1;
my $p_tmp = $2;
my $prefix = 'NONE';
### $proto may equal "all" here
$protocols{$proto}{'LOG'}{'prefix'} = $prefix;
$protocols{$proto}{'LOG'}{'rulenum'} = $rule_ctr;
- } elsif ($policy eq 'ACCEPT' and $line =~ m|^DROP\s+(\w+)\s+\-\-\s+.*
- $any_ip_re\s+$any_ip_re\s*$|x) {
+ } elsif ($policy eq 'ACCEPT' and $line =~ $drop_re) {
my $proto = $1;
$proto = 'all' if $proto eq '0';
### DROP all -- 0.0.0.0/0 0.0.0.0/0
$protocols{$1}{'DROP'} = $rule_ctr;
+ $found_default_drop = 1;
}
$rule_ctr++;
}
+
### if the policy in the chain is DROP, then we don't
### necessarily need to find a default DROP rule.
if ($policy eq 'DROP') {
$protocols{'all'}{'DROP'} = 0;
+ $found_default_drop = 1;
}
- return \%protocols;
+
+ return "[-] There are no default drop rules in the " .
+ "$self->{'_ipt_bin_name'} policy!", 0
+ unless %protocols and $found_default_drop;
+
+ return \%protocols, 1;
}
sub default_log() {
my $file = shift || '';
my $iptables = $self->{'_iptables'};
- my $any_ip_re = '(?:0\.){3}0/0';
+ my $any_ip_re = qr/(?:0\.){3}0\x2f0|\x3a{2}\x2f0/;
my @ipt_lines = ();
my %log_chains = ();
my %log_rules = ();
}
}
- return '[-] Could not get iptables output!', 0
+ return "[-] Could not get $self->{'_ipt_bin_name'} output!", 0
unless @ipt_lines;
### first get all logging rules and associated chains
my $proto = '';
my $found = 0;
if ($ipt_verbose) {
- if ($line =~ m|^\s*\d+\s+\d+\s*U?LOG\s+(\w+)\s+\-\-\s+
- \S+\s+\S+\s+$any_ip_re
- \s+$any_ip_re\s+.*U?LOG|x) {
- $proto = $1;
- $found = 1;
+ if ($self->{'_ipt_bin_name'} eq 'ip6tables') {
+ if ($line =~ m|^\s*\d+\s+\d+\s*U?LOG\s+(\w+)\s+
+ \S+\s+\S+\s+$any_ip_re
+ \s+$any_ip_re\s+.*U?LOG|x) {
+ $proto = $1;
+ $found = 1;
+ }
+ } else {
+ if ($line =~ m|^\s*\d+\s+\d+\s*U?LOG\s+(\w+)\s+\-\-\s+
+ \S+\s+\S+\s+$any_ip_re
+ \s+$any_ip_re\s+.*U?LOG|x) {
+ $proto = $1;
+ $found = 1;
+ }
}
} else {
- if ($line =~ m|^\s*U?LOG\s+(\w+)\s+\-\-\s+$any_ip_re
- \s+$any_ip_re\s+.*U?LOG|x) {
- $proto = $1;
- $found = 1;
+ if ($self->{'_ipt_bin_name'} eq 'ip6tables') {
+ if ($line =~ m|^\s*U?LOG\s+(\w+)\s+$any_ip_re
+ \s+$any_ip_re\s+.*U?LOG|x) {
+ $proto = $1;
+ $found = 1;
+ }
+ } else {
+ if ($line =~ m|^\s*U?LOG\s+(\w+)\s+\-\-\s+$any_ip_re
+ \s+$any_ip_re\s+.*U?LOG|x) {
+ $proto = $1;
+ $found = 1;
+ }
}
}
}
}
- return '[-] There are no logging rules in the iptables policy!', 0
- unless %log_chains;
+ return "[-] There are no default logging rules " .
+ "in the $self->{'_ipt_bin_name'} policy!", 0 unless %log_chains;
my %sub_chains = ();
}
}
- return \%log_rules;
+ return \%log_rules, 1;
}
sub sub_chains() {
if ($found and $line =~ /^\s*Chain\s/) {
last;
}
- if ($line =~ m|^\s*(\S+)\s+\S+\s+\-\-|) {
+ if ($line =~ m|^\s*(\S+)\s+\S+\s+|) {
my $new_chain = $1;
if ($new_chain ne 'LOG'
and $new_chain ne 'DROP'
and $new_chain ne 'QUEUE'
and $new_chain ne 'SNAT'
and $new_chain ne 'DNAT'
- and $new_chain ne 'MASQUERADE') {
+ and $new_chain ne 'MASQUERADE'
+ and $new_chain ne 'pkts'
+ and $new_chain ne 'Chain'
+ and $new_chain ne 'target') {
$chains_href->{$new_chain} = '';
&sub_chains($new_chain, $chains_href, $ipt_lines_aref);
}
sub exec_iptables() {
my $self = shift;
- my $cmd = shift || croak '[*] Must specify an iptables command to run.';
+ my $cmd = shift || croak "[*] Must specify an " .
+ "$self->{'_ipt_bin_name'} command to run.";
my $iptables = $self->{'_iptables'};
my $iptout = $self->{'_iptout'};
my $ipterr = $self->{'_ipterr'};
my $ipt_exec_sleep = $self->{'_ipt_exec_sleep'};
my $sigchld_handler = $self->{'_sigchld_handler'};
- croak "[*] $cmd does not look like an iptables command."
- unless $cmd =~ m|^\s*iptables| or $cmd =~ m|^\S+/iptables|;
+ croak "[*] $cmd does not look like an $self->{'_ipt_bin_name'} command."
+ unless $cmd =~ m|^\s*iptables| or $cmd =~ m|^\S+/iptables|
+ or $cmd =~ m|^\s*ip6tables| or $cmd =~ m|^\S+/ip6tables|;
my $rv = 1;
my @stdout = ();
if ($debug or $verbose) {
print $fh localtime() . " [+] IPTables::Parse: ",
"sleeping for $ipt_exec_sleep seconds before ",
- "executing iptables command.\n";
+ "executing $self->{'_ipt_bin_name'} command.\n";
}
sleep $ipt_exec_sleep;
}
### iptables should never take longer than 30 seconds to execute,
### unless there is some absolutely enormous policy or the kernel
### is exceedingly busy
- local $SIG{'ALRM'} = sub {die "[*] iptables command timeout.\n"};
+ local $SIG{'ALRM'} = sub {die "[*] $self->{'_ipt_bin_name'} " .
+ "command timeout.\n"};
alarm $ipt_alarm;
waitpid($ipt_pid, 0);
alarm 0;
kill 9, $ipt_pid unless kill 15, $ipt_pid;
}
} else {
- croak "[*] Could not fork iptables: $!"
+ croak "[*] Could not fork $self->{'_ipt_bin_name'}: $!"
unless defined $ipt_pid;
### exec the iptables command and preserve stdout and stderr
}
if ($debug or $verbose) {
- print $fh localtime() . " iptables command stdout:\n";
+ print $fh localtime() . " $self->{'_ipt_bin_name'} " .
+ "command stdout:\n";
for my $line (@stdout) {
if ($line =~ /\n$/) {
print $fh $line;
print $fh $line, "\n";
}
}
- print $fh localtime() . " iptables command stderr:\n";
+ print $fh localtime() . " $self->{'_ipt_bin_name'} " .
+ "command stderr:\n";
for my $line (@stderr) {
if ($line =~ /\n$/) {
print $fh $line;
=head1 NAME
-IPTables::Parse - Perl extension for parsing iptables firewall rulesets
+IPTables::Parse - Perl extension for parsing iptables and ip6tables policies
=head1 SYNOPSIS
use IPTables::Parse;
+ my $ipt_bin = '/sbin/iptables'; # can set this to /sbin/ip6tables
+
my %opts = (
- 'iptables' => '/sbin/iptables',
+ 'iptables' => $ipt_bin,
'iptout' => '/tmp/iptables.out',
'ipterr' => '/tmp/iptables.err',
'debug' => 0,
if (defined $ipt_hr->{'all'}) {
print "The INPUT chain has a default DROP rule for all protocols.\n";
} else {
- for my $proto qw/tcp udp icmp/ {
+ for my $proto (qw/tcp udp icmp/) {
if (defined $ipt_hr->{$proto}) {
print "The INPUT chain drops $proto by default.\n";
}
}
}
} else {
- print "[-] Could not parse iptables policy\n";
+ print "[-] Could not parse $ipt_obj->{'_ipt_bin_name'} policy\n";
}
($ipt_hr, $rv) = $ipt_obj->default_log($table, $chain);
if (defined $ipt_hr->{'all'}) {
print "The INPUT chain has a default LOG rule for all protocols.\n";
} else {
- for my $proto qw/tcp udp icmp/ {
+ for my $proto (qw/tcp udp icmp/) {
if (defined $ipt_hr->{$proto}) {
print "The INPUT chain logs $proto by default.\n";
}
}
}
} else {
- print "[-] Could not parse iptables policy\n";
+ print "[-] Could not parse $ipt_obj->{'_ipt_bin_name'} policy\n";
}
=head1 DESCRIPTION
-The C<IPTables::Parse> package provides an interface to parse iptables
-rules on Linux systems through the direct execution of iptables commands, or
-from parsing a file that contains an iptables policy listing. You can get the
-current policy applied to a table/chain, look for a specific user-defined chain,
-check for a default DROP policy, or determing whether or not logging rules exist.
+The C<IPTables::Parse> package provides an interface to parse iptables or
+ip6tables rules on Linux systems through the direct execution of
+iptables/ip6tables commands, or from parsing a file that contains an
+iptables/ip6tables policy listing. You can get the current policy applied to a
+table/chain, look for a specific user-defined chain, check for a default DROP
+policy, or determing whether or not logging rules exist.
=head1 FUNCTIONS
C<protocol>, C<s_port>, C<d_port>, C<target>, C<packets>, C<bytes>, C<intf_in>,
C<intf_out>, C<to_ip>, C<to_port>, C<state>, C<raw>, and C<extended>. The C<extended>
element contains the rule output past the protocol information, and the C<raw>
-element contains the complete rule itself as reported by iptables.
+element contains the complete rule itself as reported by iptables or ip6tables.
=item default_drop($table, $chain)
-This function parses the running iptables policy in order to determine if
-the specified chain contains a default DROP rule. Two values are returned,
-a hash reference whose keys are the protocols that are dropped by default
-if a global ACCEPT rule has not accepted matching packets first, along with
-a return value that tells the caller if parsing the iptables policy was
-successful. Note that if all protocols are dropped by default, then the
-hash key 'all' will be defined.
+This function parses the running iptables or ip6tables policy in order to
+determine if the specified chain contains a default DROP rule. Two values
+are returned, a hash reference whose keys are the protocols that are dropped by
+default if a global ACCEPT rule has not accepted matching packets first, along
+with a return value that tells the caller if parsing the iptables or ip6tables
+policy was successful. Note that if all protocols are dropped by default, then
+the hash key 'all' will be defined.
($ipt_hr, $rv) = $ipt_obj->default_drop('filter', 'INPUT');
=item default_log($table, $chain)
-This function parses the running iptables policy in order to determine if
+This function parses the running iptables or ip6tables policy in order to determine if
the specified chain contains a default LOG rule. Two values are returned,
a hash reference whose keys are the protocols that are logged by default
if a global ACCEPT rule has not accepted matching packets first, along with
-a return value that tells the caller if parsing the iptables policy was
+a return value that tells the caller if parsing the iptables or ip6tables policy was
successful. Note that if all protocols are logged by default, then the
hash key 'all' will be defined. An example invocation is:
=head1 SEE ALSO
The IPTables::Parse is used by the IPTables::ChainMgr extension in support of
-the psad, fwsnort, and fwknop projects to parse iptables policies (see the psad(8),
-fwsnort(8), and fwknop(8) man pages). As always, the iptables(8) provides the
-best information on command line execution and theory behind iptables.
+the psad and fwsnort projects to parse iptables or ip6tables policies (see the psad(8),
+and fwsnort(8) man pages). As always, the iptables(8) and ip6tables(8) man pages
+provide the best information on command line execution and theory behind iptables
+and ip6tables.
Although there is no mailing that is devoted specifically to the IPTables::Parse
extension, questions about the extension will be answered on the following
lists:
The psad mailing list: http://lists.sourceforge.net/lists/listinfo/psad-discuss
- The fwknop mailing list: http://lists.sourceforge.net/lists/listinfo/fwknop-discuss
The fwsnort mailing list: http://lists.sourceforge.net/lists/listinfo/fwsnort-discuss
-The latest version of the IPTables::Parse extension can be found at:
+The latest version of the IPTables::Parse extension can be found on CPAN and
+also here:
+
+ http://www.cipherdyne.org/modules/
-http://www.cipherdyne.org/modules/
+Source control is provided by git:
+
+ http://www.cipherdyne.org/git/IPTables-Parse.git
+ http://www.cipherdyne.org/cgi-bin/gitweb.cgi?p=IPTables-Parse.git;a=summary
=head1 CREDITS
=head1 AUTHOR
The IPTables::Parse extension was written by Michael Rash F<E<lt>mbr@cipherdyne.orgE<gt>>
-to support the psad, fwknop, and fwsnort projects. Please send email to
+to support the psad and fwsnort projects. Please send email to
this address if there are any questions, comments, or bug reports.
=head1 COPYRIGHT AND LICENSE
-Copyright (C) 2005-2008 by Michael Rash
+Copyright (C) 2005-2012 Michael Rash. All rights reserved.
+
+This module is free software. You can redistribute it and/or
+modify it under the terms of the Artistic License 2.0. More information
+can be found here: http://www.perl.com/perl/misc/Artistic.html
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself, either Perl version 5.8.5 or,
-at your option, any later version of Perl 5 you may have available.
+This program is distributed "as is" 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.
=cut