#!/usr/local/bin/perl
# NAS controlling functions
# get_acct_info
# hangup
# check_activity
#
#
#*******************************************************************

use BER;
use SNMP_Session;
use SNMP_util;
use Radius;

my $NAS;
my $nas_type  = '';
my %stats     = ();
my $USER_NAME = '';

use FindBin '$Bin';

#*******************************************************************
# Hangup active port
# hangup($NAS_HASH_REF, $PORT, $USER, $attr);
#*******************************************************************
sub hangup {
  my ($Nas, $PORT, $USER, $attr) = @_;

  $NAS       = $Nas;
  $nas_type  = $NAS->{NAS_TYPE};
  $USER_NAME = $USER;

  if ($nas_type eq 'exppp') {
    hangup_exppp($NAS, $PORT, $attr);
  }
  elsif (-f "$lib_path/nas/$nas_type" . '.pm') {
    require "$lib_path/nas/$nas_type" . '.pm';
    my $fn = 'hangup_' . $nas_type;
    if (defined($fn)) {
      $fn->($NAS, $PORT, $attr);
    }
  }
  elsif ($nas_type eq 'pm25') {
    hangup_pm25($NAS, $PORT, $attr);
  }
  elsif ($nas_type eq 'radpppd') {
    hangup_radpppd($NAS, $PORT, $attr);
  }
  elsif ($nas_type eq 'mikrotik') {
    hangup_radius($NAS, $PORT, $USER, $attr);

    #hangup_mikrotik_telnet($NAS, $PORT, $USER);
  }
  elsif ($nas_type eq 'chillispot') {
    $NAS->{NAS_MNG_IP_PORT} = "$NAS->{NAS_IP}:3799" if (!$NAS->{NAS_MNG_IP_PORT});
    hangup_radius($NAS, $PORT, $USER, $attr);
  }
  elsif ($nas_type eq 'usr') {
    hangup_snmp(
      $NAS, $PORT,
      {
        OID   => '.1.3.6.1.4.1.429.4.10.13.' . $PORT,
        TYPE  => 'integer',
        VALUE => 9
      }
    );
  }
  elsif ($nas_type eq 'cisco') {
    hangup_cisco($NAS, $PORT, { USER => $USER, %$attr });
  }
  elsif ($nas_type eq 'cisco_isg') {
    hangup_cisco_isg($NAS, $PORT, { USER => $USER, %$attr });
  }
  elsif ($nas_type eq 'mpd') {
    hangup_mpd($NAS, $PORT);
  }
  elsif ($nas_type eq 'mpd4') {
    hangup_mpd4($NAS, $PORT, $attr);
  }
  elsif ($nas_type eq 'mpd5') {
    hangup_mpd5($NAS, $PORT, $USER, { USER => $USER, %$attr });
  }
  elsif ($nas_type eq 'openvpn') {
    hangup_openvpn($NAS, $PORT, $USER);
  }
  elsif ($nas_type eq 'ipcad' || $nas_type eq 'dhcp' || $nas_type eq 'dlink_pb' || $nas_type eq 'dlink' || $nas_type eq 'edge_core') {
    hangup_ipcad($NAS, $PORT, $USER, $attr);
  }
  elsif ($nas_type eq 'patton') {
    hangup_patton29xx($NAS, $PORT, $attr);
  }
  elsif ($nas_type eq 'pppd' || $nas_type eq 'lepppd') {
    hangup_pppd($NAS, $PORT, $attr);
  }
  # http://sourceforge.net/projects/radcoad/
  elsif ($nas_type eq 'pppd_coa') {
    hangup_pppd_coa($NAS, $PORT, $attr);
  }
  elsif ($nas_type eq 'accel_pptp') {
    hangup_radius($NAS, $PORT, "", $attr);
  }
  elsif ($nas_type eq 'redback') {
    hangup_radius($NAS, $PORT, "", $attr);
  }
  elsif ($nas_type eq 'mx80') {
    hangup_radius($NAS, $PORT, "$USER", $attr);
    
    hangup_radius($NAS, $PORT, "$attr->{CID}", $attr) if ($attr->{CID});
  }
  elsif ($nas_type eq 'lisg_cst') {
    hangup_radius($NAS, $PORT, "$attr->{FRAMED_IP_ADDRESS}", $attr);
  }
  else {
    return 1;
  }

  return 0;
}

#*******************************************************************
# Get stats
# get_stats($NAS, $PORT, $USER);
#*******************************************************************
sub get_stats {
  my ($Nas, $PORT, $attr) = @_;

  $NAS      = $Nas;
  $nas_type = $NAS->{NAS_TYPE};

  if ($nas_type eq 'usr') {
    %stats = stats_usrns($NAS, $PORT);
  }
  elsif ($nas_type eq 'patton') {
    %stats = stats_patton29xx($NAS, $PORT);
  }
  elsif ($nas_type eq 'pm25') {
    %stats = stats_pm25($NAS, $PORT);
  }
  elsif ($nas_type eq 'dslmax') {
    my $user_ip_address = $attr->{user_ip_address};
    %stats = stats_dslmax($NAS, $PORT, $user_ip_address);
  }
  else {
    return undef;
  }

  return \%stats;
}

#*******************************************************************
#
# telnet_cmd($hostname, $login, $password, $commands)
#*******************************************************************
sub telnet_cmd {
  my ($hostname, $commands, $attr) = @_;
  my $port = 23;

  if ($hostname =~ /:/) {
    ($hostname, $port) = split(/:/, $hostname, 2);
  }

  my $debug = ($attr->{debug}) ? 1 : 0;
  my $timeout = defined($attr->{'TimeOut'}) ? $attr->{'TimeOut'} : 5;

  use Socket;
  my $dest = sockaddr_in($port, inet_aton("$hostname"));

  if (!socket(SH, PF_INET, SOCK_STREAM, getprotobyname('tcp'))) {
    print "ERR: Can't init '$hostname:$port' $!";
    return 0;
  }

  if (!CORE::connect(SH, $dest)) {
    print "ERR: Can't connect to '$hostname:$port' $!";
    return 0;
  }

  $Log->log_print('LOG_DEBUG', "$USER_NAME", "Connected to $hostname:$port", { ACTION => 'CMD' });

  my $sock   = \*SH;
  my $MAXBUF = 512;
  my $input  = '';
  my $len    = 0;
  my $text   = '';
  my $inbuf  = '';
  my $res    = '';

  my $old_fh = select($SH);
  $| = 1;
  select($old_fh);

  SH->autoflush(1);

  foreach my $line (@$commands) {
    my ($waitfor, $sendtext) = split(/\t/, $line, 2);
    my $wait_len = length($waitfor);
    $input = '';

    if ($waitfor eq '-') {
      send($sock, "$sendtext\n", 0, $dest) or die $Log->log_print('LOG_INFO', "$USER_NAME", "Can't send: '$text' $!", { ACTION => 'CMD' });
    }

    do {
        eval {
          local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n обязателен
          alarm 5;
          recv($sock, $inbuf, $MAXBUF, 0);
          alarm 0;
        };

        # если вышли по тайм-ауту $timeout
        if ($@) {
          last;
        }
        
        $input .= $inbuf;
        $len = length($inbuf);
    } while ($len >= $MAXBUF || $len < $wait_len);

    $Log->log_print('LOG_DEBUG', "$USER_NAME", "Get: \"$input\"\nLength: $len", { ACTION => 'CMD' });
    $Log->log_print('LOG_DEBUG', "$USER_NAME", " Wait for: '$waitfor'",         { ACTION => 'CMD' });

    if ($input =~ /$waitfor/ig) {    # || $waitfor eq '') {
      $text = $sendtext;
      $Log->log_print('LOG_DEBUG', "$USER_NAME", "Send: $text", { ACTION => 'CMD' });
      send($sock, "$text\n", 0, $dest) or die $Log->log_print('LOG_INFO', "$USER_NAME", "Can't send: '$text' $!", { ACTION => 'CMD' });
    }

    $res .= "$input\n";
  }

  close(SH);
  return $res;
}

#**********************************************************
#
#**********************************************************
sub telnet_cmd2 {
  my ($host, $commands, $attr) = @_;
  my $port = 23;

  if ($host =~ /:/) {
    ($host, $port) = split(/:/, $host, 2);
  }

  use IO::Socket;
  use IO::Select;
  my $data;
  my $res;

  my $timeout = defined($attr->{'TimeOut'}) ? $attr->{'TimeOut'} : 5;
  my $socket = new IO::Socket::INET(
    PeerAddr => $host,
    PeerPort => $port,
    Proto    => 'tcp',
    TimeOut  => $timeout
  ) or $Log->log_print('LOG_DEBUG', "$USER_NAME", "ERR: Can't connect to '$host:$port' $!", { ACTION => 'CMD' });
  $Log->log_print('LOG_DEBUG', '', "Connected to $host:$port");

  foreach my $line (@$commands) {
    my ($waitfor, $sendtext) = split(/\t/, $line, 2);

    $socket->send("$sendtext");
    while (<$socket>) {
      $res .= $_;
    }
  }

  close($socket);

  return $res;

}

#####################################################################
# Nas functions

#####################################################################
# Livingston Portmaster functions
#*******************************************************************
# Get stats from Livingston Portmaster
# stats_pm25($NAS, $PORT)
#*******************************************************************
sub stats_pm25 {
  my ($NAS, $PORT) = @_;

  my %stats = (
    in  => 0,
    out => 0
  );

  my $PM25_PORT = $PORT + 2;
  my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';

  my ($in)  = snmpget($SNMP_COM . '@' . $NAS->{NAS_IP}, ".1.3.6.1.2.1.2.2.1.10.$PM25_PORT");
  my ($out) = snmpget($SNMP_COM . '@' . $NAS->{NAS_IP}, ".1.3.6.1.2.1.2.2.1.16.$PM25_PORT");

  if (!defined($in)) {
    $stats{error} = 1;
  }
  elsif (int($in) + int($out) > 0) {
    $stats{in}  = int($in);
    $stats{out} = int($out);
  }

  return %stats;
}

#*******************************************************************
# HANGUP pm25
# hangup_pm25($SERVER, $PORT)
#*******************************************************************
sub hangup_pm25 {
  my ($NAS_IP, $PORT) = @_;

  push @commands, "login:\t$NAS->{NAS_MNG_USER}";
  push @commands, "Password:\t$NAS->{NAS_MNG_PASSWORD}";
  push @commands, ">\treset S$PORT";
  push @commands, ">exit";

  my $result = telnet_cmd("$NAS->{NAS_IP}", \@commands);
  print $result;

  return 0;
}

#####################################################################
# USR Netserver 8/16
#*******************************************************************
# Get stats from USR Netserver 8/16
# get_usrns_stats($SERVER, $PORT)
#*******************************************************************
sub stats_usrns {
  my ($NAS, $PORT) = @_;
  my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';

  #USR trafic taker
  my $in  = `a=\`$SNMPWALK -v 1 -c "$SNMP_COM" $NAS->{NAS_IP} interfaces.ifTable.ifEntry.ifInOctets.$PORT  | awk '{print \$4}'\`; b=\`cat /usr/local/abills/var/devices/$NAS->{NAS_IP}-$PORT.In\`; c=\`expr \$a - \$b + 0\`; echo \$c`;
  my $out = `a=\`$SNMPWALK -v 1 -c "$SNMP_COM" $NAS->{NAS_IP} interfaces.ifTable.ifEntry.ifOutOctets.$PORT  | awk '{print \$4}'\`; b=\`cat /usr/local/abills/var/devices/$NAS->{NAS_IP}-$PORT.Out\`; c=\`expr \$a - \$b + 0\`; echo \$c`;

  $stats{in}  = int($in);
  $stats{out} = int($out);

  return %stats;
}

####################################################################
# Standart FreeBSD ppp
#********************************************************************
# get accounting information from FreeBSD ppp using remove accountin
# scrips
# stats_ppp($NAS)
#********************************************************************
sub stats_ppp {
  my ($NAS_IP, $PORT) = @_;
  use IO::Socket;
  my $port = 30006;

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
  $port = $mng_port || 0;

  my $remote = IO::Socket::INET->new(
    Proto    => "tcp",
    PeerAddr => "$NAS",
    PeerPort => "$port"
  ) or print "cannot connect to pppcons port at $NAS->{NAS_IP}:$port $!\n";

  while (<$remote>) {
    ($radport, $in, $out, $tun) = split(/ +/, $_);
    $stats{ $NAS->{NAS_IP} }{$radport}{in}  = $in;
    $stats{ $NAS->{NAS_IP} }{$radport}{out} = $out;
    $stats{ $NAS->{NAS_IP} }{$radport}{tun} = $tun;
  }
}

####################################################################
# ASCEND DSL Max
#*******************************************************************
# Get stats from DSLMax
# get_dslmax_stats($SERVER, $PORT, $IP)
#*******************************************************************
sub stats_dslmax {
  my ($NAS, $PORT, $IP) = @_;
  my %stats = ();
  my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';

  my $output =
  `id=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS->{NAS_IP} RFC1213-MIB::ipRouteIfIndex.$IP | awk '{ print \$4 }'\`; a=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS IF-MIB::ifInOctets.\$id | awk '{print \$4}'\`; b=\`$SNMPWALK -c "$SNMP_COM" -v1 $NAS IF-MIB::ifOutOctets.\$id | awk '{print \$4}'\`; echo \$a \$b`;

  my ($in, $out) = split(/ /, $out);
  $stats{in}  = $in;
  $stats{out} = $out;
  return %stats;
}

#**********************************************************
# Base SNMP set hangup function
#**********************************************************
sub hangup_snmp {
  my ($NAS, $PORT, $attr) = @_;

  my $oid   = $attr->{OID};
  my $type  = $attr->{TYPE} || 'integer';
  my $value = $attr->{VALUE};

  $Log->log_print('LOG_DEBUG', '', "SNMPSET: $NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP} $oid $type $value", { ACTION => 'CMD' });
  my $result = snmpset("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", "$oid", "$type", $value);

  if ($SNMP_Session::errmsg) {
    $Log->log_print('LOG_ERR', '', "$SNMP_Session::errmrnings / $SNMP_Session::errmsg", { ACTION => 'CMD' });
  }

  return $result;
}

#*******************************************************************
# hangup_radius
#
# Radius-Disconnect messages
# rfc2882
#*******************************************************************
sub hangup_radius {
  my ($NAS, $PORT, $USER, $attr) = @_;

  if (!$NAS->{NAS_MNG_IP_PORT}) {
    print "Radius Hangup failed. Can't find NAS IP and port. NAS: $NAS->{NAS_ID} USER: $USER\n";
    return 'ERR:';
  }

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
  $Log->log_print('LOG_DEBUG', "$USER", "HANGUP: User-Name=$USER Framed-IP-Address=$attr->{FRAMED_IP_ADDRESS} NAS_MNG: $ip:$mng_port '$NAS->{NAS_MNG_PASSWORD}'", { ACTION => 'CMD', NAS => $NAS });

  my %RAD_PAIRS = ();
  $mng_port = 1700 if (!$mng_port);
  my $type;
  my $r = new Radius(
    Host   => "$NAS->{NAS_MNG_IP_PORT}",
    Secret => "$NAS->{NAS_MNG_PASSWORD}"
  ) or return "Can't connect '$NAS->{NAS_MNG_IP_PORT}' $!";

  $conf{'dictionary'} = $base_dir . '/Abills/dictionary' if (!$conf{'dictionary'});

  $r->load_dictionary($conf{'dictionary'});

  $r->add_attributes({ Name => 'User-Name', Value => "$USER" }) if ($USER);
  $r->add_attributes({ Name => 'Framed-IP-Address', Value => "$attr->{FRAMED_IP_ADDRESS}" });
  $r->send_packet(POD_REQUEST) and $type = $r->recv_packet;

  if (!defined $type) {

    # No responce from POD server
    $Log->log_print('LOG_DEBUG', "$USER", "No responce from POD server '$NAS->{NAS_MNG_IP_PORT}'", { ACTION => 'CMD' });
  }

  return $result;
}

#*******************************************************************
# hangup_mikrotik_telnet
#*******************************************************************
sub hangup_mikrotik_telnet {
  my ($NAS_IP, $PORT, $USER) = @_;

  push @commands, "Login:\t$NAS->{NAS_MNG_USER}";
  push @commands, "Password:\t$NAS->{NAS_MNG_PASSWORD}";
  push @commands, ">/interface pptp-server remove [find user=$USER]";
  push @commands, ">quit";

  my $result = telnet_cmd2("$NAS->{NAS_IP}", \@commands);

  print $result;
}

#*******************************************************************
# hangup_ipcad
#*******************************************************************
sub hangup_ipcad {
  my ($NAS_IP, $PORT, $USER_NAME, $attr) = @_;

  my $result = '';
  my $ip        = $attr->{FRAMED_IP_ADDRESS};
  my $netmask   = $attr->{NETMASK} || 32;
  my $FILTER_ID = $attr->{FILTER_ID} || '';


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

  $Ipn->acct_stop({ %$attr, SESSION_ID => $attr->{ACCT_SESSION_ID} });
  if ($NAS->{NAS_TYPE} eq 'dhcp' || $nas_type eq 'dlink_pb' || $nas_type eq 'dlink' || $nas_type eq 'edge_core' ) {
  	$Ipn->query($db, "DELETE FROM dhcphosts_leases WHERE ip=INET_ATON('$ip')", 'do');
  }

  my $num = 0;
  if ($attr->{UID} && $conf{IPN_FW_RULE_UID}) {
    $num = $attr->{UID};
  }
  else {
    my @ip_array = split(/\./, $ip, 4);
    $num = $ip_array[3];
  }

  my $rule_num = $conf{IPN_FW_FIRST_RULE} || 20000;
  $rule_num = $rule_num + 10000 + $num;

  if ($NAS->{NAS_MNG_IP_PORT}) {
    $ENV{NAS_IP_ADDRESS}  = $NAS->{NAS_MNG_IP_PORT};
    $ENV{NAS_MNG_USER}    = $NAS->{NAS_MNG_USER};
    $ENV{NAS_MNG_IP_PORT} = $NAS->{NAS_MNG_IP_PORT};
    $ENV{NAS_ID}          = $NAS->{NAS_ID};
    $ENV{NAS_TYPE}        = $NAS->{NAS_TYPE};
  }

  if ($conf{IPN_FILTER}) {
    my $cmd = "$conf{IPN_FILTER}";
    $cmd =~ s/\%STATUS/HANGUP/g;
    $cmd =~ s/\%IP/$ip/g;
    $cmd =~ s/\%LOGIN/$USER_NAME/g;
    $cmd =~ s/\%FILTER_ID/$FILTER_ID/g;
    $cmd =~ s/\%UID/$UID/g;
    $cmd =~ s/\%PORT/$PORT/g;
    system($cmd);
    print "IPN FILTER: $cmd\n" if ($attr->{debug} && $attr->{debug} > 5);
  }

  if ($conf{IPN_FW_STOP_RULE}) {
    my $cmd = $conf{IPN_FW_STOP_RULE};
    $cmd =~ s/\%IP/$ip/g;
    $cmd =~ s/\%MASK/$netmask/g;
    $cmd =~ s/\%NUM/$rule_num/g;
    $cmd =~ s/\%LOGIN/$USER_NAME/g;

    $Log->log_print('LOG_DEBUG', '', "$cmd", { ACTION => 'CMD' });
    if ($attr->{debug} && $attr->{debug} > 4) {
      print $cmd. "\n";
    }
    $result = system($cmd);
  }

  print $result;
}

#*******************************************************************
# hangup_openvpn
#*******************************************************************
sub hangup_openvpn {
  my ($NAS, $PORT, $USER, $attr) = @_;

  my @commands = (">INFO:OpenVPN Management Interface Version 1 -- type 'help' for more info\tkill $USER", "SUCCESS: common name '$USER' found, 1 client(s) killed\texit");

  my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands);
  $Log->log_print('LOG_DEBUG', $USER, "$result", { ACTION => 'CMD' });

  return 0;
}

#*******************************************************************
# HANGUP Cisco ISG
#
# ip rcmd rcp-enable
# ip rcmd rsh-enable
# no ip rcmd domain-lookup
# ! ip rcmd remote-host имя_юзера_на_cisco IP_address_или_имя_компа_с_которого_запускается_скрипт имя_юзера_от_чьего_имени_будет_запукаться_скрипт enable
# ! например
# ip rcmd remote-host admin 192.168.0.254 root enable
#
#*******************************************************************
sub hangup_cisco_isg {
  my ($NAS, $PORT, $attr) = @_;
  my $exec;
  my $command = '';
  my $user    = $attr->{USER};

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);

  #RSH Version
  if ($NAS->{NAS_MNG_USER}) {

    # имя юзера на циско котрому разрешен rsh и хватает привелегий для сброса
    my $cisco_user = $NAS->{NAS_MNG_USER};
    $command = "/usr/bin/rsh -l $cisco_user $NAS->{NAS_IP} clear ip subscriber ip $attr->{FRAMED_IP_ADDRESS}";

    #"clear subscriber session username $user";
    $Log->log_print('LOG_DEBUG', $user, "$command", { ACTION => 'CMD' });
    $exec = `$command`;
  }

  # RADIUS POD Version
  else {
    my $type;
    my $r = new Radius(
      Host   => "$NAS->{NAS_MNG_IP_PORT}",
      Secret => "$NAS->{NAS_MNG_PASSWORD}"
    ) or return "Can't connect '$NAS->{NAS_MNG_IP_PORT}' $!";

    $conf{'dictionary'} = '/usr/local/abills/Abills/dictionary' if (!$conf{'dictionary'});
    $r->load_dictionary($conf{'dictionary'});

    $r->add_attributes({ Name => 'User-Name',          Value => "$attr->{USER}" });
    $r->add_attributes({ Name => 'Cisco-Account-Info', Value => "S$attr->{FRAMED_IP_ADDRESS}" });
    $r->add_attributes({ Name => 'Cisco-AVPair',       Value => "subscriber:command=account-logoff" });

    $r->send_packet(43) and $type = $r->recv_packet;

    my %RAD_PAIRS = ();
    for my $a ($r->get_attributes) {
      $RAD_PAIRS{ $a->{'Name'} } = $a->{'Value'};
    }

    if ($RAD_PAIRS{'Error-Cause'}) {

      #log_print('LOG_WARNING', "$RAD_PAIRS{'Error-Cause'} / $RAD_PAIRS{'Reply-Message'}");
      print "$RAD_PAIRS{'Error-Cause'} / $RAD_PAIRS{'Reply-Message'}";
      print %RAD_PAIRS;
    }

    #print "Can't find 'NAS_MNG_USER'\n";
  }

  return $exec;
}

#*******************************************************************
# HANGUP Cisco
# hangup_cisco($SERVER, $PORT)
#
# Cisco config  for rsh functions:
#
# ip rcmd rcp-enable
# ip rcmd rsh-enable
# no ip rcmd domain-lookup
# ! ip rcmd remote-host имя_юзера_на_cisco IP_address_или_имя_компа_с_которого_запускается_скрипт имя_юзера_от_чьего_имени_будет_запукаться_скрипт enable
# ! например
# ip rcmd remote-host admin 192.168.0.254 root enable
#
#*******************************************************************
sub hangup_cisco {
  my ($NAS, $PORT, $attr) = @_;
  my $exec;
  my $command = '';
  my $user    = $attr->{USER};

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);

  #POD Version
  if ($mng_port && $mng_port == 1700) {
    hangup_radius($NAS, $PORT, "$user", $attr);
  }

  #Rsh version
  elsif ($NAS->{NAS_MNG_USER}) {

    # имя юзера на циско котрому разрешен rsh и хватает привелегий для сброса
    my $cisco_user = $NAS->{NAS_MNG_USER};

    # использование: NAS-IP-Address NAS-Port SQL-User-Name

    if ($PORT > 0) {
      $|       = 1;
      $command = "(/bin/sleep 5; /bin/echo 'y') | /usr/bin/rsh -4 -l $cisco_user $NAS->{NAS_IP} clear line $PORT";
      $Log->log_print('LOG_DEBUG', "$user", "$command", { ACTION => 'CMD' });
      $exec = `$command`;
      return $exec;
    }

    $command = "/usr/bin/rsh -l $cisco_user $NAS->{NAS_IP} show users | grep -i \" $user \" ";

    #| awk '{print \$1}';";
    $Log->log_print('LOG_DEBUG', "$command");
    my $out = `$command`;

    if ($out eq '') {
      print 'Can\'t get VIRTUALINT. Check permissions';
      return 'Can\'t get VIRTUALINT. Check permissions';
    }

    my $VIRTUALINT;

    if ($out =~ /\s+(\d+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)/) {
      $VIRTUALINT = $1;
      $tty        = $2;
      $line       = $3;
      $cuser      = $4;
      $chost      = $5;

      print "$VIRTUALINT, $tty, $line, $cuser, $chost";
    }

    $command = "echo $VIRTUALINT echo  | sed -e \"s/[[:alpha:]]*\\([[:digit:]]\\{1,\\}\\)/\\1/\"";
    $Log->log_print('LOG_DEBUG', "$command");
    $PORT    = `$command`;
    $command = "/usr/bin/rsh -4 -n -l $cisco_user $NAS->{NAS_IP} clear interface Virtual-Access $PORT";
    $Log->log_print('LOG_DEBUG', $user, "$command", { ACTION => 'CMD' });
    $exec = `$command`;
  }
  else {

    #SNMP version
    my $SNMP_COM = $NAS->{NAS_MNG_PASSWORD} || '';

    my $INTNUM = snmpget("$SNMP_COM\@$NAS->{NAS_IP}", ".1.3.6.1.2.1.4.21.1.2.$attr->{FRAMED_IP_ADDRESS}");
    $Log->log_print('LOG_DEBUG', "$user", "SNMP: $SNMP_COM\@$NAS->{NAS_IP} .1.3.6.1.2.1.4.21.1.2.$attr->{FRAMED_IP_ADDRESS}", { ACTION => 'CMD' });
    $exec = snmpset("$SNMP_COM\@$NAS->{NAS_IP}", ".1.3.6.1.2.1.2.2.1.7.$INTNUM", 'integer', 2);
    $Log->log_print('LOG_DEBUG', "$user", "SNMP: $SNMP_COM\@$NAS->{NAS_IP} .1.3.6.1.2.1.2.2.1.7.$INTNUM integer 2", { ACTION => 'CMD' });
  }

  return $exec;
}

#*******************************************************************
# HANGUP dslmax
# hangup_dslmax($SERVER, $PORT)
#*******************************************************************
sub hangup_dslmax {
  my ($NAS_IP, $PORT) = @_;

  #cotrol
  my @commands = ();
  push @commands, "word:\t$NAS->{NAS_MNG_PASSWORD}";
  push @commands, ">\treset S$NAS->{PORT}";
  push @commands, ">exit";

  my $result = telnet_cmd("$NAS->{NAS_IP}", \@commands);

  print $result;

  return 0;
}

#####################################################################
# Exppp functions
#*******************************************************************
# HANGUP ExPPP
# hangup_exppp($SERVER, $PORT)
#*******************************************************************
sub hangup_exppp {
  my ($NAS, $PORT) = @_;
  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);

  my $ctl_port = $mng_port + $PORT;
  my $PPPCTL   = '/usr/sbin/pppctl';
  my $out      = `$PPPCTL -p "$NAS->{NAS_MNG_PASSWORD}" $NAS->{NAS_IP}:$ctl_port down`;

  return 0;
}

#*******************************************************************
# HANGUP MPD
# hangup_mpd4($SERVER, $PORT)
#*******************************************************************
sub hangup_mpd4 {
  my ($NAS, $PORT, $attr) = @_;

  my $ctl_port = "pptp$PORT";
  if ($attr->{ACCT_SESSION_ID}) {
    if ($attr->{ACCT_SESSION_ID} =~ /\d+\-(.+)/) {
      $ctl_port = $1;

    }
  }

  my @commands = ("\t", "Username: \t$NAS->{NAS_MNG_USER}", "Password: \t$NAS->{NAS_MNG_PASSWORD}", "\\[\\] \tbundle $ctl_port", "\] \tclose", "\] \texit");

  my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands);
  return 0;
}

#*******************************************************************
# HANGUP MPD
# hangup_mpd5($SERVER, $PORT)
#*******************************************************************
sub hangup_mpd5 {
  my ($NAS, $PORT, $USER, $attr) = @_;

  if (!$NAS->{NAS_MNG_IP_PORT}) {
    print "MPD Hangup failed. Can't find NAS IP and port. NAS: $NAS->{NAS_ID}\n";
    return "Error";
  }

  if ($NAS->{NAS_MNG_USER} eq '') {
    return hangup_radius($NAS, $PORT, $USER, $attr);
  }

  my $ctl_port = "L-$PORT";
  if ($attr->{ACCT_SESSION_ID}) {
    if ($attr->{ACCT_SESSION_ID} =~ /^\d+\-(.+)/) {
      $ctl_port = $1;
    }
  }

  $Log->log_print('LOG_DEBUG', $USER_NAME, " HANGUP: SESSION: $ctl_port NAS_MNG: $NAS->{NAS_MNG_IP_PORT} '$NAS->{NAS_MNG_PASSWORD}'", { ACTION => 'CMD' });

  my @commands = ("\t", "Username: \t$NAS->{NAS_MNG_USER}", "Password: \t$NAS->{NAS_MNG_PASSWORD}", "\\[\\] \tlink $ctl_port", "\] \tclose", "\] \texit");

  if ($attr->{IFACE}) {
    $commands[3] = "\\[\\] \tiface $attr->{IFACE}";
  }

  my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands, { debug => 1 });

  return $result;
}

#*******************************************************************
# HANGUP MPD
# hangup_mpd($SERVER, $PORT)
#*******************************************************************
sub hangup_mpd {
  my ($NAS_IP, $PORT) = @_;

  my $ctl_port = "pptp$PORT";
  my @commands = ("\]\tlink $ctl_port", "\]\tlink $ctl_port", "\]\tclose", "\]\texit");

  my $result = telnet_cmd("$NAS->{NAS_MNG_IP_PORT}", \@commands);
  print $result;
  return 0;
}

#####################################################################
# radppp functions
#*******************************************************************
# HANGUP radpppd
# hangup_radpppd($SERVER, $PORT)
#*******************************************************************
sub hangup_radpppd {
  my ($NAS_IP, $PORT) = @_;

  my $RUN_DIR = '/var/run';
  my $AWK     = '/bin/awk';
  my $CAT     = '/bin/cat';
  my $GREP    = '/bin/grep';
  my $ROUTE   = '/sbin/route';
  my $KILL    = '/bin/kill';

  my $PID_FILE = "$RUN_DIR/PPP$PORT.pid";
  my $PPP_PID  = `$CAT $PID_FILE`;
  my $a        = `$KILL -1 $PPP_PID`;

  return 0;
}

#*******************************************************************
# Get stats for pppd connection from firewall
#
# get_pppd_stats ($SERVER, $PORT, $IP)
#*******************************************************************
sub stats_pppd {
  my ($NAS, $PORT, $IP) = @_;

  my $firstnumber = 1000;
  my $step        = 10;
  my $innum       = $firstnumber + $PORT * $step;
  my $outnum      = $firstnumber + $PORT * $step + 5;

  $stats{ $NAS->{NAS_IP} }{$PORT}{in}  = 0;
  $stats{ $NAS->{NAS_IP} }{$PORT}{out} = 0;

  # 01000    369242     53878162 count ip from any to any in via 217.196.163.253
  open(FW, "/usr/sbin/ipfw $innum $outnum") || die "Can't open '/usr/sbin/ipfw' $!\n";
  while (<FW>) {
    ($num, $bbyte, $bytes, $trash) = split(/ +/, $_, 4);
    if ($innum == $num) {
      $stats{$SERVER}{$PORT}{in} = $bytes;
    }
    elsif ($outnum == $num) {
      $stats{$SERVER}{$PORT}{in} = $bytes;
    }
  }
  close(FW);

}

#*******************************************************************
# HANGUP pppd
# hangup_pppd($SERVER, $PORT)
# add next string to  /etc/sudoers:
#
# apache   ALL = NOPASSWD: /usr/local/abills/misc/pppd_kill
#
#*******************************************************************
sub hangup_pppd {
  my ($NAS, $id, $attr) = @_;
  my $IP     = $attr->{FRAMED_IP_ADDRESS};
  my $result = '';

  if ($NAS->{NAS_MNG_IP_PORT} =~ /:/) {
    my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
    use IO::Socket;

    my $remote = IO::Socket::INET->new(
      Proto    => "tcp",
      PeerAddr => "$ip",
      PeerPort => $mng_port
    ) or die "cannot connect to pppd disconnect port at $ip:$mng_port $!\n";

    print $remote "$IP\n";
    $result = <$remote>;
    print "Hanguped: $IP\n" if ($debug > 1);
  }
  else {
    $result = system("/usr/bin/sudo /usr/local/abills/misc/pppd_kill $IP");
  }

  return $result;
}

#*******************************************************************
# HANGUP Patton 29xx
#*******************************************************************
sub hangup_patton29xx {
  my ($NAS, $PORT, $attr) = @_;
  my $exec = '';

  # Get active sessions
  my %active = ();
  my @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3");
  foreach my $line (@arr) {
    if ($line =~ /(\d+):6/) {
      $active{$1} = 1;
    }
  }

  #Get iface
  @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.9");
  foreach my $line (@arr) {
    if ($line =~ /(\d+):(\d+)/) {
      if ($2 == $PORT && $active{$1}) {
        $exec = snmpset("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3.$1", 'integer', 10);

        #print " IFACE: $iface INDEX $1 IN: $in OUT: $out\n";
        last;
      }
    }
  }

  return $exec;
}

#*******************************************************************
# Get stats from Patton RAS 29xx
#
#*******************************************************************
sub stats_patton29xx {
  my ($NAS, $PORT) = @_;

  my %stats = (
    in  => 0,
    out => 0
  );

  # Get active sessions
  my %active = ();
  my @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.3");
  foreach my $line (@arr) {
    if ($line =~ /(\d+):6/) {
      $active{$1} = 1;
    }
  }

  #Get iface
  @arr = snmpwalk("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.9");
  foreach my $line (@arr) {
    if ($line =~ /(\d+):(\d+)/) {
      if ($2 == $PORT && $active{$1}) {
        $stats{out} = snmpget("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.36.$1");
        $stats{in}  = snmpget("$NAS->{NAS_MNG_PASSWORD}\@$NAS->{NAS_IP}", ".1.3.6.1.4.1.1768.5.100.1.37.$1");

        $Log->log_print('LOG_DEBUG', '', "IFACE: $line INDEX $1 IN: $stats{in} OUT: $stats{out}", { ACTION => 'CMD' });
        last;
      }
    }
  }

  return %stats;
}

#*******************************************************************
# hangup_hangup_pppd_coa
#
# Radius-Disconnect messages for radcoad
# rfc3576
#*******************************************************************
sub hangup_pppd_coa {
  my ($NAS, $PORT, $attr) = @_;

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
  $Log->log_print('LOG_DEBUG', '', " HANGUP: NAS_MNG: $ip:$mng_port '$NAS->{NAS_MNG_PASSWORD}'", { ACTION => 'CMD' });

  my %RAD_PAIRS = ();
  my $type;
  my $result = 0;
  my $r      = new Radius(
    Host   => "$NAS->{NAS_MNG_IP_PORT}",
    Secret => "$NAS->{NAS_MNG_PASSWORD}"
  ) or return "Can't connect '$NAS->{NAS_MNG_IP_PORT}' $!";

  $conf{'dictionary'} = '/usr/local/abills/Abills/dictionary' if (!$conf{'dictionary'});

  $r->load_dictionary($conf{'dictionary'});

  $r->add_attributes({ Name => 'Framed-Protocol', Value => 'PPP' }, { Name => 'NAS-Port', Value => "$PORT" });
  $r->add_attributes({ Name => 'Framed-IP-Address', Value => "$attr->{FRAMED_IP_ADDRESS}" }) if $attr->{FRAMED_IP_ADDRESS};
  $r->send_packet(POD_REQUEST) and $type = $r->recv_packet;

  if (!defined $type) {

    # No responce from POD server
    $result = 1;
    $Log->log_print('LOG_DEBUG', '', "No responce from POD server '$NAS->{NAS_MNG_IP_PORT}' ", { ACTION => '' });
  }

  return $result;
}

#*******************************************************************
# Set speed for port
# setspeed($NAS_HASH_REF, $PORT, $USER, $UPSPEED, $DOWNSPEED, $attr);
#*******************************************************************
sub setspeed {
  my ($Nas, $PORT, $USER, $UPSPEED, $DOWNSPEED, $attr) = @_;

  $NAS      = $Nas;
  $nas_type = $NAS->{NAS_TYPE};

  if ($nas_type eq 'pppd_coa') {
    return setspeed_pppd_coa($NAS, $PORT, $UPSPEED, $DOWNSPEED, $attr);
  }
  else {
    return -1;
  }

  return 0;
}

#*******************************************************************
# Check CoA support
# hascoa($NAS_HASH_REF, $attr);
#*******************************************************************
sub hascoa {
  my ($Nas, $attr) = @_;

  $NAS      = $Nas;
  $nas_type = $NAS->{NAS_TYPE};

  if ($nas_type eq 'pppd_coa') {
    return 1;
  }
  else {
    return 0;
  }
}

#*******************************************************************
# setspeed_pppd_coa
#
# Radius-CoA messages for radcoad
# rfc3576
#*******************************************************************
sub setspeed_pppd_coa {
  my ($NAS, $PORT, $UPSPEED, $DOWNSPEED, $attr) = @_;

  my ($ip, $mng_port) = split(/:/, $NAS->{NAS_MNG_IP_PORT}, 2);
  $Log->log_print('LOG_DEBUG', '', " SETSPEED: NAS_MNG: $ip:$mng_port '$NAS->{NAS_MNG_PASSWORD}'");

  my %RAD_PAIRS = ();
  my $type;
  my $result = 0;
  my $r      = new Radius(
    Host   => "$NAS->{NAS_MNG_IP_PORT}",
    Secret => "$NAS->{NAS_MNG_PASSWORD}"
  ) or return "Can't connect '$NAS->{NAS_MNG_IP_PORT}' $!";

  $conf{'dictionary'} = '/usr/local/abills/Abills/dictionary' if (!$conf{'dictionary'});

  $r->load_dictionary($conf{'dictionary'});

  $r->add_attributes({ Name => 'Framed-Protocol', Value => 'PPP' }, { Name => 'NAS-Port', Value => "$PORT" }, { Name => 'PPPD-Upstream-Speed-Limit', Value => "$UPSPEED" }, { Name => 'PPPD-Downstream-Speed-Limit', Value => "$DOWNSPEED" });
  $r->add_attributes({ Name => 'Framed-IP-Address', Value => "$attr->{FRAMED_IP_ADDRESS}" }) if $attr->{FRAMED_IP_ADDRESS};
  $r->send_packet(COA_REQUEST) and $type = $r->recv_packet;

  if (!defined $type) {

    # No responce from CoA server
    #log_print('LOG_DEBUG',
    print "No responce from CoA server '$NAS->{NAS_MNG_IP_PORT}'";
    return 1;
  }

  return $result;
}

1
