#!/usr/bin/perl -w

###############################################################################
#
# iptoip,v 0.3.1 ( devel )
#
# 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 $host = hostname(); 			# hostname storage
my ($ip, $error); 			# ip | error status
my $p = 0;				# default port value
my $ident = "[iptoip]";			# ident for syslog
my $LOCAL_WEIGHT = "1";			# default weight
my $default_conf = "/etc/iptoip.xml";	# default config file

my ( $xmlarg, $help, $xmlconf );	

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

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

GetOptions(
	"f|file=s" => \$xmlarg, 
	"h|help" => \$help 
);

$help && die <<END;

iptoip-0.3.1 (devel), 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 -f -h --file --help 
 -f|--file		Use specified configuration file	
 -h|--help		Show this help message

END

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

### First config setup

sub test_file {

if ( $xmlarg eq "" ) { 
	if ( -f "$default_conf" ) { $xmlarg = $default_conf; $xmlconf = XMLin($xmlarg); } 
	else { print "Can't open $default_conf\n" ;  exit 1; }
} else {
	$xmlconf = XMLin($xmlarg);
	}
}
	
sub read_conf {

	$BYPASS_CHECK_MODE = $xmlconf->{nocheck};
	$SYSLOG_LEVEL =  $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 -$PROTOCOL $ip:$PORT -s $SCHEDULER"; ## $SCHEDULER for load balancing
system "/sbin/ipvsadm -a -$PROTOCOL $ip:$PORT -r $DEST_IP:$PORT -m -w $LOCAL_WEIGHT"; ## $LOCAL_WEIGHT the weight of 
										## the server
}

sub various_info {
	print ("Various info :\n");
	print ("\nVerbose mode = $VERBOSE\n");
	print ("Hack mode = $BYPASS_CHECK_MODE\n");
	print ("Syslog level = $SYSLOG_LEVEL\n");
	print ("Error code = $error\n");
	
}

sub comments {

print ("\n* Setting ipvsadm table for :");
print ("\nFrom $ip ( interface $IFACE ), port $PORT\n");
print ("to $DEST_IP on port $PORT:$PROTOCOL with a weight of $WEIGHT\n");
print ("the scheduling method is $SCHEDULER\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 syslog_out { # syslog with verbosity levels
	if ( $SYSLOG_LEVEL >= 1) { syslog_input("=> Ipvsadm update for $ip:$PORT to $DEST_IP:$PORT using $xmlarg\n", $ident); }
	if ( $SYSLOG_LEVEL eq 2) { syslog_input("=> Weight : $LOCAL_WEIGHT | Scheduler : $SCHEDULER\n", $ident); }
}

sub xml_update {

	if ($NUMBER eq 1) { 

		$PROTOCOL = $xmlconf->{conf}->{protocol} ;	# get protocol type
		$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_out ($SYSLOG_LEVEL);
				   if ( $VERBOSE eq 1 ) { comments(); }
				   print ("=> Ipvsadm update for $ip:$PORT to $DEST_IP:$PORT using $xmlarg\n");
				}
		else { while ( $xmlconf->{conf}->{port}->[$p] ) {
	
			$PORT = $xmlconf->{conf}->{port}->[$p];
			update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
			syslog_out ($SYSLOG_LEVEL);
			if ( $VERBOSE eq 1 ) { comments(); }
			print ("=> Ipvsadm update for $ip:$PORT to $DEST_IP:$PORT using $xmlarg\n");
			$p++;
                        }
                }	

	} else {

		$PROTOCOL = $xmlconf->{conf}->[$i]->{protocol} ;      	# get protocol type
		$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_out ($SYSLOG_LEVEL);
				   if ( $VERBOSE eq 1 ) { comments(); }
				   print ("=> Ipvsadm update for $ip:$PORT to $DEST_IP:$PORT using $xmlarg\n");
			       }
		
		else { while ( $xmlconf->{conf}->[$i]->{port}->[$p] ) {
		
		    $PORT = $xmlconf->{conf}->[$i]->{port}->[$p];
		    update_ipvs($ip,$DEST_IP,$PORT,$SCHEDULER,$LOCAL_WEIGHT);
		    syslog_out ($SYSLOG_LEVEL);
		    if ( $VERBOSE eq 1 ) { comments(); }
		    print ("=> Ipvsadm update for $ip:$PORT to $DEST_IP:$PORT from $xmlarg\n");
		    $p++;
			}
		}
	}

}

test_file();

conf_number();

read_conf();

get_ip();

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

elsif ( $error eq 1 and $BYPASS_CHECK_MODE eq 1 ) {
            clean_table();
	    if ( $VERBOSE eq 1 ) { various_info(); }
	    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
