#!/usr/bin/perl
# Server Program



use POSIX;
use IO::Socket;
use IO::Select;
use IO::Socket::INET;
use Socket;
use Fcntl;
use Tie::RefHash;



$debug = 1;

$| = 1;

print "\nstarting WiFi Manad:".localtime()."\n";


$MANAD_INTERFACE_IN=$ARGV[0];
$MANAD_INTERFACE_OUT=$ARGV[1];
$MANAD_PORT=$ARGV[2];

print "MANAD_INTERFACE_IN=".$MANAD_INTERFACE_IN."\n";
print "MANAD_INTERFACE_OUT=".$MANAD_INTERFACE_OUT."\n";
print "MANAD_PORT=".$MANAD_PORT."\n";


# Create a new socket                    
$MySocket=new IO::Socket::INET->new(LocalPort=>$MANAD_PORT,Proto=>'udp');
                                         


# База данных правил клиентов
%CLRULE = ();
%RUL_NUMBERS = ();
%DEL_RULES = ();
%CONTRACT_INDEX = ();

$pid = getpid();

open(FILE, ">/var/run/manad.pid");
print FILE $pid;
close(FILE);



$RULE = 

"[OPEN]
/sbin/tc class add dev $MANAD_INTERFACE_IN parent 1:0 classid 1:[N1] htb rate [DOWNSTREAM]kbit burst 4k prio 1
/sbin/tc qdisc add dev $MANAD_INTERFACE_IN parent 1:[N1] handle [N1]: sfq perturb 10 quantum 1500

/sbin/tc class add dev $MANAD_INTERFACE_OUT parent 1:0 classid 1:[N1] htb rate [UPSTREAM]kbit burst 4k prio 1
/sbin/tc qdisc add dev $MANAD_INTERFACE_OUT parent 1:[N1] handle [N1]: sfq perturb 10 quantum 1500

/sbin/tc filter add dev $MANAD_INTERFACE_IN parent 1:0 protocol ip prio [N1] u32 match ip dst {A}  flowid 1:[N1]

/sbin/tc filter add dev $MANAD_INTERFACE_OUT parent 1:0 protocol ip prio [N1] u32 match ip src {A} flowid 1:[N1]
[/OPEN]

[CLOSE]
/sbin/tc filter del dev $MANAD_INTERFACE_IN parent 1:0 protocol ip prio [N1]
/sbin/tc filter del dev $MANAD_INTERFACE_OUT parent 1:0 protocol ip prio [N1]

/sbin/tc class del dev $MANAD_INTERFACE_IN parent 1:0 classid 1:[N1] htb rate [DOWNSTREAM]kbit burst 4k prio 1
/sbin/tc class del dev $MANAD_INTERFACE_OUT parent 1:0 classid 1:[N1] htb rate [UPSTREAM]kbit burst 4k prio 1
[/CLOSE]";


while(1)
{
	my $request;    
	$MySocket->recv($request,128);

	print "\nrequest =".$request."\n" if ( $debug == 1);
	#/^add\t([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\t(.*)\t(.*)/
	if ( $request =~ /^add\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\s+(.*)\s+(.*)/ )
	{		
		my ($ip, $downstream, $upstream) = ($1, $2, $3);
		print "ip=".$ip."\n";
		print "downstream=".$downstream."\n";		
		print "upstream=".$upstream."\n";				
		&delete_rule( $ip ) if ( exists $CLRULE{$ip} );
		&add_rule( $ip, $downstream, $upstream ) if ( !exists $CLRULE{$ip} );
	}
	elsif ( $request =~ /^remove\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/ )
	{
		my $ip = $1;		
		print "\nip=".$ip."\n";	
		&delete_rule( $ip ) if ( exists $CLRULE{$1} );
	}	
	elsif ($request =~ /^shutup/)
	{
	    print "\n exiting\n";
	    exit;
	    
	}
}

# nonblock( $socket ) переводит сокет в неблокирующий режим
sub nonblock
{
	my $socket = shift;
	my $flags;

	$flags = fcntl( $socket, F_GETFL, 0 )
		or die "Can`t get flags for socket: $!\n";
	fcntl( $socket, F_SETFL, $flags | O_NONBLOCK )
		or die "Can`t make socket nonblocking: $!\n";
}

sub add_rule
{
	my $ip = $_[0];
	my $downstream = $_[1];
	my $upstream = $_[2];
	
	my $rule = $RULE;
	
	#заменяем все контсрукции типа [N0] 
	
	while ( $rule =~ /\[N([0-9]+)\]/ )
	{
		my $n = $1;
		my $i = 2;	

		#находим первый свободный номер 
		while( 1 )
		{
			$i++;
			last if ( !exists $RUL_NUMBERS{$i} );
		}
		$RUL_NUMBERS{$i} = "1";
		$rule =~ s/\[N$n\]/$i/g;
		

		$CONTRACT_INDEX{$ip}{$i}=$i;
	}

	#заменяем все ip	
	$rule =~ s/\{A\}/$ip/g;
	#заменяем значения скорости 
	$rule =~ s/\[DOWNSTREAM\]/$downstream/g;
	$rule =~ s/\[UPSTREAM\]/$upstream/g;
	                            
	
	
	
	print "\nrule =".$rule."\n" if ( $debug == 1);

	$CLRULE{$ip} = $rule;
	
	
	#добавляем правило 	
	my $add_rule = $rule;
	$add_rule =~ s/\[OPEN\]((.|\n)+)\[\/OPEN\]((.|\n)*)/${1}/;

	print "\nexecute:\n" if ( $debug == 1 );
	print "\ntime:".localtime()."\n" if ( $debug == 1 );
	 
	my @rules = split( /\|/, $add_rule );
        foreach $line ( @rules )
        {
	        print "$line\n" if ( $debug == 1 );
	        $err = `$line`;

        }
	#запоминаем правило на удаление в кэш 
	my $del_rule = $rule;
	$del_rule =~ s/((.|\n)*)\[CLOSE\]((.|\n)+)\[\/CLOSE\]((.|\n)*)/${3}/;
	print "\ndelete =".$del_rule."\n" if ( $debug == 1 );
	$DEL_RULES{$ip}=$del_rule

#	DEL_RULES

}

sub delete_rule
{
	my $ip = $_[0];


	my $rule = $DEL_RULES{$ip};
       	my @rules = split( /\|/, $rule );
	print "\nexecute:\n" if ( $debug == 1 );
	print "\ntime:".localtime()."\n" if ( $debug == 1 );
        foreach $line ( @rules )
        {
	        print "$line\n" if ( $debug == 1 );
	        $err = `$line`;

        }

	delete $DEL_RULES{$ip};	
	delete $CLRULE{$ip};


	foreach my $idx ( keys %{$CONTRACT_INDEX{$ip}} )
	{
	    print "\ndeleting rule index ".$CONTRACT_INDEX{$ip}{$idx}."\n";		
	    delete $RUL_NUMBERS{$CONTRACT_INDEX{$ip}{$idx}}; 	       	
	}	

	
	delete $CONTRACT_INDEX{$ip};
}
