vcl-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From arku...@apache.org
Subject svn commit: r1790637 - in /vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall: firewalld.pm iptables.pm
Date Fri, 07 Apr 2017 22:15:38 GMT
Author: arkurth
Date: Fri Apr  7 22:15:38 2017
New Revision: 1790637

URL: http://svn.apache.org/viewvc?rev=1790637&view=rev
Log:
VCL-972
Initial commit of firewalld.pm module.  It's mostly done but needs some finishing touches.
 It relies heavily on iptables.pm.

Added:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm

Added: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm?rev=1790637&view=auto
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm (added)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/firewalld.pm Fri Apr  7 22:15:38
2017
@@ -0,0 +1,625 @@
+#!/usr/bin/perl -w
+###############################################################################
+# $Id:  $
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+=head1 NAME
+
+VCL::Module::OS::Linux::firewall::firewalld.pm
+
+=head1 DESCRIPTION
+
+ This module provides VCL support for firewalld-based firewalls.
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Linux::firewall::firewalld;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux::firewall::iptables);
+
+# Specify the version of this module
+our $VERSION = '2.4';
+
+our @ISA;
+
+# Specify the version of Perl to use
+use 5.008000;
+
+use strict;
+use warnings;
+use diagnostics;
+
+use VCL::utils;
+
+##############################################################################
+
+=head1 OBJECT METHODS
+
+=cut
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 initialize
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub initialize {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my $arguments = shift || {};
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to control $computer_name");
+	
+	if (!$self->os->service_exists('firewalld')) {
+		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name,
firewalld service does not exist");
+		return 0;
+	}
+	
+	if (!$self->os->is_service_enabled('firewalld')) {
+		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name,
firewalld service is not enabled");
+		return 0;
+	}
+	
+	if (!$self->os->command_exists('firewall-cmd')) {
+		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name,
firewall-cmd command does not exist");
+		return 0;
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to control $computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 process_post_load
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Performs the initial iptables firewall configuration after an
+               image is loaded:
+               * Performs all of the tasks done by
+                 iptables.pm::process_post_load except the pre-VCL 2.5 legacy
+                 cleanup tasks
+               * Removes the ssh protocol from the public zone
+
+=cut
+
+sub process_post_load {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module::OS::Linux::firewall/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my $computer_name = $self->data->get_computer_short_name();
+	
+	notify($ERRORS{'DEBUG'}, 0, "beginning firewalld post-load configuration on $computer_name");
+	
+	# Call subroutine in iptables.pm
+	return unless $self->SUPER::process_post_load();
+	
+#### Remove ssh from public zone
+###return unless $self->remove_service('public', 'ssh');
+	
+	$self->save_configuration();
+	
+	notify($ERRORS{'DEBUG'}, 0, "completed firewalld post-load configuration on $computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_service
+
+ Parameters  : $zone_name, $service_name
+ Returns     : boolean
+ Description : Removes a service from a firewalld zone.
+
+=cut
+
+sub remove_service {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($zone_name, $service_name) = @_;
+	if (!defined($zone_name)) {
+		notify($ERRORS{'WARNING'}, 0, "zone name argument was not specified");
+		return;
+	}
+	elsif (!defined($service_name)) {
+		notify($ERRORS{'WARNING'}, 0, "service name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --zone=$zone_name --remove-service=$service_name";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to remove '$service_name' service
from '$zone_name' zone on $computer_name: $command");
+		return;
+	}
+	elsif (grep(/NOT_ENABLED/, @$output)) {
+		notify($ERRORS{'DEBUG'}, 0, "'$service_name' service is not enabled in '$zone_name' zone
on $computer_name");
+		return 1;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to remove '$service_name' service from '$zone_name'
zone on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "removed '$service_name' service from '$zone_name' zone on $computer_name,
output:\n" . join("\n", @$output));
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_all_direct_rules
+
+ Parameters  : none
+ Returns     : array
+ Description : Calls 'firewall-cmd --permanent --direct --get-all-rules' and
+               returns an array of strings.
+
+=cut
+
+sub get_all_direct_rules {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --direct --get-all-rules";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve all firewalld direct
rules on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve all firewalld direct rules on $computer_name,
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return;
+	}
+	
+	# Rules should be in the format:
+	# ipv4 filter vcl-pre_capture 0 --jump ACCEPT --protocol tcp --match comment --comment 'VCL:
Allow traffic to SSH port 22 from any IP address (2017-04-07 17:19:21)' --match tcp --destination-port
22
+	# ipv4 filter INPUT 0 --jump vcl-pre_capture --match comment --comment 'VCL: jump to rules
added during the pre-capture stage (2017-04-07 17:19:21)'
+	my @rules = grep(/^(ipv4|ipv6|eb)/, @$output);
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved all firewalld direct rules defined on $computer_name:\n"
. join("\n", @rules));
+	return @rules;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_direct_chain_rules
+
+ Parameters  : $table_name, $chain_name
+ Returns     : array
+ Description : Calls 'firewall-cmd --permanent --direct --get-rules' and returns
+               an array of strings.
+
+=cut
+
+sub get_direct_chain_rules {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name) = @_;
+	if (!$table_name) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	elsif (!$chain_name) {
+		notify($ERRORS{'WARNING'}, 0, "chain name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --direct --get-rules ipv4 $table_name $chain_name";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to retrieve firewalld direct rules
defined for '$chain_name' chain in '$table_name' table on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to retrieve firewalld direct rules defined for '$chain_name'
chain in '$table_name' table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n"
. join("\n", @$output));
+		return;
+	}
+	
+	# All rule lines should begin with an integer:
+	#    0 --jump ACCEPT --source 10.25.7.2 --match comment --comment 'VCL: Allow traffic from
management node (2017-04-07 15:36:24)'
+	#    1 --jump ACCEPT --source 10.25.7.2
+	my @rules = grep(/^\d+/, @$output);
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved firewalld direct rules defined for '$chain_name'
chain in '$table_name' table on $computer_name:\n" . join("\n", @rules));
+	return @rules;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 save_configuration
+
+ Parameters  : none
+ Returns     : boolean
+ Description : Calls 'firewall-cmd --reload'.
+
+=cut
+
+sub save_configuration {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --reload";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to reload firewalld configuration
on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to reload firewalld configuration on $computer_name,
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "reloaded firewalld configuration on $computer_name");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Creates a new chain. Returns true if the chain was successfully
+               created or already exists.
+
+=cut
+
+sub create_chain {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name) = @_;
+	if (!defined($table_name)) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	elsif (!defined($chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "chain name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --direct --add-chain ipv4 $table_name $chain_name";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
+		return;
+	}
+	elsif (grep(/ALREADY_ENABLED/i, @$output)) {
+		notify($ERRORS{'OK'}, 0, "'$chain_name' chain in '$table_name' table already exists on
$computer_name");
+		return 1;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to create '$chain_name' chain in '$table_name' table
on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
+		return 0;
+	}
+	elsif (!grep(/success/, @$output)) {
+		notify($ERRORS{'WARNING'}, 0, "potentially failed to create '$chain_name' chain in '$table_name'
table on $computer_name, output does not contain 'success', exit status: $exit_status, command:\n$command\noutput:\n"
. join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "created '$chain_name' chain in '$table_name' table on $computer_name");
+		#$self->save_configuration();
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 remove_direct_chain_rules
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Flushes (deletes) rules from the specified chain.
+
+=cut
+
+sub remove_direct_chain_rules {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name) = @_;
+	if (!defined($table_name)) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	elsif (!defined($chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "chain name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	# !!! WARNING !!!
+	# DON'T USE --remove-rules
+	# With firewall-cmd version 0.4.3.2, this option removes rules from ALL direct chains, not
just the one specified
+	#my $command = "firewall-cmd --permanent --direct --remove-rules ipv4 $table_name $chain_name";
+	
+	my @rules = $self->get_direct_chain_rules($table_name, $chain_name);
+	for my $rule (@rules) {
+		# [--permanent] --direct --remove-rule { ipv4 | ipv6 | eb } table chain priority args
+		my $command = "firewall-cmd --permanent --direct --remove-rule ipv4 $table_name $chain_name
$rule";
+		my ($exit_status, $output) = $self->os->execute($command, 0);
+		if (!defined($output)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
+			return;
+		}
+		elsif ($exit_status ne '0') {
+			notify($ERRORS{'WARNING'}, 0, "failed to remove rule from '$chain_name' chain in '$table_name'
table on $computer_name: '$rule', exit status: $exit_status, command:\n$command\noutput:\n"
. join("\n", @$output));
+			return 0;
+		}
+		else {
+			notify($ERRORS{'OK'}, 0, "removed direct rule from '$chain_name' chain in '$table_name'
table on $computer_name: '$rule'");
+		}
+	}
+	
+	notify($ERRORS{'OK'}, 0, "removed all direct rules from '$chain_name' chain in '$table_name'
table on $computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Deletes an existing chain. Returns true if the chain was
+               successfully deleted or doesn't exist.
+
+=cut
+
+sub delete_chain {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name) = @_;
+	if (!defined($table_name)) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	elsif (!defined($chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "chain name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	# Delete all rules which reference the chain being deleted or else the chain can't be deleted
+	# Do this BEFORE checking if the chain exists to clean up leftover references in direct.xml
+	if (!$self->delete_chain_references($table_name, $chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' chain from '$table_name'
table on $computer_name, failed to delete all rules which reference the chain prior to deletion");
+		return;
+	}
+
+	$self->remove_direct_chain_rules($table_name, $chain_name) || return;
+	
+	my $command = "firewall-cmd --permanent --direct --remove-chain ipv4 $table_name $chain_name";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
+		return;
+	}
+	elsif (grep(/NOT_ENABLED/i, @$output)) {
+		notify($ERRORS{'OK'}, 0, "'$chain_name' chain in '$table_name' does not exist on $computer_name");
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete '$chain_name' chain in '$table_name' table
on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
+		return 0;
+	}
+	elsif (!grep(/success/, @$output)) {
+		notify($ERRORS{'WARNING'}, 0, "potentially failed to delete '$chain_name' chain in '$table_name'
table on $computer_name, output does not contain 'success', exit status: $exit_status, command:\n$command\noutput:\n"
. join("\n", @$output));
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "deleted '$chain_name' chain in '$table_name' table on $computer_name");
+		#$self->save_configuration();
+	}
+	
+	return $self->clean_direct_xml($table_name . '.*jump\s+' . $chain_name);
+	#$self->delete_chain_references($table_name, $chain_name);
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 clean_direct_xml
+
+ Parameters  : $regex_pattern
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub clean_direct_xml {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my $regex_pattern = shift;
+	if (!defined($regex_pattern)) {
+		notify($ERRORS{'WARNING'}, 0, "regex pattern argument was not supplied");
+		return;
+	}
+	
+	$self->os->firewall->save_configuration();
+	
+	my @keep_lines;
+	my @prune_lines;
+	my $file_path = '/etc/firewalld/direct.xml';
+	my @lines = $self->os->get_file_contents($file_path);
+	for my $line (@lines) {
+		if ($line =~ /$regex_pattern/i) {
+			push @prune_lines, $line;
+		}
+		else {
+			push @keep_lines, $line;
+		}
+	}
+	
+	if (@prune_lines) {
+		my $updated_contents = join("\n", @keep_lines);
+		notify($ERRORS{'DEBUG'}, 0, "pruning the following lines from $file_path matching pattern:
'$regex_pattern'\n" . join("\n", @prune_lines) . "\nnew file contents:\n$updated_contents");
+		return $self->os->create_text_file($file_path, $updated_contents);
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "no lines were pruned from $file_path matching pattern: '$regex_pattern'");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _insert_rule
+
+ Parameters  : $table_name, $chain_name, $argument_string
+ Returns     : boolean
+ Description : Executes the command to insert a firewalld direct rule. This is a
+               helper subroutine and should only be called by
+               iptable.pm::insert_rule.
+
+=cut
+
+sub _insert_rule {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name, $argument_string) = @_;
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --direct --add-rule ipv4 $table_name $chain_name
0 $argument_string";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to add direct firewalld rule to
$chain_name chain in $table_name table on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to add direct firewalld rule to $chain_name chain
in $table_name table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n"
. join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "added direct firewalld rule to $chain_name chain in $table_name
table on $computer_name, command: $command, output:\n" . join("\n", @$output));
+		#$self->save_configuration();
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _delete_rule
+
+ Parameters  : $table_name, $chain_name, $rule_specification_string
+ Returns     : boolean
+ Description : Deletes a firewalld direct rule. This should only used as a
+               helper subroutine.
+
+=cut
+
+sub _delete_rule {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name, $rule_specification_string) = @_;
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "firewall-cmd --permanent --direct --remove-rule ipv4 $table_name $chain_name
0 $rule_specification_string";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to delete firewalld direct rule
on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete firewalld direct rule on $computer_name,
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "deleted firewalld direct rule on $computer_name, command: '$command',
output:\n" . join("\n", @$output));
+		#$self->save_configuration();
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm?rev=1790637&r1=1790636&r2=1790637&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm Fri Apr  7 22:15:38
2017
@@ -139,7 +139,7 @@ sub process_post_load {
 		}
 	}
 	
-	# Create a chain and add a jump rule to INPUT
+	# Create a chain and add a jump rule to INPUT chain
 	$self->create_chain('filter', $post_load_chain_name);
 	if (!$self->insert_rule('filter', 'INPUT',
 		{
@@ -156,7 +156,7 @@ sub process_post_load {
 		notify($ERRORS{'WARNING'}, 0, "failed to complete firewall post-load configuration on $computer_name,
failed to create rule in INPUT chain to jump to '$post_load_chain_name' chain");
 		return;
 	}
-	
+
 	# Allow traffic from any of the management node IP addresses
 	if (!$self->insert_rule('filter', $post_load_chain_name,
 		{
@@ -174,7 +174,7 @@ sub process_post_load {
 		notify($ERRORS{'WARNING'}, 0, "failed to complete firewall post-load configuration on $computer_name,
failed to add rule allowing traffic from management node IP addresses to $post_load_chain_name
chain");
 		return;
 	}
-	
+
 	# Delete other vcl-* chains added by vcld
 	my $table_info = $self->get_table_info();
 	for my $chain_name (keys %$table_info) {
@@ -183,27 +183,29 @@ sub process_post_load {
 		}
 	}
 	
-	# Legacy code may have been used previously for a reservation, before an upgrade
-	# Clean up old connect method rules from the INPUT chain
-	# Delete all rules from INPUT chain matching connect method protocols and ports
-	$self->delete_connect_method_rules();
-	
-	# Delete all TCP/22 rules
-	# Images captured prior to VCL 2.5 are saved with an expicit TCP/22 allow rule from any
address
-	$self->delete_rules('filter', 'INPUT',
-		{
-			"match_extensions" => {
-				"tcp" => {
-					"dport" => 22,
+	if (!$self->isa('VCL::Module::OS::Linux::firewall::firewalld')) {
+		# Legacy code may have been used previously for a reservation, before an upgrade
+		# Clean up old connect method rules from the INPUT chain
+		# Delete all rules from INPUT chain matching connect method protocols and ports
+		$self->delete_connect_method_rules();
+		
+		# Delete all TCP/22 rules
+		# Images captured prior to VCL 2.5 are saved with an expicit TCP/22 allow rule from any
address
+		$self->delete_rules('filter', 'INPUT',
+			{
+				"match_extensions" => {
+					"tcp" => {
+						"dport" => 22,
+					},
 				},
-			},
-			"parameters" => {
-				"jump" => "ACCEPT",
-			},
-		}
-	);
-	
-	$self->save_configuration();
+				"parameters" => {
+					"jump" => "ACCEPT",
+				},
+			}
+		);
+		
+		$self->save_configuration();
+	}
 	
 	notify($ERRORS{'DEBUG'}, 0, "completed firewall post-load configuration on $computer_name");
 	return 1;
@@ -243,7 +245,7 @@ sub process_reserved {
 	my $reserved_chain_name = $self->get_reserved_chain_name();
 	
 	# Delete existing chain if one exists to prevent inconsistent results
-	# Create a chain and add a jump rule to INPUT
+	# Create a chain and add a jump rule to INPUT chain
 	$self->create_chain('filter', $reserved_chain_name);
 	if (!$self->insert_rule('filter', 'INPUT',
 		{
@@ -340,7 +342,7 @@ sub process_inuse {
 	my $reserved_chain_name = $self->get_reserved_chain_name();
 	
 	# Delete existing chain if one exists to prevent inconsistent results
-	# Create a chain and add a jump rule to INPUT
+	# Create a chain and add a jump rule to INPUT chain
 	$self->create_chain('filter', $inuse_chain_name);
 	if (!$self->insert_rule('filter', 'INPUT',
 		{
@@ -442,7 +444,7 @@ sub process_pre_capture {
 	
 	my $pre_capture_chain_name = $self->get_pre_capture_chain_name();
 	
-	# Create a chain and add a jump rule to INPUT
+	# Create a chain and add a jump rule to INPUT chain
 	if (!$self->create_chain('filter', $pre_capture_chain_name)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to complete firewall pre-capture configuration on
$computer_name, failed to create '$pre_capture_chain_name' chain");
 		return;
@@ -484,24 +486,26 @@ sub process_pre_capture {
 		return;
 	}
 	
-	# Delete all rules explicitly defined for any of the management node IP addresses
-	# Legacy firewall code would add rules directly to the filter/INPUT table for each management
node address
-	my @mn_ip_addresses = $self->mn_os->get_ip_addresses();
-	for my $mn_ip_address (@mn_ip_addresses) {
-		$self->delete_rules('filter', 'INPUT',
-			{
-				'parameters' => {
-					'source' => $mn_ip_address,
-				},
-			}
-		);
+	if (!$self->isa('VCL::Module::OS::Linux::firewall::firewalld')) {
+		# Delete all rules explicitly defined for any of the management node IP addresses
+		# Legacy firewall code would add rules directly to the filter/INPUT table for each management
node address
+		my @mn_ip_addresses = $self->mn_os->get_ip_addresses();
+		for my $mn_ip_address (@mn_ip_addresses) {
+			$self->delete_rules('filter', 'INPUT',
+				{
+					'parameters' => {
+						'source' => $mn_ip_address,
+					},
+				}
+			);
+		}
+		
+		# Legacy code may have been used previously for a reservation, before an upgrade
+		# Clean up old connect method rules from the INPUT chain
+		# Delete all rules from INPUT chain matching connect method protocols and ports
+		$self->delete_connect_method_rules();
 	}
 	
-	# Legacy code may have been used previously for a reservation, before an upgrade
-	# Clean up old connect method rules from the INPUT chain
-	# Delete all rules from INPUT chain matching connect method protocols and ports
-	$self->delete_connect_method_rules();
-	
 	# Delete other vcl-* chains added by vcld
 	my $table_info = $self->get_table_info();
 	for my $chain_name (keys %$table_info) {
@@ -547,7 +551,7 @@ sub process_cluster {
 	# This subroutine really should only need to be called once
 	$self->delete_chain('filter', $cluster_chain_name);
 	
-	# Create a chain and add a jump rule to INPUT
+	# Create a chain and add a jump rule to INPUT chain
 	if (!$self->create_chain('filter', $cluster_chain_name)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to complete firewall cluster configuration on $computer_name,
failed to create '$cluster_chain_name' chain");
 		return;
@@ -720,22 +724,101 @@ sub insert_rule {
 		return 1;
 	}
 	
-	my $command = "/sbin/iptables -t $table_name -I $chain_name";
+	# Convert the specification into valid iptables command arguments
+	my $argument_string = $self->get_insert_rule_argument_string($rule_specification_hashref);
+	if (!$argument_string) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add iptables rule to $chain_name chain in $table_name
table on $computer_name, rule specification hash reference could not be converted into an
iptables command argument string:\n" . format_data($rule_specification_hashref));
+		return;
+	}
+	
+	my $semaphore = $self->get_iptables_semaphore();
+	return $self->_insert_rule($table_name, $chain_name, $argument_string);
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 _insert_rule
+
+ Parameters  : $table_name, $chain_name, $argument_string
+ Returns     : boolean
+ Description : Executes the command to insert a rule. This is a helper
+               subroutine and should only be called by insert_rule.
+
+=cut
+
+sub _insert_rule {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name, $argument_string) = @_;
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "/sbin/iptables -t $table_name -I $chain_name $argument_string";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to add iptables rule to $chain_name chain in $table_name
table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "added iptables rule to $chain_name chain in $table_name table
on $computer_name, command: $command");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_insert_rule_argument_string
+
+ Parameters  : $rule_specification_hashref
+ Returns     : string
+ Description : 
+
+=cut
+
+sub get_insert_rule_argument_string {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($rule_specification_hashref) = @_;
+	if (!$rule_specification_hashref) {
+		notify($ERRORS{'WARNING'}, 0, "rule specification hash reference argument was not specified");
+		return;
+	}
+	elsif (!ref($rule_specification_hashref) || ref($rule_specification_hashref) ne 'HASH')
{
+		notify($ERRORS{'WARNING'}, 0, "rule specification argument is not a hash reference:\n"
. format_data($rule_specification_hashref));
+		return;
+	}
+	elsif (!scalar(keys(%$rule_specification_hashref))) {
+		notify($ERRORS{'WARNING'}, 0, "rule specification argument does not contain any keys");
+		return;
+	}
+	
+	my $argument_string;
 	
-	# Add the parameters to the command
+	# Add the parameters to the arguments string
 	for my $parameter (sort keys %{$rule_specification_hashref->{parameters}}) {
 		my $value = $rule_specification_hashref->{parameters}{$parameter};
 		
 		if ($parameter =~ /^\!/) {
-			$command .= " !";
+			$argument_string .= "! ";
 			$parameter =~ s/^\!//;
 		}
-		$command .= " --$parameter $value";
+		$argument_string .= "--$parameter $value ";
 	}
 	
-	# Add the match extension to the command
+	# Add the match extension to the arguments string
 	for my $match_extension (sort keys %{$rule_specification_hashref->{match_extensions}})
{
-		$command .= " --match $match_extension";
+		$argument_string .= "--match $match_extension ";
 		for my $option (sort keys %{$rule_specification_hashref->{match_extensions}{$match_extension}})
{
 			my $value = $rule_specification_hashref->{match_extensions}{$match_extension}{$option};
 			
@@ -744,40 +827,28 @@ sub insert_rule {
 			}
 			
 			if ($option =~ /^\!/) {
-				$command .= " !";
+				$argument_string .= "! ";
 				$option =~ s/^\!//;
 			}
 			
-			$command .= " ";
-			$command .= "--$option " if $option;
-			$command .= $value;
+			$argument_string .= "--$option " if $option;
+			$argument_string .= "$value ";
 		}
 	}
 	
-	# Add the target extensions to the command
+	# Add the target extensions to the arguments string
 	for my $target_extension (sort keys %{$rule_specification_hashref->{target_extensions}})
{
-		$command .= " --jump $target_extension";
+		$argument_string .= "--jump $target_extension ";
 		for my $option (sort keys %{$rule_specification_hashref->{target_extensions}{$target_extension}})
{
 			my $value = $rule_specification_hashref->{target_extensions}{$target_extension}{$option};
-			$command .= " --$option " if $option;
-			$command .= $value;
+			$argument_string .= "--$option " if $option;
+			$argument_string .= "$value ";
 		}
 	}
 	
-	my $semaphore = $self->get_iptables_semaphore();
-	my ($exit_status, $output) = $self->os->execute($command, 0);
-	if (!defined($output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute command on $computer_name: $command");
-		return;
-	}
-	elsif ($exit_status ne '0') {
-		notify($ERRORS{'WARNING'}, 0, "failed to add iptables rule to $chain_name chain in $table_name
table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
-		return 0;
-	}
-	else {
-		notify($ERRORS{'OK'}, 0, "added iptables rule to $chain_name chain in $table_name table
on $computer_name, command: $command");
-		return 1;
-	}
+	$argument_string =~ s/\s+$//g;
+	
+	return $argument_string;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -865,7 +936,7 @@ sub get_matching_rules {
 		notify($ERRORS{'WARNING'}, 0, "failed to determine if any rules match on $computer_name,
attempt to collapse the rule specification hash reference argument produced a result with
no keys:\n" . format_data($rule_specification_hashref));
 		return;
 	}
-	notify($ERRORS{'DEBUG'}, 0, "checking if $chain_name chain in $table_name table on $computer_name
has any rules matching specifications:\n" . format_data($collapsed_specification));
+	#notify($ERRORS{'DEBUG'}, 0, "checking if $chain_name chain in $table_name table on $computer_name
has any rules matching specifications:\n" . format_data($collapsed_specification));
 	
 	# Some iptables options may take multiple forms
 	# Attempt to try all forms
@@ -925,7 +996,7 @@ sub get_matching_rules {
 	}
 	
 	my $matching_rule_count = scalar(@matching_rules);
-	notify($ERRORS{'DEBUG'}, 0, "found $matching_rule_count matching rule" . ($matching_rule_count
== 1 ? '' : 's'));
+	#notify($ERRORS{'DEBUG'}, 0, "found $matching_rule_count matching rule" . ($matching_rule_count
== 1 ? '' : 's')) if $matching_rule_count;
 	return @matching_rules;
 }
 
@@ -1000,25 +1071,50 @@ sub delete_rules {
 		
 		notify($ERRORS{'DEBUG'}, 0, "attempting to delete rule on $computer_name: $rule_specification_string");
 		my $semaphore = $self->get_iptables_semaphore();
-		my $command = "/sbin/iptables --delete $chain_name -t $table_name $rule_specification_string";
-		my ($exit_status, $output) = $self->os->execute($command, 0);
-		if (!defined($output)) {
-			notify($ERRORS{'WARNING'}, 0, "failed to execute command on $computer_name: $command");
-			return;
-		}
-		elsif ($exit_status ne '0') {
-			notify($ERRORS{'WARNING'}, 0, "failed to delete rule on $computer_name, exit status: $exit_status,
command:\n$command\noutput:\n" . join("\n", @$output));
-			return;
-		}
-		else {
-			notify($ERRORS{'OK'}, 0, "deleted rule on $computer_name with specification: '$rule_specification_string'");
-		}
+		$self->_delete_rule($table_name, $chain_name, $rule_specification_string) || return;
 	}
 	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 _delete_rule
+
+ Parameters  : $table_name, $chain_name, $rule_specification_string
+ Returns     : boolean
+ Description : Executes the command to delete a rule. This is a helper
+               subroutine and should only be called by delete_rules.
+
+=cut
+
+sub _delete_rule {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called
as a class method");
+		return 0;
+	}
+	
+	my ($table_name, $chain_name, $rule_specification_string) = @_;
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "/sbin/iptables --delete $chain_name -t $table_name $rule_specification_string";
+	my ($exit_status, $output) = $self->os->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command on $computer_name: $command");
+		return;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete rule on $computer_name, exit status: $exit_status,
command:\n$command\noutput:\n" . join("\n", @$output));
+		return;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "deleted rule on $computer_name with specification: '$rule_specification_string'");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 delete_connect_method_rules
 
  Parameters  : none
@@ -1214,7 +1310,7 @@ sub delete_chain_references {
 	for my $referencing_chain_name (keys %$table_info) {
 		for my $rule (@{$table_info->{$referencing_chain_name}{rules}}) {
 			my $rule_specification_string = $rule->{rule_specification};
-			if ($rule_specification_string =~ /-j $chain_name(\s|$)/) {
+			if ($rule_specification_string =~ /(-j|--jump) $chain_name(\s|$)/) {
 				notify($ERRORS{'DEBUG'}, 0, "rule in '$table_name' table references '$chain_name' chain,
referencing chain: $referencing_chain_name, rule specification: $rule_specification_string");
 				if (!$self->delete_rules($table_name, $referencing_chain_name, {'rule_specification'
=> $rule_specification_string})) {
 					return;
@@ -1325,13 +1421,7 @@ sub flush_chain {
 	
 	my $computer_name = $self->data->get_computer_hostname();
 	
-	my $command = "/sbin/iptables --flush";
-	my $chain_text = 'all chains';
-	if ($chain_name ne '*') {
-		$chain_text = "'$chain_name' chain";
-		$command .= " $chain_name";
-	}
-	$command .= " --table $table_name";
+	my $command = "/sbin/iptables --flush $chain_name --table $table_name";
 	
 	my $semaphore = $self->get_iptables_semaphore();
 	my ($exit_status, $output) = $self->os->execute($command, 0);
@@ -1340,11 +1430,11 @@ sub flush_chain {
 		return;
 	}
 	elsif ($exit_status ne '0') {
-		notify($ERRORS{'WARNING'}, 0, "failed to flush $chain_text in '$table_name' table on $computer_name,
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		notify($ERRORS{'WARNING'}, 0, "failed to flush '$chain_name' chain in '$table_name' table
on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n",
@$output));
 		return 0;
 	}
 	else {
-		notify($ERRORS{'OK'}, 0, "flushed $chain_text in '$table_name' table on $computer_name");
+		notify($ERRORS{'OK'}, 0, "flushed '$chain_name' chain in '$table_name' table on $computer_name");
 		return 1;
 	}
 }
@@ -1353,7 +1443,7 @@ sub flush_chain {
 
 =head2 get_table_info
 
- Parameters  : $table_name (optional)
+ Parameters  : $table_name, $no_cache
  Returns     : boolean
  Description : Retrieves the configuration of an iptables table and constructs a
                hash reference. Information from the 'filter' table is returned
@@ -1409,7 +1499,13 @@ sub get_table_info {
 		return 0;
 	}
 	
-	my $table_name = shift || 'filter';
+	my ($table_name, $no_cache) = @_;
+	
+	$table_name = 'filter' unless $table_name;
+	
+	if (!$no_cache && defined($self->{table_info}) && defined($self->{table_info}{$table_name}))
{
+		return $self->{table_info}{$table_name};
+	}
 	
 	$ENV{iptables_get_table_info_count}{$table_name}++;
 	
@@ -1426,9 +1522,44 @@ sub get_table_info {
 		notify($ERRORS{'WARNING'}, 0, "failed to list rules from '$table_name' table on $computer_name,
exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
 		return 0;
 	}
-
+	
+	my @lines = @$output;
+	
+	if ($self->can('get_all_direct_rules')) {
+		# Convert:
+		#    ipv4 filter vcl-pre_capture 0 --jump ACCEPT --protocol tcp --match comment --comment
'VCL: ...' --match tcp --destination-port 22
+		# To:
+		#    -A vcl-pre_capture -p tcp -m comment --comment "VCL: ..." -m tcp --dport 22 -j ACCEPT
+		DIRECT_RULE: for my $direct_rule ($self->get_all_direct_rules()) {
+			my ($rule_protocol, $rule_table, $rule_chain, $rule_priority, $rule_specification) = $direct_rule
=~
+				/^
+				(\S+)\s+
+				(\S+)\s+
+				(\S+)\s+
+				(\d+)\s+
+				(\S.*)
+				$/x
+			;
+			if (!defined($rule_specification)) {
+				notify($ERRORS{'WARNING'}, 0, "failed to parse firewalld direct rule: $direct_rule");
+				next DIRECT_RULE;
+			}
+			elsif ($rule_table ne $table_name) {
+				notify($ERRORS{'DEBUG'}, 0, "ignoring rule, table does not match '$table_name': $direct_rule");
+				next DIRECT_RULE;
+			}
+			
+			my $converted_rule = "-A $rule_chain $rule_specification";
+			notify($ERRORS{'DEBUG'}, 0, "converted iptables direct rule to iptables format:\n" .
+				"direct rule     : $direct_rule\n" .
+				"iptables format : $converted_rule"
+			);
+			push @lines, $converted_rule;
+		}
+	}
+	
 	my $table_info = {};
-	LINE: for my $line (@$output) {
+	LINE: for my $line (@lines) {
 		# Split the rule, samples:
 		#    -P OUTPUT ACCEPT
 		#    -N vcld-3115
@@ -1528,13 +1659,27 @@ sub get_table_info {
 				$rule->{parameters}{$target_parameter} = $target;
 				
 				my $target_extension_option_name;
-				my @target_extension_option_sections = split(/\s+/, $target_extension_option_string);
+				
+				# Need to split line not just by spaces, but also find sections enclosed in quotes:
+				#    -j REJECT --reject-with icmp-host-prohibited
+				#    -j LOG --log-prefix "IN_public_DROP: "
+				my @target_extension_option_sections = $target_extension_option_string =~
+				/
+					(
+						['"][^'"]*['"]
+						|
+						[^\s]+
+					)
+				/gx;
+				
 				TARGET_OPTION_SECTION: for my $target_extension_option_section (@target_extension_option_sections)
{
 					# Stop parsing if the start of a match extension specification if found
 					if ($target_extension_option_section =~ /^(-m|--match)$/) {
 						last TARGET_OPTION_SECTION;
 					}
 					
+					
+					
 					# Check if this is the beginning of a target extension option
 					if ($target_extension_option_section =~ /^[-]+(\w[\w-]+)/) {
 						$target_extension_option_name = $1;
@@ -1544,8 +1689,9 @@ sub get_table_info {
 					elsif (!$target_extension_option_name) {
 						# If here, the section should be a target extension option value
 						notify($ERRORS{'WARNING'}, 0, "failed to parse iptables rule, target extension option
name was not detected before this section: '$target_extension_option_section'\n" .
-							"iptables command: $line\n" .
-							"preceeding target parameter: $target_parameter --> $target"
+							"output line: $line\n" .
+							"preceeding target parameter: $target_parameter\n" .
+							"target value: $target"
 						);
 						next LINE;
 					}
@@ -1572,8 +1718,20 @@ sub get_table_info {
 			
 			
 			# The only text remaining in $rule_specification_string should be match extension information
-			# Split the remaining string by spaces
-			my @match_extension_sections = split(/\s+/, $rule_specification_string);
+			
+			# --match comment--comment 'my comment'
+			# --match tcp--destination-port
+			$rule_specification_string =~ s/(--match [^\s-]+)--/$1 --/g;
+			
+			# Split the remaining string by spaces or sections enclosed in quotes
+			my @match_extension_sections = $rule_specification_string =~
+				/
+					(
+						['"][^'"]*['"]
+						|
+						[^\s]+
+					)
+				/gx;
 			
 			# Match extensions will be in the form:
 			# -m,--match <module> [!] -<x>,--<option> <value> [[!] -<x>,--<option>
<value>...]
@@ -1582,6 +1740,8 @@ sub get_table_info {
 			my $match_extension_option_inverted = 0;
 			my $comment;
 			
+			
+			
 			MATCH_EXTENSION_SECTION: for my $match_extension_section (@match_extension_sections) {
 				next MATCH_EXTENSION_SECTION if !$match_extension_section;
 				
@@ -1660,6 +1820,7 @@ sub get_table_info {
 		}
 	}
 	
+	$self->{table_info}{$table_name} = $table_info;
 	#notify($ERRORS{'DEBUG'}, 0, "retrieved rules from iptables $table_name table from $computer_name:\n"
. format_data($table_info));
 	return $table_info;
 }



Mime
View raw message