#!/usr/bin/perl -w

# gforward.pl v0.2.1
# a generic forwarding tool for
# iptables.  Part of the gShield
# firewall. 

# gforward is a perl script which quickly
# generates basic port-forwarding mechanisms
# using iptables. It can also make use of 
# an external file to easily create multiple
# forward rules in a single step.

# Copyright 2001-2002 R. Gregory <godot@emptybox.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# A copy of the GPL can be found at http://www.gnu.org/copyleft/gpl.html

# sanity check
chomp ($iptables = (`which iptables`));
die "Cannot determine path to iptables: set path\n" if $iptables =~ /\s/ or $iptables eq "";

# get options
use Getopt::Long;
GetOptions( "help"	=> \$help,     		# print usage information
            "i=s" 	=> \$interface,		# interface to use
            "f=s" 	=> \$file);    		# use external file

# no options passed
sub doh() {
	print <<DUMP
Usage: gforward.pl [external interface] [firewall port] [destination ip] [destination port]
Options:
-f      read from a file a list of forwards to be generated
-i      interface to use (if reading from a file)
-h	more help
DUMP
;
}  

# usage ditty
sub usage() {
print <<DUMP
Usage: gforward.pl [external interface] [firewall port] [destination ip] [destination port] 

Options:
--------
-f	  read from a file a list of forwards to be generated
-i	  interface to use (if reading from a file)

Examples:
---------
(Forward port 80 to machine 192.168.1.6, port 80 and run the ruleset)
./gforward.pl eth0 80 192.168.1.6 80 > test.rules && sh test.rule

(gShield users)
./gforward.pl eth0 80 192.168.1.6 80 >> /etc/firewall/gshield.last && /etc/firewall/gShield.rc

Using the external file option:

./gforward -i eth0 -f fwdfile

Notes:
------
To use the -f feature, create a file with a single forward rule per
line: <firewall-port>:<ip-to-forward-to>:<destination-port>

	5900:192.168.2.10:5900
	5901:192.168.2.12:5900
	5902:192.168.2.13:5900

You MUST include the -i option to pass the proper interface if you
use the -f option.

DUMP
;
}

# multi-forward setup
sub multi_forward {
	if ($file) {
		open(FWDFILE, $file);
		while (defined($fwdline = <FWDFILE>)) {
			next if ($fwdline =~ /^#/);
			($fwport,$destip,$destport) = split(/:/,$fwdline);
			chomp ($fwport,$destip,$destport);
			die "Error: Invalid port\n" if (($fwport)  =~ /\D/) || (($fwport) < 1) || (($fwport) > 65535) || (($destport)  =~ /\D/) || (($destport) < 1) || (($destport) > 65535);
			print "$iptables -I FORWARD -p tcp -d $destip --dport $destport -j ACCEPT\n",
			      "$iptables -I FORWARD -p udp -d $destip --dport $destport -j ACCEPT\n",
			      "$iptables -t nat -A PREROUTING -p tcp -i $interface --dport $fwport -j DNAT --to $destip:$destport\n",
			      "$iptables -t nat -A PREROUTING -p udp -i $interface --dport $fwport -j DNAT --to $destip:$destport\n";
		}
	}
}

# forward from stdin
sub single_forward {
	
	if ( $#ARGV != 3 ) {
		&doh(); exit;
	}

	($interface, $fwport, $destip, $destport) = ($ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3]);
	die "Error: Invalid port\n" if (($fwport)  =~ /\D/) || (($fwport) < 1) || (($fwport) > 65535) || (($destport)  =~ /\D/) || (($destport) < 1) || (($destport) > 65535);
    	print "$iptables -I FORWARD -p tcp -d $destip --dport $destport -j ACCEPT\n",
	      "$iptables -I FORWARD -p udp -d $destip --dport $destport -j ACCEPT\n",
	      "$iptables -t nat -A PREROUTING -p tcp -i $interface --dport $fwport -j DNAT --to $destip:$destport\n",
	      "$iptables -t nat -A PREROUTING -p udp -i $interface --dport $fwport -j DNAT --to $destip:$destport\n";
}

# dump help
if ($help) {
	&usage();                            
	exit 1;
}

# begin
if ($file) {
	if ($interface) {
		&multi_forward;
	} else {
		die "Error: set external interface thru -i\n";
	}
} else {
	&single_forward;
}

