# # PortFW.pm - Perl module to interface with ipmasqadm portfw command. # # This file is part of Fwctl. # # Author: Francis J. Lacoste # # Copyright (C) 1999,2000 iNsu Innovations Inc. # # 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. # package IPChains::PortFW; use strict; use Carp; use Symbol; use vars qw( $VERSION $IPMASQADM ); BEGIN { ($VERSION) = '$Revision: 1.4 $' =~ /Revision: ([0-9.]+)/; } my %VALID_OPTIONS = map { $_ => 1 } qw( LocalAddr LocalPort RemAddr RemPort Proto Pref ); sub new { my $proto = shift; my $class = ref $proto || $proto; my %args = @_; my $self = { }; # Look for ipmasqadm my ($path) = grep { -x "$_/ipmasqadm" } split /:/, "/sbin:/bin:/usr/sbin:/usr/bin:$ENV{PATH}"; die ( "Couldn't find ipmasqadm in PATH ($ENV{PATH})\n" ) unless $path; $self->{ipmasqadm} = "$path/ipmasqadm"; bless $self, $class; while ( my ($key,$value) = each %args ) { $self->attribute( $key, $value ); } $self; } sub attribute { my ($self,$key,$value) = @_; if ( @_ == 3 ) { if ( $VALID_OPTIONS{$key} ) { $self->{$key} = $value; } else { carp "Unknown option : $key"; } } return $self->{$key}; } sub clopts { my ( $self ) = shift; foreach my $key ( keys %VALID_OPTIONS ) { delete $self->{$key}; } } sub run_portfw { my ( $self, @args ) = @_; my ($r_fh,$w_fh) = (gensym,gensym); pipe $r_fh, $w_fh or die "can't pipe: $!\n"; my $pid = fork; die "can't fork: $!\n" unless defined $pid; if ( $pid ) { # Don't need this one close $w_fh; # Collect STDOUT and STDERR my $output; while ( my $line = <$r_fh> ) { $output .= $line; } # Collect exit status waitpid $pid,0; die "ipmasq exit with non zero status:\n$output\n" if $?; $output; } else { # Don't need this one close $r_fh; # Redirect STDOUT and STDERR to parent open ( STDOUT, ">&" . fileno $w_fh ) or die "can't redirect STDOUT to proper pipe: $!\n"; open ( STDERR, ">&" . fileno $w_fh ) or die "can't redirect STDERR to proper output fd: $!\n"; exec( $self->{ipmasqadm}, "portfw", @args ) or die "can't exec ipmasqadm: $!"; } } sub append { my ( $self ) = shift; my @args = ( "-a" ); croak "missing protocol" unless exists $self->{Proto}; croak "invalid protocol" unless $self->{Proto} =~ /udp|tcp|6|17/i; croak "missing local address" unless exists $self->{LocalAddr}; croak "missing local port" unless exists $self->{LocalPort}; croak "missing remote address" unless exists $self->{RemAddr}; croak "missing remote port" unless exists $self->{RemPort}; if ( exists $self->{Pref} ) { croak "invalid preference" unless $self->{Pref} =~ /\d+/ && $self->{Pref} >= 0; } push @args, "-P", lc $self->{Proto}, "-L", $self->{LocalAddr}, $self->{LocalPort}, "-R", $self->{RemAddr}, $self->{RemPort}; push @args, "-p", $self->{Pref} if exists $self->{Pref}; $self->run_portfw( @args ); } sub delete { my ( $self ) = shift; my @args = ( "-d" ); croak "missing protocol" unless exists $self->{Proto}; croak "invalid protocol" unless $self->{Proto} =~ /udp|tcp|6|17/i; croak "missing local address" unless exists $self->{LocalAddr}; croak "missing local port" unless exists $self->{LocalPort}; push @args, "-P", lc $self->{Proto}, "-L", $self->{LocalAddr}, $self->{LocalPort}; push @args, "-R", $self->{RemAddr}, $self->{RemPort} if exists $self->{RemAddr}; $self->run_portfw( @args ); } sub flush { $_[0]->run_portfw( "-f" ); } sub list { my ($self, $use_dns) = @_; my @args = ( "-l" ); push @args, "-n" unless defined $use_dns && $use_dns; my $output = $self->run_portfw( @args ); return () unless defined $output; # Parse output my @lines = split /\n/, $output; # Skip header line shift @lines; my @rules = (); foreach my $line ( @lines ) { my ( $prot, $laddr, $raddr, $lport, $rport, $ignored, $pref ) = split / +/, $line; push @rules, $self->new( Proto => lc $prot, LocalAddr => $laddr, RemAddr => $raddr, LocalPort => $lport, RemPort => $rport, ); } @rules; } 1; __END__ =pod =head1 NAME IPChains::PortFW - Perl module to manipulate portfw masquerading table. =head1 SYNOPSIS my $masq = new IPChains::PortFW( option => value, ... ); $masq->append(); =head1 DESCRIPTION IPChains::PortFW is an perl interface to the linux kernel port forwarding facility. You must have ipmasqadm and the portfw module installed for this module to work. A kernel compiled with CONFIG_IP_MASQUERADE_IPPORTFW would also helps. It has a similar interface than the IPChains(3) module. You create an IPChains::PortFW object with new(), you can query or set attributes with the attribute() method and you add or deletes the port forwarding rules using append() or delete(). =head2 ATTRIBUTES Here are the attributes valids for IPChains::PortFW. =over =item LocalAddr This is the local address from which packets will be redirected. =item LocalPort This is the port from which packets will be redirected. =item RemAddr This is the address to which the packets will be forwarded to. =item RemPort This is the port to which the packets will be forwarded to. =item Pref This is a preferences value used for load balancing in the case when there are many possible remote destinations. =back =head2 METHODS =over =item new( [options], ... ) Create a new IPChains::PortFW object and sets its attributes. =item attribute( attribute [, value] ) Get or sets an attribute. Use undef to delete a value. =item clopts() Unset all attributes. =item append() Append a rule to the port forwarding masquerade table as specified by the attributes of the current objects. =item delete() Deletes entries in the port forwarding masquerade table. The entries matching the attributes will be deleted. =item flush() Removes all entries from the port forwarding masquerade table. =item list() Returns an array of IPChains::PortFW objects. One for each entries in the port forwarding table. =back =head1 EXAMPLE Redirecting http protocol to internal web server. my $portfw = new IPChains::PortFW( Proto => 'udp', LocalAddr => '199.168.1.10', LocalPort => 80, RemAddr => '10.0.0.1', RemPort => 80 ); $portfw->append; =head1 AUTHOR Francis J. Lacoste =head1 COPYRIGHT Copyright (C) iNsu Innovations Inc. All rights reserved. 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. =head1 SEE ALSO IPChains(3) =cut