Derive iptables rule 'match' keys from IPTables::Parse
authorMichael Rash <mbr@cipherdyne.org>
Tue, 6 Mar 2012 03:05:54 +0000 (22:05 -0500)
committerMichael Rash <mbr@cipherdyne.org>
Tue, 6 Mar 2012 03:05:54 +0000 (22:05 -0500)
For IPTables::Parse versions > 1.1, updated IPTables::ChainMgr to derive
iptables 'match' keys to build iptables rules from IPTables::Parse.

Changes
lib/IPTables/ChainMgr.pm
t/basic_tests.pl

diff --git a/Changes b/Changes
index 62d245c..7f18698 100644 (file)
--- a/Changes
+++ b/Changes
@@ -3,6 +3,8 @@ Revision history for Perl extension IPTables::ChainMgr.
 1.3 Sun Mar 04 22:45:44 2012
     - For IPTables::Parse versions > 1.1, updated IPTables::ChainMgr to derive
       extended hash search keys for find_ip_rule() from IPTables::Parse.
+    - For IPTables::Parse versions > 1.1, updated IPTables::ChainMgr to derive
+      iptables 'match' keys to build iptables rules from IPTables::Parse.
     - Simplified append_ip_rule() to just be a wrapper around add_ip_rule(),
       which was updated to allow the value "-1" to be passed in as the rule
       insertion number in order to denote "append" (-A <chain>) mode.
index 80fe495..0d7b35b 100644 (file)
@@ -224,24 +224,117 @@ sub add_ip_rule() {
     }
 
     if (keys %$extended_hr) {
-        $ipt_cmd .= "-p $extended_hr->{'protocol'} "
+
+        my ($ipt_tmp_str, $msg_tmp_str) = $self->build_ipt_matches(
+            $extended_hr, $normalized_src, $normalized_dst);
+
+        $msg = "Table: $table, chain: $chain, added $normalized_src " .
+            "-> $normalized_dst ";
+
+        ### always add the target at the end
+        $ipt_cmd .= "$ipt_tmp_str -j $target";
+
+        $msg .= $msg_tmp_str;
+        $msg =~ s/\s*$//;
+
+    } else {
+        $ipt_cmd .= "-s $normalized_src -d $normalized_dst -j $target";
+        $msg = "Table: $table, chain: $chain, added $normalized_src " .
+            "-> $normalized_dst";
+    }
+    my ($rv, $out_ar, $err_ar) = $self->run_ipt_cmd($ipt_cmd);
+    if ($rv) {
+        push @$out_ar, $msg if $msg;
+    }
+    push @$err_ar, $idx_err if $idx_err;
+    return $rv, $out_ar, $err_ar;
+}
+
+sub build_ipt_matches() {
+    my $self = shift;
+    my $extended_hr = shift;
+    my $normalized_src = shift || '';
+    my $normalized_dst = shift || '';
+
+    my $ipt_matches = '';
+    my $msg = '';
+
+    if ($IPTables::Parse::VERSION > 1.1) {
+
+        ### get the information about how to build the match part of the
+        ### iptables command from the IPTables::Parse module
+        my $ipt_parse = new IPTables::Parse(
+            'iptables'  => $self->{'_iptables'},
+            'iptout'    => $self->{'_iptout'},
+            'ipterr'    => $self->{'_ipterr'},
+            'debug'     => $self->{'_debug'},
+            'verbose'   => $self->{'_verbose'},
+            'ipt_alarm' => $self->{'_ipt_alarm'},
+            'ipt_exec_style' => $self->{'_ipt_exec_style'},
+            'ipt_exec_sleep' => $self->{'_ipt_exec_sleep'},
+            'sigchld_handler' => $self->{'_sigchld_handler'},
+        ) or croak "[*] Could not acquire IPTables::Parse object";
+
+        ### src and dst
+        if ($normalized_src ne '') {
+            $ipt_matches .= "$ipt_parse->{'parse_keys'}->{'regular'}->{'src'}->{'ipt_match'} " .
+                "$normalized_src ";
+        }
+
+        if ($normalized_src ne '') {
+            $ipt_matches .= "$ipt_parse->{'parse_keys'}->{'regular'}->{'dst'}->{'ipt_match'} " .
+                "$normalized_dst ";
+        }
+
+        ### handle 'regular' keys first
+        for my $key (keys %$extended_hr) {
+            if (defined $ipt_parse->{'parse_keys'}->{'regular'}->{$key}) {
+                $ipt_matches .= "$ipt_parse->{'parse_keys'}->{'regular'}->{$key}->{'ipt_match'} " .
+                    "$extended_hr->{$key} ";
+            }
+        }
+
+        ### special case for port values (handle them now)
+        for my $key (qw/sport s_dport dport d_port/) {
+            next unless defined $extended_hr->{$key};
+            if ($extended_hr->{$key}) {
+                $ipt_matches .= "$ipt_parse->{'parse_keys'}->{'extended'}->{$key}->{'ipt_match'} " .
+                    qq|$extended_hr->{$key} |;
+            }
+        }
+
+        ### now handle 'match' keys
+        for my $key (keys %$extended_hr) {
+            my $parse_hr = $ipt_parse->{'parse_keys'}->{'extended'};
+            if (defined $parse_hr->{$key}) {
+                next if $key =~ /s_?port$/ or $key =~ /d_?port$/;
+                if (defined $parse_hr->{$key}->{'use_quotes'}
+                        and $parse_hr->{$key}->{'use_quotes'}) {
+                    $ipt_matches .= "$parse_hr->{$key}->{'ipt_match'} " .
+                        qq|"$extended_hr->{$key}" |;
+                } else {
+                    $ipt_matches .= "$parse_hr->{$key}->{'ipt_match'} " .
+                        "$extended_hr->{$key} ";
+                }
+            }
+        }
+
+    } else {
+        $ipt_matches .= "-p $extended_hr->{'protocol'} "
             if defined $extended_hr->{'protocol'};
-        $ipt_cmd .= "-s $normalized_src ";
-        $ipt_cmd .= "--sport $extended_hr->{'s_port'} "
+        $ipt_matches .= "-s $normalized_src ";
+        $ipt_matches .= "--sport $extended_hr->{'s_port'} "
             if defined $extended_hr->{'s_port'};
-        $ipt_cmd .= "-d $normalized_dst ";
-        $ipt_cmd .= "--dport $extended_hr->{'d_port'} "
+        $ipt_matches .= "-d $normalized_dst ";
+        $ipt_matches .= "--dport $extended_hr->{'d_port'} "
             if defined $extended_hr->{'d_port'};
-        $ipt_cmd .= "-m mac --mac-source $extended_hr->{'mac_source'} "
+        $ipt_matches .= "-m mac --mac-source $extended_hr->{'mac_source'} "
             if defined $extended_hr->{'mac_source'};
-        $ipt_cmd .= "-m state --state $extended_hr->{'state'} "
+        $ipt_matches .= "-m state --state $extended_hr->{'state'} "
             if defined $extended_hr->{'state'};
-        $ipt_cmd .= "-m conntrack --ctstate $extended_hr->{'ctstate'} "
+        $ipt_matches .= "-m conntrack --ctstate $extended_hr->{'ctstate'} "
             if defined $extended_hr->{'ctstate'};
-        $ipt_cmd .= "-j $target";
 
-        $msg = "Table: $table, chain: $chain, added $normalized_src " .
-            "-> $normalized_dst ";
         for my $key (keys %$extended_hr) {
             $msg .= "$key $extended_hr->{$key} "
                 if defined $extended_hr->{$key};
@@ -250,23 +343,15 @@ sub add_ip_rule() {
         ### for NAT
         if (defined $extended_hr->{'to_ip'} and
                 defined $extended_hr->{'to_port'}) {
-            $ipt_cmd .= " --to $extended_hr->{'to_ip'}:" .
+            $ipt_matches .= " --to $extended_hr->{'to_ip'}:" .
                 "$extended_hr->{'to_port'}";
             $msg .= "$extended_hr->{'to_ip'}:$extended_hr->{'to_port'}";
         }
-
-        $msg =~ s/\s*$//;
-    } else {
-        $ipt_cmd .= "-s $normalized_src -d $normalized_dst -j $target";
-        $msg = "Table: $table, chain: $chain, added $normalized_src " .
-            "-> $normalized_dst";
     }
-    my ($rv, $out_ar, $err_ar) = $self->run_ipt_cmd($ipt_cmd);
-    if ($rv) {
-        push @$out_ar, $msg if $msg;
-    }
-    push @$err_ar, $idx_err if $idx_err;
-    return $rv, $out_ar, $err_ar;
+
+    $ipt_matches =~ s/\s*$//;
+
+    return ($ipt_matches, $msg);
 }
 
 sub delete_ip_rule() {
@@ -355,6 +440,7 @@ sub find_ip_rule() {
 
     if ($IPTables::Parse::VERSION > 1.1) {
         @parse_keys = ();
+
         ### get the keys list from the IPTables::Parse module
         for my $key (keys %{$ipt_parse->{'parse_keys'}->{'regular'}}) {
             push @parse_keys, $key;
index 4dc8cf2..c33fa65 100755 (executable)
@@ -290,35 +290,42 @@ sub add_extended_rules_tests() {
     for my $target (qw/LOG ACCEPT RETURN/) {
 
         ### TCP
-        &dots_print("add_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target ");
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target ");
         my ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
                 $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
                 {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
         &pass_fail($rv, "   Could not add TCP $src_ip(0) -> $dst_ip(80) $target rule");
 
         ### TCP + state tracking
-        &dots_print("add_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target ");
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target ");
         ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
                 $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
                 {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80, 'state' => 'ESTABLISHED,RELATED'});
         &pass_fail($rv, "   Could not add TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target rule");
 
         ### TCP + ctstate tracking
-        &dots_print("add_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target ");
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target ");
         ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
                 $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
                 {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80, 'ctstate' => 'ESTABLISHED,RELATED'});
         &pass_fail($rv, "   Could not add TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target rule");
 
         ### TCP + mac source
-        &dots_print("add_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source ");
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source ");
         ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
                 $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
                 {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80, 'mac_source' => $mac_source});
         &pass_fail($rv, "   Could not add TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source");
 
+        ### TCP + comment
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target comment 'test comment' ");
+        ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
+                $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
+                {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80, 'comment' => 'test comment'});
+        &pass_fail($rv, "   Could not add TCP $src_ip(0) -> $dst_ip(80) $target comment 'test comment'");
+
         ### UDP
-        &dots_print("add_ip_rules(): $test_table $test_chain UDP $src_ip(0) -> $dst_ip(53) $target ");
+        &dots_print("add_ext_ip_rules(): $test_table $test_chain UDP $src_ip(0) -> $dst_ip(53) $target ");
         ($rv, $out_ar, $err_ar) = $ipt_obj->add_ip_rule($src_ip,
                 $dst_ip, $chain_past_end, $test_table, $test_chain, $target,
                 {'protocol' => 'udp', 's_port' => 0, 'd_port' => 53});
@@ -341,34 +348,41 @@ sub find_extended_rules_tests() {
     }
 
     for my $target (qw/LOG ACCEPT RETURN/) {
-        &dots_print("find rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target ");
+        &dots_print("find ext rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target ");
         my ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
                 $dst_ip, $test_table, $test_chain, $target,
                 {'normalize' => 1, 'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80});
         &pass_fail($rule_position, "   Could not find TCP $src_ip(0) -> $dst_ip(80) $target rule");
 
-        &dots_print("find rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target ");
+        &dots_print("find ext rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target ");
         ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
                 $dst_ip, $test_table, $test_chain, $target,
                 {'normalize' => 1, 'protocol' => 'tcp', 's_port' => 0,
                 'd_port' => 80, 'state' => 'ESTABLISHED,RELATED'});
         &pass_fail($rule_position, "   Could not find TCP $src_ip(0) -> $dst_ip(80) state ESTABLISHED,RELATED $target rule");
 
-        &dots_print("find rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target ");
+        &dots_print("find ext rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target ");
         ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
                 $dst_ip, $test_table, $test_chain, $target,
                 {'normalize' => 1, 'protocol' => 'tcp', 's_port' => 0,
                 'd_port' => 80, 'ctstate' => 'ESTABLISHED,RELATED'});
         &pass_fail($rule_position, "   Could not find TCP $src_ip(0) -> $dst_ip(80) ctstate ESTABLISHED,RELATED $target rule");
 
-        &dots_print("find rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source ");
+        &dots_print("find ext rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source ");
         ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
                 $dst_ip, $test_table, $test_chain, $target,
                 {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80,
                 'mac_source' => $mac_source});
         &pass_fail($rule_position, "   Could not find TCP $src_ip(0) -> $dst_ip(80) $target mac_source $mac_source");
 
-        &dots_print("find rule: $test_table $test_chain UDP $src_ip(0) -> $dst_ip(53) $target ");
+        ### TCP + comment
+        &dots_print("find ext rule: $test_table $test_chain TCP $src_ip(0) -> $dst_ip(80) $target comment 'test comment' ");
+        ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
+                $dst_ip, $test_table, $test_chain, $target,
+                {'protocol' => 'tcp', 's_port' => 0, 'd_port' => 80, 'comment' => 'test comment'});
+        &pass_fail($rule_position, "   Could not find TCP $src_ip(0) -> $dst_ip(80) $target comment 'test comment'");
+
+        &dots_print("find ext rule: $test_table $test_chain UDP $src_ip(0) -> $dst_ip(53) $target ");
         ($rule_position, $num_chain_rules) = $ipt_obj->find_ip_rule($src_ip,
                 $dst_ip, $test_table, $test_chain, $target,
                 {'normalize' => 1, 'protocol' => 'udp', 's_port' => 0, 'd_port' => 53});