#!/usr/bin/perl -w
# ppp
# external script for traffic shapping
#
#-------------------------------------------------------------
# /etc/ppp/ppp.linkup
#
# MYADDR:
# !bg /usr/abills/libexec/linkupdown up INTERFACE USER HISADDR
#
# /etc/ppp/ppp.linkdown
#
# MYADDR:
# !bg /usr/abills/libexec/linkupdown down INTERFACE USER HISADDR
#
#-------------------------------------------------------------
# /usr/local/etc/mpd/mpd.conf
#
# set iface up-script "/usr/abills/libexec/linkupdown mpd up"
# set iface down-script "/usr/abills/libexec/linkupdown mpd down"
#-------------------------------------------------------------
# For Linux
#
# /etc/ppp/ip-up.local 
# /usr/abills/libexec/linkupdown pppd up $1 'username' $4
#
# /etc/ppp/ip-down.local 
# /usr/abills/libexec/linkupdown pppd down $1 'username' $4
#




my $debug=0;

#If not anought arguments
if ( $#ARGV < 3 ) {
 print qq{Not anought argumets:
 Example:

 /etc/ppp/ppp.linkup
 MYADDR:
   !bg /usr/abills/libexec/linkupdown up INTERFACE USER HISADDR

 /etc/ppp/ppp.linkdown
 MYADDR:
   !bg /usr/abills/libexec/linkupdown down INTERFACE USER HISADDR

----------------------------------------------------------------
 /usr/local/etc/mpd/mpd.conf
   set iface up-script "/usr/abills/libexec/linkupdown mpd up"
   set iface down-script "/usr/abills/libexec/linkupdown mpd down"
----------------------------------------------------------------
 For Linux:
 /etc/ppp/ip-up.local 
   /usr/abills/libexec/linkupdown pppd up \$1 'username' \$4

 /etc/ppp/ip-down.local 
   /usr/abills/libexec/linkupdown pppd down \$1 'username' \$4
DEBUG: $debug
};
exit 0;
}


use vars  qw(%RAD %conf $db @START_FW);
use strict;

use FindBin '$Bin';
require $Bin . '/config.pl';
#use lib '../', "../Abills/$conf{dbtype}";
unshift(@INC, $Bin . '/../', $Bin ."/../Abills/$conf{dbtype}");
require Abills::Base;
Abills::Base->import();
my $begin_time = check_time();

require Abills::SQL;
my $sql = Abills::SQL->connect($conf{dbtype}, "$conf{dbhost}", $conf{dbname}, $conf{dbuser}, $conf{dbpasswd});
my $db = $sql->{db};

require Billing;
Billing->import();
my $Billing = Billing->new($db, \%conf);







my $IPFW='/sbin/ipfw';
#my @START_FW = (3000, 2000, 1000);

# Arguments
my ($ACTION, $INTERFACE, $USER, $HISADDR, $inum);

#MPD
if ($ARGV[0] eq 'mpd') {
  $ACTION=$ARGV[1];
  $INTERFACE=$ARGV[2];
  $USER=$ARGV[6];
  $HISADDR=$ARGV[5];
  $inum = $INTERFACE;
  $inum =~ s/ng//;
}
#pppd
elsif($ARGV[0] eq 'pppd') {
  $ACTION=$ARGV[1];
  $INTERFACE=$ARGV[2];
  $USER=$ARGV[3];
  $HISADDR=$ARGV[4];
  $inum = $INTERFACE;
  $inum =~ s/ppp//;
 }
# FreeBSD PPP
else {
  $ACTION=$ARGV[0];
  $INTERFACE=$ARGV[1];
  $USER=$ARGV[2];
  $HISADDR=$ARGV[3];
  $inum = $INTERFACE;
  $inum =~ s/tun//;
} 


require Admins;
Admins->import();
require Dv;
Dv->import();


my $admin = Admins->new($db);
$admin->info($conf{SYSTEM_ADMIN_ID}, { IP => '127.0.0.1' });
my $Dv = Dv->new($db, $admin, \%conf); 


my @FW_ACTIONS = ();
# Flush rules
  my($fw_num, $pipe_num, $fw_nums, $pipe_nums);
  
  foreach my $num (@START_FW) {
    $fw_num =     $num + $inum * 2;
    $pipe_num =   $num + $inum * 2;
    $fw_nums .=   ' ' . ($fw_num)   .' '. ($fw_num +1);
    $pipe_nums .= ' ' . ($pipe_num) .' '. ($pipe_num+1);
  }

my %LIST_PARAMS = ();

if ($ARGV[0] eq 'pppd') {
	push @FW_ACTIONS, "/sbin/tc qdisk del dev $INTERFACE";
	$LIST_PARAMS{IP}=$HISADDR;
} 
else {
  push @FW_ACTIONS, "$IPFW -f delete $fw_nums";
  push @FW_ACTIONS, "$IPFW -f pipe delete $pipe_nums";
  $LIST_PARAMS{LOGIN} = "$USER";
}

# Up fw shaper rules
if ($ACTION eq 'up') {
  my %speeds = ();
  my %nets = ();
  
  my $user = $Dv->info(0, { %LIST_PARAMS });

  if(defined($Dv->{errno})) {
     print "[$Dv->{errno}] $Dv->{errstr}\n";
     exit 1;
   }
  elsif($Dv->{TOTAL} < 1){
    print "$USER - Not exist\n";
    exit 1;
   }



  if ($user->{SPEED} > 0) {
    $speeds{0}{IN}=int($user->{SPEED});
    $speeds{0}{OUT}=int($user->{SPEED});
   }
  else {
    my ($remaining_time, $ret_attr); 


    ($user->{TIME_INTERVALS},
     $user->{INTERVAL_TIME_TARIF}, 
     $user->{INTERVAL_TRAF_TARIF}) = $Billing->time_intervals($user->{TP_ID});

    ($remaining_time, $ret_attr) = $Billing->remaining_time($user->{DEPOSIT}, {
    	    TIME_INTERVALS      => $user->{TIME_INTERVALS},
          INTERVAL_TIME_TARIF => $user->{INTERVAL_TIME_TARIF},
          INTERVAL_TRAF_TARIF => $user->{INTERVAL_TRAF_TARIF},
          SESSION_START       => $user->{SESSION_START},
          DAY_BEGIN           => $user->{DAY_BEGIN},
          DAY_OF_WEEK         => $user->{DAY_OF_WEEK},
          DAY_OF_YEAR         => $user->{DAY_OF_YEAR},
          REDUCTION           => $user->{REDUCTION},
          POSTPAID            => 1
         });

#    print "RT: $remaining_time\n"  if ($debug == 1);
    my %TT_IDS = %$ret_attr;


    if (keys %TT_IDS > 0) {
    	
      require Tariffs;
      Tariffs->import();
      my $tariffs = Tariffs->new($db, $admin);

      #Get intervals
      while(my($k, $v)=each( %TT_IDS)) {
# 	      print "$k, $v\n";
        $user->{TI_ID}=$v;
 	      my $list = $tariffs->tt_list({ TI_ID => $v });
 	      foreach my $line (@$list)  {
 	    	  $speeds{$line->[0]}{IN}="$line->[4]";
 	    	  $speeds{$line->[0]}{OUT}="$line->[5]";
 	    	  $nets{$line->[0]}="$line->[7]";
 	    	  #print "$line->[0] $line->[4]\n";
 	      }
      }
    }
  
   }

 # speed apply
  my $fw_num_in    =0;
  my $fw_num_out   =0;
  my $pipe_num_in  =0;
  my $pipe_num_out =0;



  while(my ($traf_type, $speed)=each %speeds) {
    $fw_num_in  = $START_FW[$traf_type] + $inum * 2;
    $fw_num_out = $START_FW[$traf_type] + $inum * 2 + 1;
    $pipe_num_in  = $START_FW[$traf_type] + $inum * 2 ;
    $pipe_num_out = $START_FW[$traf_type] + $inum * 2 + 1;
    
    my $speed_in  = (defined($speed->{IN}))  ? $speed->{IN}  : 0;
    my $speed_out = (defined($speed->{OUT})) ? $speed->{OUT} : 0;

    #Linux tc shaper
    if ($ARGV[0] eq 'pppd') {
       # In
      if ($speed_in > 0) {
        if ($traf_type == 0) {
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE root handle $fw_num_out: htb default 10";
          push @FW_ACTIONS, "/sbin/tc class add dev $INTERFACE parent $fw_num_out: classid 1:10 htb rate $speed_in";
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE parent $fw_num_out:10 handle 10: sfq perturb 10";
         }
        else {
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE root handle $fw_num_out: htb default 10";
          push @FW_ACTIONS, "/sbin/tc class add dev $INTERFACE parent $fw_num_out: classid 1:10 htb rate $speed_out";
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE parent $fw_num_out:10 handle 10: sfq perturb 10";
         }
       }

      if ($speed_out > 0) {
        if ($traf_type == 0) {
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE root handle $fw_num_out: htb default 10";
          push @FW_ACTIONS, "/sbin/tc class add dev $INTERFACE parent $fw_num_out: classid 1:10 htb rate $speed_in";
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE parent $fw_num_out:10 handle 10: sfq perturb 10";
         }
        else {
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE root handle $fw_num_out: htb default 10";
          push @FW_ACTIONS, "/sbin/tc class add dev $INTERFACE parent $fw_num_out: classid 1:10 htb rate $speed_out";
          push @FW_ACTIONS, "/sbin/tc qdisc add dev $INTERFACE parent $fw_num_out:10 handle 10: sfq perturb 10";
         }
       }
      
     }
    #FreeBSD ipfw pipe shaper
    else {
   	  $nets{$traf_type} =~ s/[\r]?\n//g;
  	  $nets{$traf_type} =~ s/;/,/g;
      my @nets_arr = split(/;|,/, $nets{$traf_type});

     if ($speed_in > 0) {
    	  #In;
        if ($traf_type == 0) {
          # In
          push @FW_ACTIONS, "$IPFW add $fw_num_in pipe $pipe_num_in ip from any to any via $INTERFACE in";
          push @FW_ACTIONS, "$IPFW pipe $pipe_num_in config bw ". $speed_in ."Kbit/s queue 10Kbytes";
         }
        else {
          #ipfw tables
          if ($#nets_arr > 5) {
             my $table_number = $user->{TI_ID};

             my $result = `$IPFW table $table_number list`;
             if ($result eq '') {
               foreach my $line (@nets_arr) {
               	  push @FW_ACTIONS, "$IPFW table $table_number add $line";
                }
              }
            push @FW_ACTIONS, "$IPFW add $fw_num_in pipe $pipe_num_in ip from table\\($table_number\\) to any via $INTERFACE in";
           }
          else {
            push @FW_ACTIONS, "$IPFW add $fw_num_in pipe $pipe_num_in ip from $nets{$traf_type} to any via $INTERFACE in";
           }
            push @FW_ACTIONS, "$IPFW pipe $pipe_num_in config bw ". $speed_in ."Kbit/s queue 10Kbytes";
         }
       }

      if ($speed_out > 0) {
        if ($traf_type == 0) {
          push @FW_ACTIONS, "$IPFW add $fw_num_out pipe $pipe_num_out ip from any to any via $INTERFACE out";
          push @FW_ACTIONS, "$IPFW pipe $pipe_num_out config bw ". $speed_out ."Kbit/s queue 10Kbytes";
         }
        else {
          if ($#nets_arr > 5) {
             my $table_number = $user->{TI_ID};

             my $result = `$IPFW table $table_number list`;
             if ($result eq '') {
               foreach my $line (@nets_arr) {
               	  push @FW_ACTIONS, "$IPFW table $table_number add $line";
                }
              }
            push @FW_ACTIONS, "$IPFW add $fw_num_out pipe $pipe_num_out ip from table\\($table_number\\) to any via $INTERFACE out";
           }
          else {
            push @FW_ACTIONS, "$IPFW add $fw_num_out pipe $pipe_num_out ip from $nets{$traf_type} to any via $INTERFACE out";
           }


          push @FW_ACTIONS, "$IPFW pipe $pipe_num_out config bw ". $speed_out ."Kbit/s queue 10Kbytes";
         }
       }
    }
    
  }

 }

#make firewall actions
foreach my $line (@FW_ACTIONS) {
  if ($debug == 1) {
    print "$line\n";	
   }	
  else {
    system("$line");
   }
}
