You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2014/12/09 22:13:08 UTC

svn commit: r1644186 - in /vcl/trunk/managementnode/lib/VCL/Module/OS/Linux: firewall/iptables.pm init.pm

Author: arkurth
Date: Tue Dec  9 21:13:08 2014
New Revision: 1644186

URL: http://svn.apache.org/r1644186
Log:
VCL-174
Set svn properties on iptables.pm and init.pm.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm   (contents, props changed)
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm   (contents, props changed)

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=1644186&r1=1644185&r2=1644186&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 Tue Dec  9 21:13:08 2014
@@ -1,882 +1,882 @@
-#!/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::iptables.pm
-
-=head1 DESCRIPTION
-
- This module provides VCL support for iptables-based firewalls.
-
-=cut
-
-##############################################################################
-package VCL::Module::OS::Linux::firewall::iptables;
-
-# Specify the lib path using FindBin
-use FindBin;
-use lib "$FindBin::Bin/../../../../..";
-
-# Configure inheritance
-use base qw(VCL::Module::OS::Linux);
-
-# Specify the version of this module
-our $VERSION = '2.3';
-
-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 || {};
-	
-	# Check if base_package argument was specified
-	# This is necessary for ManagementNode OS objects to work
-	# Otherwise the base Linux.pm subroutines would be used instead of ManagementNode.pm
-	if (defined($arguments->{base_package})) {
-		notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . $ISA[0] . " --> $arguments->{base_package}");
-		@ISA = ($arguments->{base_package});
-	}
-	
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to control $computer_name");
-	
-	if (!$self->service_exists('iptables')) {
-		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name, iptables service does not exist");
-		return 0;
-	}
-	
-	notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to control $computer_name");
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 insert_rule
-
- Parameters  : none
- Returns     : boolean
- Description : 
-
-=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 $arguments = shift;
-	if (!$arguments) {
-		notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
-		return;
-	}
-	elsif (!ref($arguments) || ref($arguments) ne 'HASH') {
-		notify($ERRORS{'WARNING'}, 0, "argument is not a hash reference");
-		return;
-	}
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my $command = '/sbin/iptables';
-	
-	# Add the table argument if specified
-	if ($arguments->{table}) {
-		$command .= " -t $arguments->{table}";
-	}
-	
-	# Get the chain argument
-	my $chain = $arguments->{chain};
-	if (!defined($chain)) {
-		notify($ERRORS{'WARNING'}, 0, "chain argument was not specified:\n" . format_data($arguments));
-		return;
-	}
-	$command .= " -I $chain";
-	
-	# Add the parameters to the command
-	for my $parameter (sort keys %{$arguments->{parameters}}) {
-		my $value = $arguments->{parameters}{$parameter};
-		$command .= " --$parameter $value";
-	}
-	
-	# Add the match extension to the command
-	for my $match_extension (sort keys %{$arguments->{match_extensions}}) {
-		$command .= " --match $match_extension";
-		for my $option (sort keys %{$arguments->{match_extensions}{$match_extension}}) {
-			my $value = $arguments->{match_extensions}{$match_extension}{$option};
-			
-			if ($option =~ /(comment)/) {
-				$value = "\"$value\"";
-			}
-			
-			$command .= " --$option $value";
-		}
-	}
-	
-	# Add the target extensions to the command
-	for my $target_extension (sort keys %{$arguments->{target_extensions}}) {
-		$command .= " --jump $target_extension";
-		for my $option (sort keys %{$arguments->{target_extensions}{$target_extension}}) {
-			my $value = $arguments->{target_extensions}{$target_extension}{$option};
-			$command .= " --$option $value";
-		}
-	}
-	
-	my ($exit_status, $output) = $self->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 add iptables rule 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 on $computer_name, command: $command");
-		return 1;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 delete_rule
-
- Parameters  : hash reference
-               -or-
-					$table_name, $chain_name, $rule_specification
- Returns     : boolean
- Description : Deletes a rule.
-
-=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 $argument = shift;
-	if (!$argument) {
-		notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
-		return;
-	}
-	
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my $command = '/sbin/iptables';
-	
-	
-	if (ref($argument) && ref($argument) eq 'HASH') {
-		# Add the table argument if specified
-		if ($argument->{table}) {
-			$command .= " -t $argument->{table}";
-		}
-		
-		# Get the chain argument
-		my $chain = $argument->{chain};
-		if (!defined($chain)) {
-			notify($ERRORS{'WARNING'}, 0, "chain argument was not specified:\n" . format_data($argument));
-			return;
-		}
-		$command .= " -D $chain";
-		
-		# Add the parameters to the command
-		for my $parameter (sort keys %{$argument->{parameters}}) {
-			my $value = $argument->{parameters}{$parameter};
-			$command .= " --$parameter $value";
-		}
-		
-		# Add the match extension to the command
-		for my $match_extension (sort keys %{$argument->{match_extensions}}) {
-			$command .= " --match $match_extension";
-			for my $option (sort keys %{$argument->{match_extensions}{$match_extension}}) {
-				my $value = $argument->{match_extensions}{$match_extension}{$option};
-				
-				if ($option =~ /(comment)/) {
-					$value = "\"$value\"";
-				}
-				
-				$command .= " --$option $value";
-			}
-		}
-		
-		# Add the target extensions to the command
-		for my $target_extension (sort keys %{$argument->{target_extensions}}) {
-			$command .= " --jump $target_extension";
-			for my $option (sort keys %{$argument->{target_extensions}{$target_extension}}) {
-				my $value = $argument->{target_extensions}{$target_extension}{$option};
-				$command .= " --$option $value";
-			}
-		}
-	}
-	elsif (my $type = ref($argument)) {
-		notify($ERRORS{'WARNING'}, 0, "argument $type reference not supported, argument must only be a HASH reference or scalar");
-		return;
-	}
-	else {
-		my $table_name = $argument;
-		my ($chain_name, $specification) = @_;
-		if (!defined($chain_name) || !defined($specification)) {
-			notify($ERRORS{'WARNING'}, 0, "1st argument is a scalar, 2nd chain name and 3rd rule specification arguments not provided");
-			return;
-		}
-		$command .= " -D $chain_name -t $table_name $specification";
-	}
-	
-	my ($exit_status, $output) = $self->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 delete iptables rule on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
-		return 0;
-	}
-	else {
-		notify($ERRORS{'OK'}, 0, "deleted iptables rule on $computer_name, command: $command");
-		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 = "/sbin/iptables --new-chain $chain_name --table $table_name";
-	my ($exit_status, $output) = $self->execute($command, 0);
-	if (!defined($output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
-		return;
-	}
-	elsif (grep(/already exists/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;
-	}
-	else {
-		notify($ERRORS{'OK'}, 0, "created '$chain_name' chain in '$table_name' table on $computer_name");
-		return 1;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 delete_chain
-
- Parameters  : $table_name, $chain_name
- Returns     : boolean
- Description : Deletes the specified chain from the specified table. All rules
-               which exist in the chain or reference the chain are deleted prior
-               to deletion of the chain.
-
-=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();
-	
-	my $table_info = $self->get_table_info($table_name);
-	if (!defined($table_info->{$chain_name})) {
-		notify($ERRORS{'DEBUG'}, 0, "'$chain_name' chain in '$table_name' table does not exist on $computer_name");
-		return 1;
-	}
-	
-	# Flush the chain first - delete will fail if the chain still contains rules
-	if (!$self->flush_chain($table_name, $chain_name)) {
-		notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' chain from '$table_name' table on $computer_name, failed to flush chain prior to deletion");
-		return;
-	}
-	
-	# Delete all rules which reference the chain being deleted or else the chain can't be deleted
-	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;
-	}
-	
-	my $command = "/sbin/iptables --delete-chain $chain_name --table $table_name";
-	my ($exit_status, $output) = $self->execute($command, 0);
-	if (!defined($output)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
-		return;
-	}
-	elsif (grep(/Too many links/i, @$output)) {
-		notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' chain from '$table_name' table on $computer_name, the chain is referenced by another rule");
-		return 0;
-	}
-	elsif ($exit_status ne '0') {
-		notify($ERRORS{'WARNING'}, 0, "failed to delete '$chain_name' chain from '$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, "deleted '$chain_name' chain from '$table_name' table on $computer_name");
-		return 1;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 delete_chain_references
-
- Parameters  : $table_name, $referenced_chain_name
- Returns     : boolean
- Description : Checks all chains in the specified table for references to the
-               $referenced_chain_name argument. If found, the referencing rules
-               are deleted.
-
-=cut
-
-sub delete_chain_references {
-	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, $referenced_chain_name) = @_;
-	if (!defined($table_name)) {
-		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
-		return;
-	}
-	elsif (!defined($referenced_chain_name)) {
-		notify($ERRORS{'WARNING'}, 0, "referenced chain name argument was not specified");
-		return;
-	}
-	
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my $table_info = $self->get_table_info($table_name);
-	for my $referencing_chain_name (keys %$table_info) {
-		for my $rule_specification (@{$table_info->{$referencing_chain_name}{rules}}) {
-			if ($rule_specification =~ /-j $referenced_chain_name(\s|$)/) {
-				notify($ERRORS{'DEBUG'}, 0, "rule in '$table_name' table references '$referenced_chain_name' chain, referencing chain: $referencing_chain_name, rule specification: $rule_specification");
-				if (!$self->delete_rule($table_name, $referencing_chain_name, $rule_specification)) {
-					return;
-				}
-			}
-		}
-	}
-	
-	notify($ERRORS{'DEBUG'}, 0, "deleted all rules in '$table_name' table referencing '$referenced_chain_name' chain on $computer_name");
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 flush_chain
-
- Parameters  : $table_name, $chain_name
- Returns     : boolean
- Description : Flushes (deletes) rules from the specified chain.
-
-=cut
-
-sub flush_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 = "/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 ($exit_status, $output) = $self->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 flush $chain_text 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");
-		return 1;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 get_table_info
-
- Parameters  : $table_name, $chain_name (optional)
- Returns     : boolean
- Description : 
-
-=cut
-
-sub get_table_info {
-	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;
-	}
-	
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my $command = "/sbin/iptables --list-rules";
-	my $chain_text = '';
-	if (defined($chain_name)) {
-		$command .= " $chain_name";
-		$chain_text = "of '$chain_name' chain ";
-	}
-	$command .= " --table $table_name";
-	
-	my ($exit_status, $output) = $self->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 list rules " . $chain_text . "from '$table_name' table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
-		return 0;
-	}
-	
-	my $table_info = {};
-	for my $line (@$output) {
-		my ($iptables_command, $chain_name, $specification) = $line =~ /^(-\w) ([^ ]+)\s*(.*)$/;
-		if (!defined($iptables_command) || !defined($chain_name)) {
-			notify($ERRORS{'WARNING'}, 0, "failed to parse line: '$line'\ncommand: $command");
-			next;
-		}
-		$specification = '' unless defined($specification);
-		
-		if ($iptables_command eq '-P') {
-			# -P, --policy chain target (Set  the policy for the chain to the given target)
-			$table_info->{$chain_name}{policy} = $specification;
-		}
-		elsif ($iptables_command eq '-N') {
-			# -N, --new-chain chain
-			$table_info->{$chain_name} = {} unless defined($table_info->{$chain_name});
-		}
-		elsif ($iptables_command eq '-A') {
-			# -A, --append chain rule-specification
-			push @{$table_info->{$chain_name}{rules}}, $specification;
-		}
-		else {
-			notify($ERRORS{'WARNING'}, 0, "'$iptables_command' command is not supported: $line");
-		}
-	}
-	
-	notify($ERRORS{'DEBUG'}, 0, "retrieved rules " . $chain_text . "from '$table_name' table from $computer_name:\n" . format_data($table_info));
-	return $table_info;
-}
-
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 configure_nat
-
- Parameters  : none
- Returns     : boolean
- Description : 
-
-=cut
-
-sub configure_nat {
-	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 $reservation_id = $self->data->get_reservation_id();
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my $table_info = $self->get_table_info('nat');
-	if (!$table_info) {
-		notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on $computer_name, nat table info could not be retrieved");
-		return;
-	}
-	elsif (!defined($table_info->{PREROUTING})) {
-		notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a PREROUTING chain:\n" . format_data($table_info));
-		return;
-	}
-	elsif (!defined($table_info->{POSTROUTING})) {
-		notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a POSTROUTING chain:\n" . format_data($table_info));
-		return;
-	}
-	
-	# Check if NAT has previously been configured
-	my $nat_previously_configured = 0;
-	for my $rule_specification (@{$table_info->{POSTROUTING}{rules}}) {
-		if ($rule_specification =~ /MASQUERADE/) {
-			$nat_previously_configured = 1;
-			notify($ERRORS{'DEBUG'}, 0, "POSTROUTING chain in nat table contains a MASQUERADE rule, assuming NAT has already been configured: $rule_specification");
-			last;
-		}
-	}
-	if (!$nat_previously_configured) {
-		my $private_interface_name = $self->get_private_interface_name();
-		my $private_ip_address = $self->get_private_ip_address();
-		my $public_interface_name = $self->get_public_interface_name();
-		my $public_ip_address = $self->get_public_ip_address();
-		
-		my $natport_ranges_variable = get_variable('natport_ranges') || '49152-65535';
-		my $destination_ports = '';
-		for my $natport_range (split(/[,;]+/, $natport_ranges_variable)) {
-			my ($start_port, $end_port) = $natport_range =~ /(\d+)-(\d+)/g;
-			if (!defined($start_port)) {
-				notify($ERRORS{'WARNING'}, 0, "unable to parse NAT port range: '$natport_range'");
-				next;
-			}
-			$destination_ports .= "," if ($destination_ports);
-			$destination_ports .= "$start_port:$end_port";
-		}
-		
-		
-		if (!$self->insert_rule({
-			'table' => 'nat',
-			'chain' => 'POSTROUTING',
-			'parameters' => {
-				'out-interface' => $private_interface_name,
-				'jump' => 'MASQUERADE',
-			},
-			'match_extensions' => {
-				'comment' => {
-					'comment' => "change IP of outbound private $private_interface_name packets to NAT host private IP address $private_interface_name",
-				},
-			},
-		})) {
-			return;
-		}
-		
-		if (!$self->insert_rule({
-			'chain' => 'INPUT',
-			'parameters' => {
-				'in-interface' => $public_interface_name,
-				'destination' => $public_ip_address,
-				'jump' => 'ACCEPT',
-				'protocol' => 'tcp',
-			},
-			'match_extensions' => {
-				'state' => {
-					'state' => 'RELATED,ESTABLISHED',
-				},
-				'multiport' => {
-					'destination-ports' => $destination_ports,
-				},
-			},
-		})) {
-			return;
-		}
-		
-		if (!$self->insert_rule({
-			'chain' => 'INPUT',
-			'parameters' => {
-				'in-interface' => $public_interface_name,
-				'destination' => $public_ip_address,
-				'jump' => 'ACCEPT',
-				'protocol' => 'udp',
-			},
-			'match_extensions' => {
-				'state' => {
-					'state' => 'RELATED,ESTABLISHED',
-				},
-				'multiport' => {
-					'destination-ports' => $destination_ports,
-				},
-			},
-		})) {
-			return;
-		}
-		
-		if (!$self->insert_rule({
-			'chain' => 'FORWARD',
-			'parameters' => {
-				'in-interface' => $public_interface_name,
-				'out-interface' => $private_interface_name,
-				'jump' => 'ACCEPT',
-			},
-			'match_extensions' => {
-				'state' => {
-					'state' => 'NEW,RELATED,ESTABLISHED',
-				},
-				'comment' => {
-					'comment' => "forward inbound packets from public $public_interface_name to private $private_interface_name",
-				},
-			},	
-		})) {
-			return;
-		}
-		
-		if (!$self->insert_rule({
-			'chain' => 'FORWARD',
-			'parameters' => {
-				'in-interface' => $private_interface_name,
-				'out-interface' => $public_interface_name,
-				'jump' => 'ACCEPT',
-			},
-			'match_extensions' => {
-				'comment' => {
-					'comment' => "forward outbound packets from private $private_interface_name to public $public_interface_name",
-				},
-			},
-		})) {
-			return;
-		}
-		
-		#if (!$self->insert_rule({
-		#	'chain' => 'INPUT',
-		#	'parameters' => {
-		#		'in-interface' => $public_interface_name,
-		#	},
-		#	'target_extensions' => {
-		#		'REJECT' => {
-		#			'reject-with' => "icmp-host-prohibited",
-		#		},
-		#	},
-		#})) {
-		#	return;
-		#}
-		#
-		#if (!$self->insert_rule({
-		#	'chain' => 'FORWARD',
-		#	'target_extensions' => {
-		#		'REJECT' => {
-		#			'reject-with' => "icmp-host-prohibited",
-		#		},
-		#	},
-		#})) {
-		#	return;
-		#}
-	}
-	
-	# Check if chain for reservation has already been created
-	if (defined($table_info->{$reservation_id})) {
-		notify($ERRORS{'DEBUG'}, 0, "'$reservation_id' chain already exists in nat table on $computer_name for this reservation");
-	}
-	else {
-		if (!$self->create_chain('nat', $reservation_id)) {
-			notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on $computer_name, '$reservation_id' chain could not be created in nat table for this reservation");
-			return;
-		}
-	}
-	
-	# Check if rule to jump to reservation's chain already exists in the PREROUTING table
-	my $jump_previously_configured = 0;
-	for my $rule_specification (@{$table_info->{PREROUTING}{rules}}) {
-		if ($rule_specification =~ /-j $reservation_id(\s|$)/) {
-			$jump_previously_configured = 1;
-			notify($ERRORS{'DEBUG'}, 0, "PREROUTING chain in nat table on $computer_name already contains a rule to jump to '$reservation_id' chain: $rule_specification");
-			last;
-		}
-	}
-	if (!$jump_previously_configured) {
-		if (!$self->insert_rule({
-			'table' => 'nat',
-			'chain' => 'PREROUTING',
-			'parameters' => {
-				'jump' => $reservation_id,
-			},
-		})) {
-			notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, failed to create rule in PREROUTING chain in nat table to jump to '$reservation_id' chain");
-			return;
-		}
-	}
-	
-	notify($ERRORS{'DEBUG'}, 0, "successfully configured NAT on $computer_name for reservation");
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 add_nat_port_forward
-
- Parameters  : $protocol, $source_port, $destination_ip_address, $destination_port, $chain_name (optional)
- Returns     : boolean
- Description : 
-
-=cut
-
-sub add_nat_port_forward {
-	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 $reservation_id = $self->data->get_reservation_id();
-	my $computer_name = $self->data->get_computer_hostname();
-	
-	my ($protocol, $source_port, $destination_ip_address, $destination_port, $chain_name) = @_;
-	if (!defined($protocol)) {
-		notify($ERRORS{'WARNING'}, 0, "protocol argument was not provided");
-		return;
-	}
-	elsif (!defined($source_port)) {
-		notify($ERRORS{'WARNING'}, 0, "source port argument was not provided");
-		return;
-	}
-	elsif (!defined($destination_ip_address)) {
-		notify($ERRORS{'WARNING'}, 0, "destination IP address argument was not provided");
-		return;
-	}
-	elsif (!defined($destination_port)) {
-		notify($ERRORS{'WARNING'}, 0, "destination port argument was not provided");
-		return;
-	}
-	$chain_name = 'PREROUTING' unless defined $chain_name;
-	
-	$protocol = lc($protocol);
-	
-	my $public_interface_name = $self->get_public_interface_name();
-	my $public_ip_address = $self->get_public_ip_address();
-	
-	if ($self->insert_rule({
-		'table' => 'nat',
-		'chain' => $chain_name,
-		'parameters' => {
-			'protocol' => $protocol,
-			'in-interface' => $public_interface_name,
-			'destination' => $public_ip_address,
-		},
-		'match_extensions' => {
-			'comment' => {
-				'comment' => "change destination address: $public_ip_address:$source_port --> $destination_ip_address:$destination_port ($protocol)",
-			},
-			$protocol => {
-				'destination-port' => $source_port,
-			},
-		},
-		'target_extensions' => {
-			'DNAT' => {
-				'to-destination' => "$destination_ip_address:$destination_port",
-			},
-		},
-	})) {
-		notify($ERRORS{'OK'}, 0, "added NAT port forward on $computer_name: $public_interface_name:$source_port --> $destination_ip_address:$destination_port");
-		return 1;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to add NAT port forward on $computer_name: $public_interface_name:$source_port --> $destination_ip_address:$destination_port");
-		return;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-1;
-__END__
-
-=head1 SEE ALSO
-
-L<http://cwiki.apache.org/VCL/>
-
-=cut
+#!/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::iptables.pm
+
+=head1 DESCRIPTION
+
+ This module provides VCL support for iptables-based firewalls.
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Linux::firewall::iptables;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux);
+
+# Specify the version of this module
+our $VERSION = '2.3';
+
+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 || {};
+	
+	# Check if base_package argument was specified
+	# This is necessary for ManagementNode OS objects to work
+	# Otherwise the base Linux.pm subroutines would be used instead of ManagementNode.pm
+	if (defined($arguments->{base_package})) {
+		notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . $ISA[0] . " --> $arguments->{base_package}");
+		@ISA = ($arguments->{base_package});
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object to control $computer_name");
+	
+	if (!$self->service_exists('iptables')) {
+		notify($ERRORS{'DEBUG'}, 0, ref($self) . " object not initialized to control $computer_name, iptables service does not exist");
+		return 0;
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, ref($self) . " object initialized to control $computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 insert_rule
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=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 $arguments = shift;
+	if (!$arguments) {
+		notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
+		return;
+	}
+	elsif (!ref($arguments) || ref($arguments) ne 'HASH') {
+		notify($ERRORS{'WARNING'}, 0, "argument is not a hash reference");
+		return;
+	}
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = '/sbin/iptables';
+	
+	# Add the table argument if specified
+	if ($arguments->{table}) {
+		$command .= " -t $arguments->{table}";
+	}
+	
+	# Get the chain argument
+	my $chain = $arguments->{chain};
+	if (!defined($chain)) {
+		notify($ERRORS{'WARNING'}, 0, "chain argument was not specified:\n" . format_data($arguments));
+		return;
+	}
+	$command .= " -I $chain";
+	
+	# Add the parameters to the command
+	for my $parameter (sort keys %{$arguments->{parameters}}) {
+		my $value = $arguments->{parameters}{$parameter};
+		$command .= " --$parameter $value";
+	}
+	
+	# Add the match extension to the command
+	for my $match_extension (sort keys %{$arguments->{match_extensions}}) {
+		$command .= " --match $match_extension";
+		for my $option (sort keys %{$arguments->{match_extensions}{$match_extension}}) {
+			my $value = $arguments->{match_extensions}{$match_extension}{$option};
+			
+			if ($option =~ /(comment)/) {
+				$value = "\"$value\"";
+			}
+			
+			$command .= " --$option $value";
+		}
+	}
+	
+	# Add the target extensions to the command
+	for my $target_extension (sort keys %{$arguments->{target_extensions}}) {
+		$command .= " --jump $target_extension";
+		for my $option (sort keys %{$arguments->{target_extensions}{$target_extension}}) {
+			my $value = $arguments->{target_extensions}{$target_extension}{$option};
+			$command .= " --$option $value";
+		}
+	}
+	
+	my ($exit_status, $output) = $self->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 add iptables rule 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 on $computer_name, command: $command");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_rule
+
+ Parameters  : hash reference
+               -or-
+					$table_name, $chain_name, $rule_specification
+ Returns     : boolean
+ Description : Deletes a rule.
+
+=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 $argument = shift;
+	if (!$argument) {
+		notify($ERRORS{'WARNING'}, 0, "argument was not supplied");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = '/sbin/iptables';
+	
+	
+	if (ref($argument) && ref($argument) eq 'HASH') {
+		# Add the table argument if specified
+		if ($argument->{table}) {
+			$command .= " -t $argument->{table}";
+		}
+		
+		# Get the chain argument
+		my $chain = $argument->{chain};
+		if (!defined($chain)) {
+			notify($ERRORS{'WARNING'}, 0, "chain argument was not specified:\n" . format_data($argument));
+			return;
+		}
+		$command .= " -D $chain";
+		
+		# Add the parameters to the command
+		for my $parameter (sort keys %{$argument->{parameters}}) {
+			my $value = $argument->{parameters}{$parameter};
+			$command .= " --$parameter $value";
+		}
+		
+		# Add the match extension to the command
+		for my $match_extension (sort keys %{$argument->{match_extensions}}) {
+			$command .= " --match $match_extension";
+			for my $option (sort keys %{$argument->{match_extensions}{$match_extension}}) {
+				my $value = $argument->{match_extensions}{$match_extension}{$option};
+				
+				if ($option =~ /(comment)/) {
+					$value = "\"$value\"";
+				}
+				
+				$command .= " --$option $value";
+			}
+		}
+		
+		# Add the target extensions to the command
+		for my $target_extension (sort keys %{$argument->{target_extensions}}) {
+			$command .= " --jump $target_extension";
+			for my $option (sort keys %{$argument->{target_extensions}{$target_extension}}) {
+				my $value = $argument->{target_extensions}{$target_extension}{$option};
+				$command .= " --$option $value";
+			}
+		}
+	}
+	elsif (my $type = ref($argument)) {
+		notify($ERRORS{'WARNING'}, 0, "argument $type reference not supported, argument must only be a HASH reference or scalar");
+		return;
+	}
+	else {
+		my $table_name = $argument;
+		my ($chain_name, $specification) = @_;
+		if (!defined($chain_name) || !defined($specification)) {
+			notify($ERRORS{'WARNING'}, 0, "1st argument is a scalar, 2nd chain name and 3rd rule specification arguments not provided");
+			return;
+		}
+		$command .= " -D $chain_name -t $table_name $specification";
+	}
+	
+	my ($exit_status, $output) = $self->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 delete iptables rule on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return 0;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "deleted iptables rule on $computer_name, command: $command");
+		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 = "/sbin/iptables --new-chain $chain_name --table $table_name";
+	my ($exit_status, $output) = $self->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
+		return;
+	}
+	elsif (grep(/already exists/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;
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "created '$chain_name' chain in '$table_name' table on $computer_name");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Deletes the specified chain from the specified table. All rules
+               which exist in the chain or reference the chain are deleted prior
+               to deletion of the chain.
+
+=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();
+	
+	my $table_info = $self->get_table_info($table_name);
+	if (!defined($table_info->{$chain_name})) {
+		notify($ERRORS{'DEBUG'}, 0, "'$chain_name' chain in '$table_name' table does not exist on $computer_name");
+		return 1;
+	}
+	
+	# Flush the chain first - delete will fail if the chain still contains rules
+	if (!$self->flush_chain($table_name, $chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' chain from '$table_name' table on $computer_name, failed to flush chain prior to deletion");
+		return;
+	}
+	
+	# Delete all rules which reference the chain being deleted or else the chain can't be deleted
+	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;
+	}
+	
+	my $command = "/sbin/iptables --delete-chain $chain_name --table $table_name";
+	my ($exit_status, $output) = $self->execute($command, 0);
+	if (!defined($output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command $computer_name: $command");
+		return;
+	}
+	elsif (grep(/Too many links/i, @$output)) {
+		notify($ERRORS{'WARNING'}, 0, "unable to delete '$chain_name' chain from '$table_name' table on $computer_name, the chain is referenced by another rule");
+		return 0;
+	}
+	elsif ($exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete '$chain_name' chain from '$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, "deleted '$chain_name' chain from '$table_name' table on $computer_name");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_chain_references
+
+ Parameters  : $table_name, $referenced_chain_name
+ Returns     : boolean
+ Description : Checks all chains in the specified table for references to the
+               $referenced_chain_name argument. If found, the referencing rules
+               are deleted.
+
+=cut
+
+sub delete_chain_references {
+	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, $referenced_chain_name) = @_;
+	if (!defined($table_name)) {
+		notify($ERRORS{'WARNING'}, 0, "table name argument was not specified");
+		return;
+	}
+	elsif (!defined($referenced_chain_name)) {
+		notify($ERRORS{'WARNING'}, 0, "referenced chain name argument was not specified");
+		return;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $table_info = $self->get_table_info($table_name);
+	for my $referencing_chain_name (keys %$table_info) {
+		for my $rule_specification (@{$table_info->{$referencing_chain_name}{rules}}) {
+			if ($rule_specification =~ /-j $referenced_chain_name(\s|$)/) {
+				notify($ERRORS{'DEBUG'}, 0, "rule in '$table_name' table references '$referenced_chain_name' chain, referencing chain: $referencing_chain_name, rule specification: $rule_specification");
+				if (!$self->delete_rule($table_name, $referencing_chain_name, $rule_specification)) {
+					return;
+				}
+			}
+		}
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "deleted all rules in '$table_name' table referencing '$referenced_chain_name' chain on $computer_name");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 flush_chain
+
+ Parameters  : $table_name, $chain_name
+ Returns     : boolean
+ Description : Flushes (deletes) rules from the specified chain.
+
+=cut
+
+sub flush_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 = "/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 ($exit_status, $output) = $self->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 flush $chain_text 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");
+		return 1;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 get_table_info
+
+ Parameters  : $table_name, $chain_name (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub get_table_info {
+	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;
+	}
+	
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $command = "/sbin/iptables --list-rules";
+	my $chain_text = '';
+	if (defined($chain_name)) {
+		$command .= " $chain_name";
+		$chain_text = "of '$chain_name' chain ";
+	}
+	$command .= " --table $table_name";
+	
+	my ($exit_status, $output) = $self->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 list rules " . $chain_text . "from '$table_name' table on $computer_name, exit status: $exit_status, command:\n$command\noutput:\n" . join("\n", @$output));
+		return 0;
+	}
+	
+	my $table_info = {};
+	for my $line (@$output) {
+		my ($iptables_command, $chain_name, $specification) = $line =~ /^(-\w) ([^ ]+)\s*(.*)$/;
+		if (!defined($iptables_command) || !defined($chain_name)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to parse line: '$line'\ncommand: $command");
+			next;
+		}
+		$specification = '' unless defined($specification);
+		
+		if ($iptables_command eq '-P') {
+			# -P, --policy chain target (Set  the policy for the chain to the given target)
+			$table_info->{$chain_name}{policy} = $specification;
+		}
+		elsif ($iptables_command eq '-N') {
+			# -N, --new-chain chain
+			$table_info->{$chain_name} = {} unless defined($table_info->{$chain_name});
+		}
+		elsif ($iptables_command eq '-A') {
+			# -A, --append chain rule-specification
+			push @{$table_info->{$chain_name}{rules}}, $specification;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "'$iptables_command' command is not supported: $line");
+		}
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved rules " . $chain_text . "from '$table_name' table from $computer_name:\n" . format_data($table_info));
+	return $table_info;
+}
+
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 configure_nat
+
+ Parameters  : none
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub configure_nat {
+	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 $reservation_id = $self->data->get_reservation_id();
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my $table_info = $self->get_table_info('nat');
+	if (!$table_info) {
+		notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on $computer_name, nat table info could not be retrieved");
+		return;
+	}
+	elsif (!defined($table_info->{PREROUTING})) {
+		notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a PREROUTING chain:\n" . format_data($table_info));
+		return;
+	}
+	elsif (!defined($table_info->{POSTROUTING})) {
+		notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, nat table does not contain a POSTROUTING chain:\n" . format_data($table_info));
+		return;
+	}
+	
+	# Check if NAT has previously been configured
+	my $nat_previously_configured = 0;
+	for my $rule_specification (@{$table_info->{POSTROUTING}{rules}}) {
+		if ($rule_specification =~ /MASQUERADE/) {
+			$nat_previously_configured = 1;
+			notify($ERRORS{'DEBUG'}, 0, "POSTROUTING chain in nat table contains a MASQUERADE rule, assuming NAT has already been configured: $rule_specification");
+			last;
+		}
+	}
+	if (!$nat_previously_configured) {
+		my $private_interface_name = $self->get_private_interface_name();
+		my $private_ip_address = $self->get_private_ip_address();
+		my $public_interface_name = $self->get_public_interface_name();
+		my $public_ip_address = $self->get_public_ip_address();
+		
+		my $natport_ranges_variable = get_variable('natport_ranges') || '49152-65535';
+		my $destination_ports = '';
+		for my $natport_range (split(/[,;]+/, $natport_ranges_variable)) {
+			my ($start_port, $end_port) = $natport_range =~ /(\d+)-(\d+)/g;
+			if (!defined($start_port)) {
+				notify($ERRORS{'WARNING'}, 0, "unable to parse NAT port range: '$natport_range'");
+				next;
+			}
+			$destination_ports .= "," if ($destination_ports);
+			$destination_ports .= "$start_port:$end_port";
+		}
+		
+		
+		if (!$self->insert_rule({
+			'table' => 'nat',
+			'chain' => 'POSTROUTING',
+			'parameters' => {
+				'out-interface' => $private_interface_name,
+				'jump' => 'MASQUERADE',
+			},
+			'match_extensions' => {
+				'comment' => {
+					'comment' => "change IP of outbound private $private_interface_name packets to NAT host private IP address $private_interface_name",
+				},
+			},
+		})) {
+			return;
+		}
+		
+		if (!$self->insert_rule({
+			'chain' => 'INPUT',
+			'parameters' => {
+				'in-interface' => $public_interface_name,
+				'destination' => $public_ip_address,
+				'jump' => 'ACCEPT',
+				'protocol' => 'tcp',
+			},
+			'match_extensions' => {
+				'state' => {
+					'state' => 'RELATED,ESTABLISHED',
+				},
+				'multiport' => {
+					'destination-ports' => $destination_ports,
+				},
+			},
+		})) {
+			return;
+		}
+		
+		if (!$self->insert_rule({
+			'chain' => 'INPUT',
+			'parameters' => {
+				'in-interface' => $public_interface_name,
+				'destination' => $public_ip_address,
+				'jump' => 'ACCEPT',
+				'protocol' => 'udp',
+			},
+			'match_extensions' => {
+				'state' => {
+					'state' => 'RELATED,ESTABLISHED',
+				},
+				'multiport' => {
+					'destination-ports' => $destination_ports,
+				},
+			},
+		})) {
+			return;
+		}
+		
+		if (!$self->insert_rule({
+			'chain' => 'FORWARD',
+			'parameters' => {
+				'in-interface' => $public_interface_name,
+				'out-interface' => $private_interface_name,
+				'jump' => 'ACCEPT',
+			},
+			'match_extensions' => {
+				'state' => {
+					'state' => 'NEW,RELATED,ESTABLISHED',
+				},
+				'comment' => {
+					'comment' => "forward inbound packets from public $public_interface_name to private $private_interface_name",
+				},
+			},	
+		})) {
+			return;
+		}
+		
+		if (!$self->insert_rule({
+			'chain' => 'FORWARD',
+			'parameters' => {
+				'in-interface' => $private_interface_name,
+				'out-interface' => $public_interface_name,
+				'jump' => 'ACCEPT',
+			},
+			'match_extensions' => {
+				'comment' => {
+					'comment' => "forward outbound packets from private $private_interface_name to public $public_interface_name",
+				},
+			},
+		})) {
+			return;
+		}
+		
+		#if (!$self->insert_rule({
+		#	'chain' => 'INPUT',
+		#	'parameters' => {
+		#		'in-interface' => $public_interface_name,
+		#	},
+		#	'target_extensions' => {
+		#		'REJECT' => {
+		#			'reject-with' => "icmp-host-prohibited",
+		#		},
+		#	},
+		#})) {
+		#	return;
+		#}
+		#
+		#if (!$self->insert_rule({
+		#	'chain' => 'FORWARD',
+		#	'target_extensions' => {
+		#		'REJECT' => {
+		#			'reject-with' => "icmp-host-prohibited",
+		#		},
+		#	},
+		#})) {
+		#	return;
+		#}
+	}
+	
+	# Check if chain for reservation has already been created
+	if (defined($table_info->{$reservation_id})) {
+		notify($ERRORS{'DEBUG'}, 0, "'$reservation_id' chain already exists in nat table on $computer_name for this reservation");
+	}
+	else {
+		if (!$self->create_chain('nat', $reservation_id)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to configure NAT on $computer_name, '$reservation_id' chain could not be created in nat table for this reservation");
+			return;
+		}
+	}
+	
+	# Check if rule to jump to reservation's chain already exists in the PREROUTING table
+	my $jump_previously_configured = 0;
+	for my $rule_specification (@{$table_info->{PREROUTING}{rules}}) {
+		if ($rule_specification =~ /-j $reservation_id(\s|$)/) {
+			$jump_previously_configured = 1;
+			notify($ERRORS{'DEBUG'}, 0, "PREROUTING chain in nat table on $computer_name already contains a rule to jump to '$reservation_id' chain: $rule_specification");
+			last;
+		}
+	}
+	if (!$jump_previously_configured) {
+		if (!$self->insert_rule({
+			'table' => 'nat',
+			'chain' => 'PREROUTING',
+			'parameters' => {
+				'jump' => $reservation_id,
+			},
+		})) {
+			notify($ERRORS{'WARNING'}, 0, "unable to configure NAT on $computer_name, failed to create rule in PREROUTING chain in nat table to jump to '$reservation_id' chain");
+			return;
+		}
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "successfully configured NAT on $computer_name for reservation");
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 add_nat_port_forward
+
+ Parameters  : $protocol, $source_port, $destination_ip_address, $destination_port, $chain_name (optional)
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub add_nat_port_forward {
+	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 $reservation_id = $self->data->get_reservation_id();
+	my $computer_name = $self->data->get_computer_hostname();
+	
+	my ($protocol, $source_port, $destination_ip_address, $destination_port, $chain_name) = @_;
+	if (!defined($protocol)) {
+		notify($ERRORS{'WARNING'}, 0, "protocol argument was not provided");
+		return;
+	}
+	elsif (!defined($source_port)) {
+		notify($ERRORS{'WARNING'}, 0, "source port argument was not provided");
+		return;
+	}
+	elsif (!defined($destination_ip_address)) {
+		notify($ERRORS{'WARNING'}, 0, "destination IP address argument was not provided");
+		return;
+	}
+	elsif (!defined($destination_port)) {
+		notify($ERRORS{'WARNING'}, 0, "destination port argument was not provided");
+		return;
+	}
+	$chain_name = 'PREROUTING' unless defined $chain_name;
+	
+	$protocol = lc($protocol);
+	
+	my $public_interface_name = $self->get_public_interface_name();
+	my $public_ip_address = $self->get_public_ip_address();
+	
+	if ($self->insert_rule({
+		'table' => 'nat',
+		'chain' => $chain_name,
+		'parameters' => {
+			'protocol' => $protocol,
+			'in-interface' => $public_interface_name,
+			'destination' => $public_ip_address,
+		},
+		'match_extensions' => {
+			'comment' => {
+				'comment' => "change destination address: $public_ip_address:$source_port --> $destination_ip_address:$destination_port ($protocol)",
+			},
+			$protocol => {
+				'destination-port' => $source_port,
+			},
+		},
+		'target_extensions' => {
+			'DNAT' => {
+				'to-destination' => "$destination_ip_address:$destination_port",
+			},
+		},
+	})) {
+		notify($ERRORS{'OK'}, 0, "added NAT port forward on $computer_name: $public_interface_name:$source_port --> $destination_ip_address:$destination_port");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to add NAT port forward on $computer_name: $public_interface_name:$source_port --> $destination_ip_address:$destination_port");
+		return;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/firewall/iptables.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm?rev=1644186&r1=1644185&r2=1644186&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm Tue Dec  9 21:13:08 2014
@@ -1,100 +1,100 @@
-#!/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::init
-
-=head1 DESCRIPTION
-
- This module is the parent class for the Linux init modules.
-
-=cut
-
-##############################################################################
-package VCL::Module::OS::Linux::init;
-
-# Specify the lib path using FindBin
-use FindBin;
-use lib "$FindBin::Bin/../../../..";
-
-# Configure inheritance
-use base qw(VCL::Module::OS::Linux);
-
-# Specify the version of this module
-our $VERSION = '2.3';
-
-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  : 
- Returns     : boolean
- Description : 
-
-=cut
-
-sub initialize {
-	my $self = shift;
-	if (ref($self) !~ /linux/i) {
-		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
-		return;
-	}
-	
-	my $arguments = shift || {};
-	
-	# Check if base_package argument was specified
-	# This is necessary for ManagementNode OS objects to work
-	# Otherwise the base Linux.pm subroutines would be used instead of ManagementNode.pm
-	if (defined($arguments->{base_package})) {
-		notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . $ISA[0] . " --> $arguments->{base_package}");
-		@ISA = ($arguments->{base_package});
-	}
-	
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-1;
-__END__
-
-=head1 SEE ALSO
-
-L<http://cwiki.apache.org/VCL/>
-
-=cut
+#!/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::init
+
+=head1 DESCRIPTION
+
+ This module is the parent class for the Linux init modules.
+
+=cut
+
+##############################################################################
+package VCL::Module::OS::Linux::init;
+
+# Specify the lib path using FindBin
+use FindBin;
+use lib "$FindBin::Bin/../../../..";
+
+# Configure inheritance
+use base qw(VCL::Module::OS::Linux);
+
+# Specify the version of this module
+our $VERSION = '2.3';
+
+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  : 
+ Returns     : boolean
+ Description : 
+
+=cut
+
+sub initialize {
+	my $self = shift;
+	if (ref($self) !~ /linux/i) {
+		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
+		return;
+	}
+	
+	my $arguments = shift || {};
+	
+	# Check if base_package argument was specified
+	# This is necessary for ManagementNode OS objects to work
+	# Otherwise the base Linux.pm subroutines would be used instead of ManagementNode.pm
+	if (defined($arguments->{base_package})) {
+		notify($ERRORS{'DEBUG'}, 0, "overriding object package: " . $ISA[0] . " --> $arguments->{base_package}");
+		@ISA = ($arguments->{base_package});
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+L<http://cwiki.apache.org/VCL/>
+
+=cut

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/init.pm
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id