#!/usr/bin/perl
#Switch mac and speed assign

use FindBin '$Bin';

my $debug   = 0;
my $version = 0.58;

require $Bin . '/../libexec/config.pl';
unshift(@INC, $Bin . '/../', $Bin . '/../Abills', $Bin . "/../Abills/$conf{dbtype}");
require Abills::SQL;
Abills::SQL->import();
require Abills::Base;
Abills::Base->import();

require Admins;
Admins->import();
require Dv_Sessions;
Dv_Sessions->import();
require Dv;
Dv->import();
require Dhcphosts;
Dhcphosts->import();
require Nas;
Nas->import();

use Socket;

require Snmputils;
Snmputils->import();
require SNMP_Session;
SNMP_Session->import();
require SNMP_util;
SNMP_util->import();
require BER;
BER->import();
require Dv;
Dv->import();
require Billing;
Billing->import();

my $ARGV = parse_arguments(\@ARGV);

if ($ARGV->{help} || $#ARGV == -1) {
  print << "[END]";
Version: $version
snmp_control.pl (ONLINE_ENABLE|ONLINE_DISABLE|HANGUP) %LOGIN %FILTER_ID %PORT
  SHOW_VLANS=[NAS_ID] - Show Vlan on switch
  FILTER_ID=...       - Filter syntax VLAN:PORT_SPEED
     PORT_STATUS      - Change port status
     PORT_SPEED:[speed] - Change port speed
     [main_vlan]:[guest_vlan]:[speed]
     [main_vlan]:[speed]
  PORT=...            - Switch port
  TP_SPEED=1          - get speed From TP
  IN_ONLY=1           - Input shape speed only
  OUT_ONLY=1          - Output shape speed only
  MAIN_VLAN=...       - main Vlan
  GUEST_VLAN=...      - guest Vlan
  RESERV_PORTS=...,.. - reserv ports (Default: 25,26,27,28)
  RESET=1             - reset ports
  DEBUG=1..6          - Debug mode
  DHCP_NAS_INFO       - get Nas info from dhcphosts
  IP=...              - Client IP
  help                - This help
  
[END]
  exit;
}

my $VLAN        = $ARGV->{MAIN_VLAN}  || 3256;
my $GUEST_VLAN  = $ARGV->{GUEST_VLAN} || 3257;

my $sql         = Abills::SQL->connect($conf{dbtype}, $conf{dbhost}, $conf{dbname}, $conf{dbuser}, $conf{dbpasswd}, { CHARSET => ($conf{dbcharset}) ? $conf{dbcharset} : undef });
my $db          = $sql->{db};
my $admin       = Admins->new($db, \%conf);
$admin->info($conf{SYSTEM_ADMIN_ID}, { IP => '127.0.0.1' });
my $Nas         = Nas->new($db, \%conf);
my $Dv_sessions = Dv_Sessions->new($db, $admin, \%conf);
my $Dv          = Dv->new($db, $admin, \%conf);
my $Dhcphosts   = Dhcphosts->new($db, $admin, \%conf);
my $Snmputils   = Snmputils->new($db, $admin, \%conf);
my $Billing     = Billing->new($db, \%conf);

my $NAS_ID          = $ENV{NAS_ID}          || 1;
my $NAS_MNG_IP_PORT = $ENV{NAS_MNG_IP_PORT} || $ARGV->{NAS_MNG_IP_PORT} || '';
my $NAS_MNG_PASSWD  = $ENV{NAS_MNG_PASSWD}  || $ARGV->{NAS_MNG_PASSWD} || '';
my $NAS_TYPE        = $ENV{NAS_TYPE}        || '';
my $NAS_MNG_USER    = $ENV{NAS_MNG_USER}    || $ARGV->{NAS_MNG_USER} || '';
my @RESERV_PORTS    = (25, 26, 27, 28);

my $test_snmp = `echo "NAS_ID: $NAS_ID  IP: $NAS_MNG_IP_PORT PASSWD: $NAS_MNG_PASSWD TYPE: $NAS_TYPE USER: $NAS_MNG_USER " >> /tmp/test_snmp `;

if ($ARGV->{DEBUG}) {
  $debug = $ARGV->{DEBUG};
  if ($debug > 6) {
    $Dv_sessions->{debug}=1;
    $Dhcphosts->{debug}=1;
  }
}


if ($ARGV->{RESERV_PORTS}) {
  @RESERV_PORTS = split(/,/, $ARGV->{RESERV_PORTS});
}

if ($ARGV->{NAS_ID}) {
  $NAS_ID = $ARGV->{NAS_ID};
}

my $ACTION       = $ARGV[0];
my $LOGIN        = $ARGV[1];
my $FILTER_ID    = $ARGV[2];
my $PORT         = $ARGV[3];
my $get_tp_speed = 0;

$FILTER_ID =~ s/Session-Timeout=\d+,//g;

#Get NAS info from dhcphosts
if ($ARGV->{DHCP_NAS_INFO}) {
  $Dhcphosts->host_info(0, { IP => $ARGV->{IP} });
  $PORT = $Dhcphosts->{PORTS};
  if ($Dhcphosts->{TOTAL} < 0) {
    print "IP '$ARGV->{IP}' not registred in Dhcphosts\n";
    exit;
  }
  $NAS_ID = $Dhcphosts->{NAS_ID};
}

if ($NAS_ID) {
  $Nas->info({ NAS_ID => $NAS_ID });
  $NAS_MNG_IP_PORT = $Nas->{NAS_MNG_IP_PORT};
  $NAS_MNG_PASSWD  = $Nas->{NAS_MNG_PASSWORD};
  $NAS_TYPE        = $Nas->{NAS_TYPE};
  $NAS_MNG_USER    = $Nas->{NAS_MNG_USER} || $Nas->{NAS_MNG_PASSWORD};
}

if ($ARGV->{TP_SPEED}) {
  $get_tp_speed = 1;
  $FILTER_ID = "PORT_SPEED:0" if ($FILTER_ID =~ /PORT_SPEED/);
}

# 1 auto
# 2 half 10
# 3 full 10
# 4 half 100
# 5 full 100
# 6 half 1000
# 7 full 1000
my %speeds = (
  auto => 1,
  10   => 3,
  100  => 5,
  1000 => 7,
);

my $SPEED = 3;

#For Edgecore
# For Dlink              1.3.6.1.2.1.17.7.1.4.2.1.5
#                         1.3.6.1.2.1.17.7.1.4.3.1.4
my $untaged_ports_mib = '1.3.6.1.2.1.17.7.1.4.2.1.5';
my $taged_ports_mib   = '1.3.6.1.2.1.17.7.1.4.3.1.2';
my $all_ports_mib     = '1.3.6.1.2.1.17.7.1.4.2.1.4';
my $ports_mib         = $untaged_ports_mib;
my $type              = '';
my $sw_info;

if ($NAS_TYPE eq 'ipcad') {
  exit;
}

my $SNMP_COMMUNITY = "$NAS_MNG_USER\@$NAS_MNG_IP_PORT";
if ($ARGV->{RESET}) {
  dlink_vlan_add();
}
elsif ($ARGV->{SHOW_VLANS}) {
  show_vlans();
}

#Set port speed
elsif ($FILTER_ID =~ /PORT_SPEED:(\S+)/) {
  $SPEED = $1;
  if ($type = get_version()) {
    set_speed();
  }
}

#Port Enable/Disable
elsif ($FILTER_ID =~ /PORT_STATUS/) {
  if ($type = get_version()) {
    set_port_status();
  }
}

#main_vlan:guest_vlan:speed
elsif ($FILTER_ID =~ /(\S+):(\S+):(\S+)/) {
  $VLAN       = $1;
  $GUEST_VLAN = $2;
  $SPEED      = $3;

  if ($type = get_version()) {
    set_speed();
    if ($GUEST_VLAN || $VLAN) { set_vlan(); }
  }
}

#Vlan:SPEED
elsif ($FILTER_ID =~ /(\S+):(\S+)/) {
  $VLAN = $1;    #$VLAN;

  $SPEED = $speeds{ lc($2) };
  dlink_vlan_add();
  dlink_port_speed();
}

#***********************************************************
# enable/disable port
#
#***********************************************************
sub set_port_status {
  my ($attr) = @_;

  if (!$PORT) {
    $Dv->info(0, { LOGIN => $LOGIN });
    $PORT = $Dv->{PORT};

    if (!$PORT) {
      print "Select port\n";
      exit;
    }
  }

  my $STATUS = 1;
  if ($ACTION eq 'ONLINE_ENABLE') {
    $STATUS = 1;
  }
  elsif ($ACTION eq 'HANGUP' || $ACTION eq 'ONLINE_DISABLE') {
    $STATUS = 2;
  }

  #Edge-core mibs
  my $oid = '.1.3.6.1.2.1.2.2.1.7.';

  if ($debug > 0) {
    print "STATUS:\n $oid" . $PORT . " -> $STATUS\n";
  }

  snmpset($SNMP_COMMUNITY, "$oid" . $PORT, "integer", $STATUS);
}

#***********************************************************
#
#***********************************************************
sub show_vlans {
  my ($attr) = @_;

  $Nas->info({ NAS_ID => $ARGV->{SHOW_VLANS} });
  my $RESULT;
  if ($debug < 5) {
    $SNMP_COMMUNITY = "$Nas->{NAS_MNG_USER}\@$Nas->{NAS_MNG_IP_PORT}";
    if ($debug > 1) {
      print "NAS_ID: $Nas->{NAS_ID} SNMP: $Nas->{NAS_MNG_USER}\@$Nas->{NAS_MNG_IP_PORT}\n";
      if ($debug > 2) {
        print "MIB: $ports_mib\n";
      }
    }

    my $version  = snmpget($SNMP_COMMUNITY, ".1.3.6.1.2.1.1.1.0");
    my $firmvare = snmpget($SNMP_COMMUNITY, ".1.3.6.1.2.1.16.19.2.0");
    print "Version: $version Firmvare: $firmvare\n";

    my %mibs = (
      ALL_PORTS     => $all_ports_mib,
      UNTAGED_PORTS => $untaged_ports_mib
    );

    while (my ($name, $oid) = each %mibs) {
      my @result = snmpwalk($SNMP_COMMUNITY, "$oid");
      print "Name: $name\n";
      foreach my $line (@result) {
        ($vlan, $ports_bin) = split(/:/, $line, 2);
        my $p = unpack("B64", $ports_bin);
        my $ports = '';
        for ($i = 0 ; $i < length($p) ; $i++) {
          my $port_val = substr($p, $i, 1);
          if ($port_val == 1) {
            $ports .= ($i + 1) . ", ";
          }
        }

        print "$vlan: \t";
        print "$ports\n";
        if ($debug > 1) { print "$p\n"; }
      }
    }
  }

}

#***********************************************************
#
#***********************************************************
sub get_active_ports {
  my ($attr) = @_;

  my %LIST_PARAMS = ();
  $LIST_PARAMS{NAS_ID} = $NAS_ID if ($NAS_ID);

  $Dv_sessions->online({ FIELDS_NAMES => [ NAS_PORT_ID, NAS_IP, CLIENT_IP, UID ] });

  my $online_list = $Dv_sessions->{nas_sorted};
  my %ports       = ();

  # Billing ports, get active
  foreach my $online (@{ $online_list->{$NAS_ID} }) {
    $ports{ $online->{nas_port_id} } = 1 if ($online->{nas_port_id} && $online->{nas_port_id} > 0);
  }

  #Get all
  my $dhcphosts_list = $Dhcphosts->hosts_list(
    {
      NAS_ID                 => $NAS_ID,
      PORTS                  => '*',
      DHCPHOSTS_DEPOSITCHECK => 1,
      PAGE_ROWS              => 10000,
      COLS_NAME              => 1
    }
  );

  foreach my $host (@{$dhcphosts_list}) {
    my $port = $host->{ports};
    if ($LOGIN eq $line->{login}) {
      $ports{$port} = 0;
      $PORT = $port;
    }
    #elsif ($line->[18] > 0 && 
    elsif($host->{disable} == 0) {
      $ports{$port} = 1;
    }
    else {
      $ports{$port} = 0;
    }
    print "$host->{login} Port: $port STATUS: $ports{$port}\n" if ($debug > 1);
  }

  if ($ACTION eq 'ONLINE_ENABLE') {
    $ports{$PORT} = $UID || 1;
  }
  elsif ($ACTION eq 'HANGUP' || $ACTION eq 'ONLINE_DISABLE') {
    $ports{$PORT} = 0;
  }

  return \%ports;
}

#***********************************************************
#
#***********************************************************
sub dlink_vlan_add {

  my $ports = get_active_ports();

  my $RESULT;
  if ($debug < 5) {
    $RESULT = snmputils_dlink_pb_version({ SNMP_COMMUNITY => "$SNMP_COMMUNITY" });
  }
  print "version: " . $RESULT->{version} . "/" . $RESULT->{PORTS} . "\n" if ($debug > 0);

  my $all_ports_str = '';

  if ($ARGV->{RESET}) {
    $Nas->info({ NAS_ID => $ARGV->{SHOW_VLANS} || $ARGV->{NAS_ID} });
    my $RESULT;
    if ($debug < 5) {
      $SNMP_COMMUNITY = "$Nas->{NAS_MNG_USER}\@$Nas->{NAS_MNG_IP_PORT}";

      if ($debug > 1) {
        print "NAS_ID: $Nas->{NAS_ID} SNMP: $Nas->{NAS_MNG_USER}\@$Nas->{NAS_MNG_IP_PORT}\n";
        if ($debug > 2) {
          print "MIB: $ports_mib\n";
        }
      }
    }

    $all_ports_str = "000000000000000000000000101000000000000000000000";
    my $bin_ports = pack("B64", join("", split(//, $all_ports_str)));
    snmpset($SNMP_COMMUNITY, "$taged_ports_mib.$GUEST_VLAN", "octetstring", pack("B64", join("", split(//, "000000000000000000000000000000000000000000000000"))));
    snmpset($SNMP_COMMUNITY, "$taged_ports_mib.$VLAN", "octetstring", $bin_ports);
    print "Reseted";
    return 0;
  }

  my %PORT_HASH = ();

  $PORT_HASH{MAIN}{all_ports_str}     = unpack("B64", $RESULT->{ALL_PORTS}{$VLAN});
  $PORT_HASH{MAIN}{untaged_ports_str} = unpack("B64", $RESULT->{UNTAGED_PORTS}{$VLAN});
  $RESULT->{TAGED_PORTS}{$VLAN}       = $RESULT->{ALL_PORTS}{$VLAN} ^ $RESULT->{UNTAGED_PORTS}{$VLAN};
  $PORT_HASH{MAIN}{taged_ports}       = unpack("B64", $RESULT->{ALL_PORTS}{$VLAN} ^ $RESULT->{UNTAGED_PORTS}{$VLAN});

  $PORT_HASH{GUEST}{all_ports_str}     = unpack("B64", $RESULT->{ALL_PORTS}{$GUEST_VLAN});
  $PORT_HASH{GUEST}{untaged_ports_str} = unpack("B64", $RESULT->{UNTAGED_PORTS}{$GUEST_VLAN});
  $RESULT->{TAGED_PORTS}{$GUEST_VLAN}  = $RESULT->{ALL_PORTS}{$GUEST_VLAN} ^ $RESULT->{UNTAGED_PORTS}{$GUEST_VLAN};
  $PORT_HASH{GUEST}{taged_ports}       = unpack("B64", $RESULT->{TAGED_PORTS}{$GUEST_VLAN});

  if ($debug > 2) {
    print "
Current MAIN: $VLAN
> A: $PORT_HASH{MAIN}{all_ports_str}
> U: $PORT_HASH{MAIN}{untaged_ports_str} 
> T: $PORT_HASH{MAIN}{taged_ports}
GUEST: $GUEST_VLAN
> A: $PORT_HASH{GUEST}{all_ports_str}
> U: $PORT_HASH{GUEST}{untaged_ports_str}
> T: $PORT_HASH{GUEST}{taged_ports}
";
  }

  #Switch ports
  my @port_array  = split(//, $PORT_HASH{MAIN}{untaged_ports_str});
  my @guest_array = split(//, $PORT_HASH{GUEST}{untaged_ports_str});

  # reserved hash
  my %reserved_ports = ();
  foreach my $id (@RESERV_PORTS) {
    $reserved_ports{$id} = 1;
  }

  for ($i = 0 ; $i < length($PORT_HASH{MAIN}{all_ports_str}) ; $i++) {
    if ($reserved_ports{ $i + 1 }) {
      next;
    }

    if (defined($ports->{ $i + 1 })) {

      #Active
      if ($ports->{ $i + 1 } > 0) {
        $port_array[$i]  = 1;
        $guest_array[$i] = 0;
        print "Active: " . ($i + 1) . "\n" if ($debug > 1);
      }

      #Blocked
      else {
        $port_array[$i]  = 0;
        $guest_array[$i] = 1;
        print "Guest: " . ($i + 1) . "\n" if ($debug > 1);
      }
    }
    else {
      $port_array[$i]  = 0;
      $guest_array[$i] = 0;
    }
  }

  if ($debug > 0) {
    my $p = join(',', @port_array);
    my $a = `echo " >> $PORT $ports->{$PORT} '$ACTION'//$p" >> /tmp/vlan_test `;
  }

  my $bin_ports       = pack("B64", join("", @port_array));
  my $bin_ports_guest = pack("B64", join("", @guest_array));

  if ($debug > 2) {
    print "
New MAIN: $VLAN
> A: $PORT_HASH{MAIN}{all_ports_str}
> U: " . unpack("B64", $bin_ports) . "
> T: $PORT_HASH{MAIN}{taged_ports}
GUEST: $GUEST_VLAN
> A: $PORT_HASH{GUEST}{all_ports_str}
> U: " . unpack("B64", $bin_ports_guest) . "
> T: $PORT_HASH{GUEST}{taged_ports}
";
  }

  if ($debug < 5) {

    #Reset ports
    # Taged
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$GUEST_VLAN", "octetstring", pack("B64", "000000000000000000000000000000000000000000000000"));
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN",       "octetstring", pack("B64", "000000000000000000000000000000000000000000000000"));

    #Untaged
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN", "octetstring", pack("B64", "000000000000000000000000000000000000000000000000"));
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$VLAN",       "octetstring", pack("B64", "000000000000000000000000000000000000000000000000"));

    if ($SNMP_Session::errmsg) {
      print "Reset ports error
  	$SNMP_Session::suppress_warnings / $SNMP_Session::errmsg";
      exit;
    }

    #Main Section
    #Add taged port
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN", "octetstring", $RESULT->{TAGED_PORTS}{$VLAN});

    #Add untaged ports
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$VLAN", "string", $bin_ports);

    if ($SNMP_Session::errmsg) {
      print "Main VLAN Section \n $SNMP_Session::suppress_warnings\n" . "
  	$SNMP_COMMUNITY 1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN -> " . unpack("B64", $RESULT->{TAGED_PORTS}{$VLAN}) . "/" . unpack("H*", $RESULT->{TAGED_PORTS}{$VLAN}) . "\n" . "$SNMP_COMMUNITY 1.3.6.1.2.1.17.7.1.4.3.1.4.$VLAN -> " . unpack("B*", $bin_ports) . "/" . unpack("H*", $bin_ports) . "\n";
      exit;
    }

    #eval {
    #Guest Section
    #Add taged port
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$GUEST_VLAN", "octetstring", $RESULT->{TAGED_PORTS}{$GUEST_VLAN});

    #Add untaged ports
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN", "octetstring", $bin_ports_guest);

    #};

    if ($SNMP_Session::errmsg) {
      print "Guest VLAN Section\n	$SNMP_Session::suppress_warnings \n" . "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN -> " . unpack("B64", $bin_ports_guest) . "\n";
      exit;
    }

    #Change port state
    snmpset($SNMP_COMMUNITY, "1.3.6.1.4.1.171.11.63.6.2.2.2.1.3." . $PORT . ".100", "integer", 2);
    snmpset($SNMP_COMMUNITY, "1.3.6.1.4.1.171.11.63.6.2.2.2.1.3." . $PORT . ".100", "integer", 3);

    if ($SNMP_Session::errmsg) {
      print "Port change state
  	$SNMP_Session::suppress_warnings / $SNMP_Session::errmsg";
    }

    if ($debug > 2) {
      print "$SNMP_COMMUNITY -> $ports_mib.$VLAN s $bin_ports\n";
    }
  }
}

#**********************************************************
# http://www.mounblan.com/faq.php?prodid=0&deviceid=0&id=100
#**********************************************************
sub edgecore_vlan_get {
  my ($attr) = @_;
  my %RESULT = ();

  my $ports = '';

  #TAGED
  $RESULT{TAGED_PORTS}{$VLAN} = snmpget($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN");
  $RESULT{TAGED_PORTS}{$GUEST_VLAN} = snmpget($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$GUEST_VLAN", $ports);

  #UNtaged
  $RESULT->{UNTAGED_PORTS}{$VLAN}       = snmpget($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.30.$VLAN",    $ports);
  $RESULT->{UNTAGED_PORTS}{$GUEST_VLAN} = snmpget($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN", $ports);

  return \%RESULT;
}

#**********************************************************
# http://www.mounblan.com/faq.php?prodid=0&deviceid=0&id=100
#**********************************************************
sub edgecore_vlan {
  my ($attr) = @_;

  my $vlan      = $attr->{VLAN};
  my $ports_bin = $attr->{PORTS_BIN};

  if ($debug > 0) {
    print "1.3.6.1.2.1.17.7.1.4.3.1.4." . $VLAN . " octetstring $attr->{PORTS_BIN}\n";
  }

  my $RESULT;
  if ($debug < 5) {
    $RESULT = edgecore_vlan_get({ SNMP_COMMUNITY => "$SNMP_COMMUNITY" });
  }
  print "version: " . $RESULT->{version} . "/" . $RESULT->{PORTS} . "\n" if ($debug > 0);

  my $all_ports_str = '';

  my %PORT_HASH = ();

  $PORT_HASH{MAIN}{untaged_ports_str} = unpack("B64", $RESULT->{UNTAGED_PORTS}{$VLAN});
  $PORT_HASH{MAIN}{taged_ports}       = unpack("B64", $RESULT->{ALL_PORTS}{$VLAN} ^ $RESULT->{UNTAGED_PORTS}{$VLAN});
  $PORT_HASH{MAIN}{all_ports_str}     = $PORT_HASH{MAIN}{taged_ports};

  $PORT_HASH{GUEST}{untaged_ports_str} = unpack("B64", $RESULT->{UNTAGED_PORTS}{$GUEST_VLAN});
  $PORT_HASH{GUEST}{taged_ports}       = unpack("B64", $RESULT->{TAGED_PORTS}{$GUEST_VLAN});
  $PORT_HASH{GUEST}{all_ports_str}     = $PORT_HASH{GUEST}{taged_ports};

  if ($debug > 2) {
    print "
MAIN:
> A: $PORT_HASH{MAIN}{all_ports_str}
> U: $PORT_HASH{MAIN}{untaged_ports_str} 
> T: $PORT_HASH{MAIN}{taged_ports}
GUEST:
> A: $PORT_HASH{GUEST}{all_ports_str}
> U: $PORT_HASH{GUEST}{untaged_ports_str}
> T: $PORT_HASH{GUEST}{taged_ports}
";
  }

  #Switch ports
  my @port_array  = split(//, $PORT_HASH{MAIN}{untaged_ports_str});
  my @guest_array = split(//, $PORT_HASH{GUEST}{untaged_ports_str});

  # reserved hash
  my %reserved_ports = ();
  foreach my $id (@RESERV_PORTS) {
    $reserved_ports{$id} = 1;
  }

  for ($i = 0 ; $i < length($PORT_HASH{MAIN}{all_ports_str}) ; $i++) {
    if ($reserved_ports{ $i + 1 }) {
      next;
    }

    if (defined($ports->{ $i + 1 })) {

      #Active
      if ($ports->{ $i + 1 } > 0) {
        $port_array[$i]  = 1;
        $guest_array[$i] = 0;
        print "Active: " . ($i + 1) . "\n" if ($debug > 1);
      }

      #Blocked
      else {
        $port_array[$i]  = 0;
        $guest_array[$i] = 1;
        print "Guest: " . ($i + 1) . "\n" if ($debug > 1);
      }
    }
    else {
      $port_array[$i]  = 0;
      $guest_array[$i] = 0;
    }
  }

  if ($debug > 0) {
    my $p = join(',', @port_array);
    my $a = `echo " >> $PORT $ports->{$PORT} '$ACTION'//$p" >> /tmp/vlan_test `;
  }

  my $bin_ports       = pack("B64", join("", @port_array));
  my $bin_ports_guest = pack("B64", join("", @guest_array));

  if ($debug > 2) {
    print "
MAIN:
> A: $PORT_HASH{MAIN}{all_ports_str}
> U: " . unpack("B64", $bin_ports) . "
> T: $PORT_HASH{MAIN}{taged_ports}
GUEST:
> A: $PORT_HASH{GUEST}{all_ports_str}
> U: " . unpack("B64", $bin_ports_guest) . "
> T: $PORT_HASH{GUEST}{taged_ports}
";
  }

  if ($debug < 5) {

    #Reset ports
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$GUEST_VLAN", "octetstring", pack("B32", "00000000000000000000000000000000"));
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN",       "octetstring", pack("B32", "00000000000000000000000000000000"));
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN", "octetstring", pack("B32", "00000000000000000000000000000000"));
    snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$VLAN",       "octetstring", pack("B32", "00000000000000000000000000000000"));

    eval {
      #Guest Section
      #Add taged port
      snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$GUEST_VLAN", "octetstring", $RESULT->{TAGED_PORTS}{$GUEST_VLAN});

      #Add untaged ports
      snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$GUEST_VLAN", "octetstring", $bin_ports_guest);
    };

    eval {
      #Main Section
      #Add taged port
      snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.2.$VLAN", "octetstring", $RESULT->{TAGED_PORTS}{$VLAN});

      #Add untaged ports
      snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4.$VLAN", "octetstring", $bin_ports);
    };

    #Change port state
    #snmpset($SNMP_COMMUNITY, "1.3.6.1.4.1.171.11.63.6.2.2.2.1.3.". $PORT .".100", "integer", 2);
    #snmpset($SNMP_COMMUNITY, "1.3.6.1.4.1.171.11.63.6.2.2.2.1.3.". $PORT .".100", "integer", 3);

    if ($debug > 2) {
      print "$SNMP_COMMUNITY -> $ports_mib.$VLAN s $bin_ports\n";
    }
  }

  snmpset($SNMP_COMMUNITY, "1.3.6.1.2.1.17.7.1.4.3.1.4." . $VLAN, "octetstring", $attr->{PORTS_BIN});

  #snmpset -c private -v 2c 192.168.1.137 1.3.6.1.2.1.17.7.1.4.3.1.4.30 x "f0 00 00 00"
}

#**********************************************************
#
#**********************************************************
sub snmputils_dlink_pb_version {
  my ($attr) = @_;

  my $SNMP_COMMUNITY = $attr->{SNMP_COMMUNITY} || '';
  my %RESULT = ();

  $RESULT{version}        = '';
  $RESULT{SNMP_COMMUNITY} = $SNMP_COMMUNITY;
  $RESULT{oid_prefix}     = '.1.3.6.1.4.1.171.11.64.1.2.7.';

  if ($RESULT{version} = snmpget($SNMP_COMMUNITY, ".1.3.6.1.2.1.1.1.0")) {
    if ($VLAN !~ /^(\d+)$/) {
      print "Wrong vlan '$VLAN'\n";
      return \%RESULT;
    }

    if ($VLAN) {

      #Full Vlan info
      my %mibs = (
        ALL_PORTS     => $all_ports_mib,
        UNTAGED_PORTS => $untaged_ports_mib
      );
      while (my ($name, $oid) = each %mibs) {
        my @result = snmpwalk($SNMP_COMMUNITY, "$oid");

        foreach my $line (@result) {
          ($vlan, $ports_bin) = split(/:/, $line, 2);
          $vlan =~ /(\d+)$/;
          $vlan_id = $1;

          $RESULT{$name}{$vlan_id} = $ports_bin;
          my $p = unpack("B64", $ports_bin);
          print "$name: '$vlan_id' $p\n" if ($debug > 3);
        }
      }
    }
  }
  else {
    print "$SNMP_Session::suppress_warnings / $SNMP_Session::errmsg";
    return 0;
  }

  return \%RESULT;
}

#**********************************************************
#
#**********************************************************
sub dlink_port_speed {
  my ($attr) = @_;

  my $sw_info = $attr->{SWITCH_INFO} || snmputils_dlink_pb_version({ SNMP_COMMUNITY => $SNMP_COMMUNITY });

  if (!$PORT) {
    print "Select port\n";
    exit;
  }

  if ($FILTER_ID !~ /PORT_SPEED:(\S+)/) {
    if ($sw_info->{version} =~ /DES-3028/) {
      $RESULT{oid_prefix} = '1.3.6.1.4.1.171.11.63.6.2.2.2.1.4';
    }
    else {
      $RESULT{oid_prefix} = '1.3.6.1.4.1.171.11.63.8.2.2.2.1.4';
    }

    print "$RESULT{oid_prefix}." . $PORT . ".100 -> integer $SPEED\n" if ($debug > 2);
    snmpset($SNMP_COMMUNITY, "$RESULT{oid_prefix}." . $PORT . ".100", "integer", "$SPEED");
    return 0;
  }
  elsif ($sw_info->{version} =~ /DES-3028/) {
    # port speed 10 / 100 /1000
    #$RESULT{oid_prefix} = '1.3.6.1.4.1.171.11.63.6.2.2.2.1.4';
    #dlink 3028 (скорость кратна 1Кбит/с)
    #snmpset -c snmppass -v2c 10.133.251.243 1.3.6.1.4.1.171.11.63.6.2.3.1.1.2.24 i 1024
    #2 - вход
    #3 - исход
    #24 - порт
    #1024 - скорость 1024 Кбит/с
    #1024000 - скорость no_limit
    #
    $in_oid  = '1.3.6.1.4.1.171.11.63.6.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.63.6.2.3.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-3526/) {
    #dlink 3526 (скорость кратна 1Мбит/с)
    #snmpset -c snmppass -v2c 10.133.200.102 1.3.6.1.4.1.171.11.64.1.2.6.1.1.2.1 i 2
    #snmpset -c snmppass -v2c 10.133.200.102 1.3.6.1.4.1.171.11.64.1.2.6.1.1.3.1 i 2
    #2 - вход
    #3 - исход
    #1 - port
    #2 - скорость 2 Мбит/с
    #0 - скорость no_limit

    if ($SPEED > 0 && $SPEED <= 1024) {
      $SPEED = 1;
    }
    elsif ($SPEED > 1024) {
      $SPEED = int($SPEED / 1024);
    }
    else {
      $SPEED = 0;
    }

    $in_oid  = '1.3.6.1.4.1.171.11.64.1.2.6.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.64.1.2.6.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-1228/) {
    $in_oid  = '1.3.6.1.4.1.171.11.116.2.2.3.1.1.2';
    $out_oid = '1.3.6.1.4.1.171.11.116.2.2.3.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-3528/) {
    #dlink 3528 (скорость кратна 1Кбит/с)
    #snmpset -c snmppass -v2c 10.133.247.2 1.3.6.1.4.1.171.12.61.3.1.1.2.24 i 1024
    #2 - вход
    #3 - исход
    #24 - port
    #1024 - скорость в Кбит/с
    #0 - скорость no_limit

    $in_oid  = '1.3.6.1.4.1.171.12.61.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.12.61.3.1.1.3.';
  }

  # DES-3550 and other
  elsif ($sw_info->{version} =~ /DES-3550/) {
    if ($SPEED > 0 && $SPEED < 1024) {
      $SPEED = $1;
    }
    else {
      $SPEED = int($SPEED / 1024);
    }

    $in_oid  = '1.3.6.1.4.1.171.11.64.2.2.6.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.64.2.2.6.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-3052/) {
    $in_oid  = '1.3.6.1.4.1.171.11.63.8.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.63.8.2.3.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-3200-18/) {
    $in_oid  = '1.3.6.1.4.1.171.11.113.1.2.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.113.1.2.2.3.1.1.3.';
  }
  # Old
  elsif ($sw_info->{version} =~ /Dlink DES-3200-26/) {
    $in_oid  = '1.3.6.1.4.1.171.11.113.1.5.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.113.1.5.2.3.1.1.3.';
  }
  # DES-3200-26 ревизия C1
  elsif ($sw_info->{version} =~ /DES-3200-26/) {
    $in_oid  = '.1.3.6.1.4.1.171.12.61.3.1.1.2.';
    $out_oid = '.1.3.6.1.4.1.171.12.61.3.1.1.3.';
  }
  elsif ($sw_info->{version} =~ /DES-3200-28/) {
    $in_oid  = '1.3.6.1.4.1.171.11.113.1.3.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.113.1.3.2.3.1.1.3.';
  }
  else {

    #$RESULT{oid_prefix} = '1.3.6.1.4.1.171.11.63.8.2.2.2.1.4';
    $in_oid  = '1.3.6.1.4.1.171.11.63.6.2.3.1.1.2.';
    $out_oid = '1.3.6.1.4.1.171.11.63.6.2.3.1.1.3.';
  }

  if ($debug > 0) {
    print "Speed:\n $in_oid" . $PORT . " -> $SPEED \n $out_oid" . $PORT . " -> $SPEED\n";
  }

  if (!defined($ARGV->{IN_ONLY})) {
    snmpset($SNMP_COMMUNITY, "$out_oid" . $PORT, "integer", $SPEED);
  }
  if (!defined($ARGV->{OUT_ONLY})) {
    snmpset($SNMP_COMMUNITY, "$in_oid" . $PORT, "integer", $SPEED);
  }
}

#**********************************************************
#
#**********************************************************
sub edgecore_port_speed {
  my ($attr) = @_;

  if (!$PORT) {
    print "Select port\n";
    exit;
  }

  my $in_oid  = '1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.10.';
  my $out_oid = '1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.10.';

  # Oid:type:value
  my @snmp_oids = ();

  if ($sw_info->{version} =~ /3528/) {

    #edge-core 3528 (скорость  от 64kbps до 100000 kbps кратна 1Кбит/с)
    #snmpset -c snmppass -v2c 10.133.200.101 1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.10.1 i 1024
    #10 - вход
    #11 - исход
    #1   - port
    #1024 - скорость 1024 Кбит/с
    $in_oid  = '1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.10.';
    $out_oid = '1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.11.';

    # ON/Off
    #snmpset -c snmppass -v2c 10.133.200.101 1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.6.1 i 1
    # 6  - вход
    # 7 -  исход
    # 1 -  port
    # 1 - включить
    # 2 - выключить

    push @snmp_oids, "$in_oid" . $PORT . ":integer:$SPEED"  if (!defined($ARGV->{OUT_ONLY}));
    push @snmp_oids, "$out_oid" . $PORT . ":integer:$SPEED" if (!defined($ARGV->{IN_ONLY}));

    push @snmp_oids, "1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.6." . $PORT . ":integer:1" if (!defined($ARGV->{OUT_ONLY}));
    push @snmp_oids, "1.3.6.1.4.1.259.6.10.94.1.16.1.2.1.7." . $PORT . ":integer:1" if (!defined($ARGV->{IN_ONLY}));
  }
  elsif ($sw_info->{version} =~ /3526/) {
    my $byte_speed = $SPEED;
    my $scale      = 3;
    my $port_speed = 0;

    if ($byte_speed < 80) {
      $scale      = 4;
      $port_speed = int($byte_speed / 8);
    }
    elsif ($byte_speed <= 1024) {
      $scale      = 3;
      $port_speed = int($byte_speed / 80);
    }
    elsif ($byte_speed <= 10240) {
      $scale      = 2;
      $port_speed = int($byte_speed / 800);
    }
    elsif ($byte_speed <= 102400) {
      $scale      = 1;
      $port_speed = int($byte_speed / 8000);
    }

    # ON off
    $in_oid  = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.6.';
    $out_oid = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.7.';

    # Scale
    push @snmp_oids, "$in_oid" . $PORT . ":integer:1"  if (!defined($ARGV->{OUT_ONLY}));
    push @snmp_oids, "$out_oid" . $PORT . ":integer:2" if (!defined($ARGV->{IN_ONLY}));

    $in_oid  = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.9.';
    $out_oid = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.11.';
    push @snmp_oids, "$in_oid" . $PORT . ":integer:$scale"  if (!defined($ARGV->{OUT_ONLY}));
    push @snmp_oids, "$out_oid" . $PORT . ":integer:$scale" if (!defined($ARGV->{IN_ONLY}));

    # mnozhnyk
    $in_oid  = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.8.';
    $out_oid = '1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.10.';
    push @snmp_oids, "$in_oid" . $PORT . ":integer:$port_speed"  if (!defined($ARGV->{OUT_ONLY}));
    push @snmp_oids, "$out_oid" . $PORT . ":integer:$port_speed" if (!defined($ARGV->{IN_ONLY}));
  }

  foreach my $line (@snmp_oids) {
    my ($oid, $type, $value) = split(/:/, $line, 3);
    if ($debug > 3) {
      print "$oid : $type -> $value\n";
    }
    snmpset($SNMP_COMMUNITY, "$oid", "$type", $value);
    if ($SNMP_Session::errmsg) {
      print "Error: Get set: oid : $type -> $value \n 	$SNMP_Session::suppress_warnings\n";
      exit;
    }

  }

  #edge-core 3526 (скорость хрен знает чему кратна)
  #snmpset -c snmppass -v2c 10.133.254.221 1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.7.24 i 1
  #6 - вход
  #7 - исход
  #24 - port
  #1 - включить
  #2 - выключить
  #
  #snmpset -c snmppass -v2c 10.133.254.221 1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.9.24 i 2
  #9 - вход
  #11 - исход
  #24 - port
  #2 - scale ( {1, 2, 3, 4}; {8M, 800K, 80K, 8K} соответственно ), то есть скорость в битах задать нельзя, только в байтах - придется как-то вычислять
  #
  #
  #
  #snmpset -c snmppass -v2c 10.133.254.221 1.3.6.1.4.1.259.8.1.5.1.16.1.2.1.8.24 i 3
  #8 - вход
  #10 - исход
  #24 - port
  #3 - level (множитель, умножается на scale для вычисления скорости, 1-127)
}

#**********************************************************
#
#**********************************************************
sub set_speed {
  if ($get_tp_speed) {
    my $Dv = Dv->new($db, $admin, \%conf);
    my $user = $Dv->info(0, { LOGIN => $LOGIN });
    if ($Dv->{errno}) {
      print "Error: User not exist '$USER' IP: $HISADDR ([$Dv->{errno}] $Dv->{errstr})\n";
      exit 1;
    }
    elsif ($Dv->{TOTAL} < 1) {
      print "$USER - Not exist\n";
      exit 1;
    }

    #If set individual user speed
    if ($user->{SPEED} > 0) {
      $SPEED = int($user->{SPEED});
    }
    else {
      ($user->{TIME_INTERVALS}, 
      $user->{INTERVAL_TIME_TARIF}, 
      $user->{INTERVAL_TRAF_TARIF}) = $Billing->time_intervals($user->{TP_NUM});

      my ($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,
          GET_INTERVAL        => 1,

          #          debug               => ($debug > 0) ? 1 : undef
        }
      );

      #    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, \%conf, $admin);

        #Get intervals
        while (my ($k, $v) = each(%TT_IDS)) {
          print "$k, $v\n" if ($debug > 0);
          next if ($k ne 'FIRST_INTERVAL');
          $user->{TI_ID} = $v;
          my $list = $tariffs->tt_list({ TI_ID => $v, SHOW_NETS => 1 });
          foreach my $line (@$list) {
            $speeds{ $line->[0] }{IN}  = "$line->[4]";
            $speeds{ $line->[0] }{OUT} = "$line->[5]";
          }
        }
      }
      $SPEED = $speeds{0}{IN} if ($speeds{0}{IN});
    }
  }

  if ($PORT == 0) {
    #Get all
    my $dhcphosts_list = $Dhcphosts->hosts_list(
      {
        NAS_ID    => $NAS_ID,
        PORTS     => '*',
        PAGE_ROWS => 10000,
        COLS_NAME => 1
      }
    );

    foreach my $host (@{$dhcphosts_list}) {
      my $port = $host->{port};
      if ($LOGIN eq $host->{login}) {
        $ports{$port} = 0;
        $PORT = $port;
      }
      #elsif ($line->[18] > 0 && 
      elsif ($host->{disable} == 0) {
        $ports{$port} = 1;
      }
      else {
        $ports{$port} = 0;
      }
      print "$host->{login} Port: $port STATUS: $ports{$port}\n" if ($debug > 1);
    }
  }

  print "Speed : $SPEED Type: $type Version: $sw_info->{version}\n" if ($debug > 0);

  if ($type eq 'edge_core') {
    edgecore_port_speed();
  }
  elsif ($type eq 'dlink') {
    dlink_port_speed();
  }
}

#**********************************************************
#
#**********************************************************
sub set_vlan {

  if ($type eq 'edge_core') {
    edgecore_vlan();
  }
  elsif ($type eq 'dlink') {
    dlink_vlan_add();
  }

}

#**********************************************************
# Get nas info from billing and from switch
#**********************************************************
sub get_version {
  my ($attr) = @_;

  my %RESULT = ();
  my $type   = 'Unknown';
  
  if (length($SNMP_COMMUNITY) < 4) {
  	print "Error: NAS_ID: $NAS_ID Not specified SNMP community\n";
  	return 1;
  }
  
  if ($RESULT{version} = snmpget($SNMP_COMMUNITY, ".1.3.6.1.2.1.1.1.0")) {
    if ($SNMP_Session::errmsg) {
      print "Error: Get Version	$SNMP_Session::suppress_warnings\n";
      exit;
    }

    elsif ($RESULT{version} =~ /Edge\-Core|ES3526/) { $type = 'edge_core'; }
    elsif ($RESULT{version} =~ /DES/) { $type = 'dlink'; }

    $sw_info->{TYPE}    = $type;
    $sw_info->{version} = $RESULT{version};
  }

  $Nas->info({ NAS_ID => $NAS_ID });
  $NAS_MNG_IP_PORT = $Nas->{NAS_MNG_IP_PORT};
  $NAS_MNG_PASSWD  = $Nas->{NAS_MNG_PASSWD};
  $NAS_TYPE        = $Nas->{NAS_TYPE};
  $NAS_MNG_USER    = $Nas->{NAS_MNG_USER};

  return $type;
}

1

