#!/usr/bin/perl

###############################################################################
#
# iptoip,v 0.1.7
#
# Copyright (C) 2001 Lenny Cartier (l.cartier@free.fr)
#
# 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, 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
###############################################################################

#use strict;
use Sys::Hostname;
use Getopt::Long;
use Sys::Syslog  qw(:DEFAULT setlogsock);
use XML::Simple;

setlogsock 'unix';

my ( $i, $j );
my $xmlarg = "/etc/iptoip.xml";
my $host = hostname();
my ($ip, $error); 
my $p = 0;
my $help = 0; 
my $ident = "[iptoip]";
my $LOCAL_WEIGHT = "1";

my ( $BYPASS_CHECK_MODE, $SYSLOG_OUT, $VERBOSE, $IFACE );
my ( $DEST_IP, $PORT, $SCHEDULER, $WEIGHT ); # ipvsadm args
my ( $NUMBER, $nb, $quot ); # to help find how much conf

## Inline Options #########################################

GetOptions ('file=s' => \$xmlarg, 'help' => \$help );

$help && die <<END;

iptoip-0.1.5, Copyright (C) 2001 Lenny Cartier
iptoip comes with ABSOLUTELY NO WARRANTY; 
This is free software, and you are welcome to 
redistribute it under the terms of the GNU GPL.

iptoip - Update an ipvsadm table

Usage: iptoip [--file] [--help] 
 --file		Use specified configuration file	
 --help		Show this help message

END

###########################################################

my $xmlconf = XMLin($xmlarg); # define the xml file access point 

sub read_conf {

	$BYPASS_CHECK_MODE = $xmlconf->{nocheck};
	$SYSLOG_OUT =  $xmlconf->{syslog};
	$VERBOSE = $xmlconf->{verbose};
	$IFACE = $xmlconf->{iface};

}

sub syslog_input {			## output $msg to syslog
	
	my $msg = shift;
	openlog $ident,'pid';
	syslog ('notice',$msg);
	closelog;

}


sub get_ip {				## extract internet ip

	$_ = `/sbin/ifconfig $IFACE 2>&1`;

	if (s/^.*inet[ \t:]*(addr|adr)*[ \t:]*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*$/$2/s) {  		# if ip
        	if(/^192\.168\..*$/ || /^10\..*$/ || /^172\.((1[6-9])|(2\d)|(3[01]))\..*$/) {  		# is it local ?
		$ip = $_; $error = 1; 									# yes
		return $ip, $error;
        } else { 											# no
		s/[\n\ \t]//g;
		$ip = $_; $error = 0;
		return $ip, $error;
        } 

	}
	else { 	$ip = "-- none --"; $error = 1;						# everything else than an ip
		return $ip, $error; }

}

sub clean_table {

system "/sbin/ipvsadm -C";

}

sub update_ipvs {			## update ipvsadm table with
					## specified $ip, $DEST_IP, and $PORT
system "/sbin/ipvsadm -A -t $ip:$PORT -s $SCHEDULER"; ## $SCHEDULER for load balancing
system "/sbin/ipvsadm -a -t $ip:$PORT -r $DEST_IP:$PORT -m -w $LOCAL_WEIGHT"; ## $LOCAL_WEIGHT the weight of 
										## the server
}

sub comments {

print ("\n External interface : ",$IFACE);
print ("\n Incoming port : ",$PORT);
print ("\n Destination machine : ",$DEST_IP);
print ("\n Returned ip : ",$ip);
print ("\n Hack mode : ", $BYPASS_CHECK_MODE);
print ("\n Error code : ",$error);
print ("\n Scheduling method : ",$SCHEDULER);
print ("\n Weight : ",$LOCAL_WEIGHT);
print (" \n");	

}

sub conf_number { # to find how much configs we have
	my $nb;
	open (XMLFILE,$xmlarg) || die " Can't open $xmlarg\n ";
	while (<XMLFILE>) { if ( /\bconf\b/ ) { $nb++; } } 	# number of <conf> tags
	
	# test if there is a non closed tag && to know if we've got 1 conf	
	$quot = ( $nb / 2 ); 
	$j = ( $nb / 2 ) ;
	if ( $quot =~ /\d\.\d/ ) { die " ERROR, check your xml configuration file\n Remember to close <conf> tags\n"  }
		elsif ( $quot eq 1 ) { $NUMBER = $quot; }
		return $NUMBER, $j;
}
								    
sub xml_update {

	if ($NUMBER eq 1) { 

		$DEST_IP = $xmlconf->{conf}->{destination} ;      # get destination
		$PORT = $xmlconf->{conf}->{port}->[$p] ;          # get port value
		$SCHEDULER = $xmlconf->{conf}->{scheduler} ;      # get scheduler type
		$WEIGHT = $xmlconf->{conf}->{weight} ;		   # get the weight of servers
		
		if ( $SCHEDULER eq  "wrr" || "wlc" )
		
		{ if ( $WEIGHT >= "1" ) { $LOCAL_WEIGHT = $WEIGHT } }

		if ($PORT eq "") { $PORT = $xmlconf->{conf}->{port};
				   update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
				   syslog_input("ipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n", $ident);
				   	print ("TATA");
				   if ( $VERBOSE eq 1 ) { comments(); }
				   print ("\nipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n");
				}
		else { while ( $xmlconf->{conf}->{port}->[$p] ) {
	
			$PORT = $xmlconf->{conf}->{port}->[$p];
			update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
			syslog_input("ipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n", $ident);
			if ( $VERBOSE eq 1 ) { comments(); }
			print ("\nipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n");
			$p++;
                        }
                }	

	} else {

		$DEST_IP = $xmlconf->{conf}->[$i]->{destination} ; # get destination
	        $PORT = $xmlconf->{conf}->[$i]->{port}->[$p] ;	    # get port value
		$SCHEDULER = $xmlconf->{conf}->[$i]->{scheduler} ;   # get scheduler type    
	        $WEIGHT = $xmlconf->{conf}->[$i]->{weight} ; 	    # get the weight of servers
	
		if ( $SCHEDULER eq  "wrr" || "wlc" )
		
		{ if ( $WEIGHT >= "1" ) { $LOCAL_WEIGHT = $WEIGHT } }
		
		if ($PORT eq "") { $PORT = $xmlconf->{conf}->[$i]->{port};
				   update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
				   syslog_input("ipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n", $ident);
				   if ( $VERBOSE eq 1 ) { comments(); }
				   print ("\nipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n");
			       }
		
		else { while ( $xmlconf->{conf}->[$i]->{port}->[$p] ) {
		
		    $PORT = $xmlconf->{conf}->[$i]->{port}->[$p];
		    update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
		    syslog_input("ipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n", $ident);
		    if ( $VERBOSE eq 1 ) { comments(); }
		    print ("\nipvsadm table updated for $ip:$PORT to $DEST_IP:$PORT\n");
		    $p++;
			}
		}
	}

}

conf_number();

print ("\nvaleur de j : $j\n");

read_conf();

get_ip();

if ( $error eq 0 ) {
            clean_table();
            for ($i = 0; $i < $j; $i++) {  xml_update(); }
            }

elsif ( $error eq 1 and $BYPASS_CHECK_MODE eq 1 ) {
            clean_table();
            for ($i = 0; $i < $j; $i++) {  xml_update(); }
            }


else { 	print("IP on ",$host," is a private IP. Cannot Update\n");
	syslog_input("IP on $host is a private IP. Cannot Update\n", $ident);
	}

# end of file
