#!/usr/bin/perl -w
# DHCP tools utility
# GRAB arp table and add user to DHCPhosts

use FindBin '$Bin';


my $debug   = 1;
my $version = 0.8;
my $ARP     = '/usr/sbin/arp';


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 Users;
Users->import();

require Admins;
Admins->import();

require Customers ;
Customers->import();

require Dv_Sessions ;
Dv_Sessions->import();

require Dhcphosts;
Dhcphosts->import();




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 $Users       = Users->new($db, $admin, \%conf); 
my $Dv_sessions = Dv_Sessions->new($db, $admin, \%conf);
my $Dhcphosts   = Dhcphosts->new($db, $admin, \%conf);


my $ARGV = parse_arguments(\@ARGV);

$debug = ($ARGV->{DEBUG}) ? $ARGV->{DEBUG} : $debug;

if ($#ARGV< 0) {
	help();
	exit;
}


if ( $ARGV->{FILE} ) {
  &file_grab($ARGV->{FILE});	
 }
elsif (defined($ARGV->{ASSIGN_NET}) ) {
  $ARGV->{ASSIGN_NET}=1;
  assign_net();
 }
elsif($ARGV->{ADDRESS_SHIFT}) {
	address_shift();
 }
else {
  if (! $ARGV->{NETWORK}) {
	 print "SELECT network\n";

	 help();
	 exit;
  }
  
  &arp_grab();
  &assign_net();
}


#**********************************************************
# file grab
#**********************************************************
sub file_grab() {
  my ($filename) = @_;
  
  #Get all logins
  my %user_logins = ();

  $Users->{debug}=1 if ($debug > 5);

  my $list = $Users->list({ PAGE_ROWS => 1000000 });
  foreach my $line (@$list) {
  	$user_logins{"$line->[0]"}=$line->[5 + $Users->{SEARCH_FIELDS_COUNT}];
   }
  
  
  
  my $content = '';
  open(FILE, "$filename") || die "Can't open file '$filename' $!\n\n";
    while(<FILE>) {
    	$content .= $_;
     }
  close(FILE);
  
  my @rows = split(/\n/, $content);
  my $i         =0;
  my $not_found =0; 
  foreach my $line (@rows) {
  	my ($login, $ip, $mac, $ports, $nas_id, $option_82, $vid, $hostname)=split(/\t/, $line) ;
   	print "Login: $login";
  	if ($user_logins{"$login"}) {
      my $uid = $user_logins{"$login"};
      my $network = get_net($ip); 
      print " ADD UID: $uid IP: $ip MAC: $mac NET: $network\n" if ($debug > 0);
      if ($debug < 5) {
        $Dhcphosts->host_add({ UID      => $uid, 
    	                         IP       => $ip,
    	                         MAC      => $mac,
    	                         HOSTNAME => $hostname || "$login",
                               PORTS    => $ports,
                               NAS_ID   => $nas_id,
                               OPTION_82=> $option_82,
                               VID      => $vid,
                               NETWORK  => $network
                               
    	                      });
       }
  	  $i++;
  	 }
    else {
    	print " Not found! \n";
    	$not_found++;
     }
  }
  

  print "Total: $i Not found: $not_found\n";
}


#**********************************************************
# Assign IP to spec network
#**********************************************************
sub get_net {
my ($ip) = @_;

my %network_hash = ();
my $list = $Dhcphosts->networks_list();


foreach my $line (@$list) {
	my $first_ip = ip2int($line->[2]);
	my $last_ip  = $first_ip + (4294967295  - ip2int($line->[3]) );

	$network_hash{$first_ip}="$last_ip:$line->[0]";
}
 
  $ip = ip2int($ip);

  reset %network_hash;
	foreach my $first (keys %network_hash) {
	  my $v = $network_hash{$first};
		my ($last, $id)=split(/:/, $v, 2);
		if ($ip >= $first && $ip <= $last) {
			 return $id;
			 last;
		 }
	 }

}

#**********************************************************
# Assign IP to spec network
#**********************************************************
sub assign_net {
# Net hash start -> end:id
my %network_hash = ();
my $list = $Dhcphosts->networks_list();


foreach my $line (@$list) {
	my $first_ip = ip2int($line->[2]);
	my $last_ip  = $first_ip + (4294967295  - ip2int($line->[3]) );
	$network_hash{$first_ip}="$last_ip:$line->[0]";
}

#Get users
$list = $Dhcphosts->hosts_list({ PAGE_ROWS => 1000000 });
print "Total: ". $Dhcphosts->{TOTAL} ."\n";
foreach my $line (@$list) {
	#print "$line->[2]";
	my $ip = $line->[2]; #ip2int($line->[2]);
  
  reset %network_hash;
	foreach my $first (keys %network_hash) {
	  my $v = $network_hash{$first};
		my ($last, $id)=split(/:/, $v, 2);
		if ($ip >= $first && $ip <= $last) {
			 #make change
			 print " NET ID: $id\n" if ($debug > 1);
			 if ($id != $line->[5]) {
			   #$Dhcphosts->host_change({
			 	 #   ID      => $line->[0],
			 	 #   NETWORK => $id 
			 	 #  });
         print "Added\n";
			  }
			 last;
		 }
	 }
	
	print "\n" if ($debug > 0); 
}

}

#**********************************************************
# Grab ip from arp table
#**********************************************************
sub arp_grab () {
  my $arp_ip_mac    = get_arp_info();
  my %online_ip_uid = ();
  my $network_id    = $ARGV->{NETWORK};

  #Get online Users-
  my $list = $Dv_sessions->online({ FIELDS_NAMES => [ USER_NAME, CID, UID ]});
  foreach my $line (@$list) {
	  if ($line->[1] =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
		  print "LOGIN: $line->[0] UID: $line->[2] CID: $line->[1]\n" if ($debug > 0);
		  $online_ip_uid{$line->[1]}=$line->[2];
	   }
  }
#Get DHCP records
#$Dhcphosts->{debug}=1;
$list = $Dhcphosts->hosts_list({ PAGE_ROWS => 1000000,
    	                           MAC       => join(',', sort values %$arp_ip_mac)
  	                           });

foreach $line (@$list) {
	print "Login: $line->[1] IP: $line->[2] MAC: $line->[6] UID: $line->[12]\n" if ($debug > 0);
	if ($online_ip_uid{$line->[2]}) {
		if ($online_ip_uid{$line->[2]} ne $line->[12]) {
			print "Alert: IP: $line->[2] DHCPHOST UID: $line->[11] ONLINE UID: $online_ip_uid{$line->[2]}\n"
		 }	
	  else {
	  	delete $online_ip_uid{$line->[2]};
	   }
	 }
}

#Add to dhcphosts
foreach my $ip (sort keys %online_ip_uid) {
  my $uid = $online_ip_uid{$ip};

  if ($arp_ip_mac->{$ip}) {
    print "ADD UID: $uid IP: $ip MAC: $arp_ip_mac->{$ip}\n" if ($debug > 0);
    $Dhcphosts->host_add({ UID      => $uid, 
    	                     IP       => $ip,
    	                     MAC      => $arp_ip_mac->{$ip},
    	                     HOSTNAME => $ip,
    	                     NETWORK  => $network_id    	                     
    	                   })
	 }
}

}


#**********************************************************
# get arp records
#**********************************************************
sub get_arp_info {
	my ($attr) = @_;
	my %arp_ip_mac = ();
	
	my $result = `$ARP -an `;
	
	my @res_arr = split(/\n/, $result);

	# ? (192.168.0.255) at ff:ff:ff:ff:ff:ff on lnc0 permanent [ethernet]
	foreach my $line (@res_arr) {
    my(undef, $ip, undef, $mac, undef, $interface, undef)=split(/ /, $line);
    
    if ($mac =~ /:/ && $mac ne 'ff:ff:ff:ff:ff:ff') {
      $ip =~ s/\(|\)//g;
      print "$ip : $mac\n" if ($debug > 0); 
      $arp_ip_mac{$ip}=$mac; 		
     }
	 } 
	
	return \%arp_ip_mac;
}


#**********************************************************
#
#**********************************************************
sub address_shift {
	my ($attr)=@_;
	
#Get net
my ($network_id, $src_net, $dst_net)=split(/:/, $ARGV->{ADDRESS_SHIFT});
my $number = 1;
my $prefix = 128;

my ($src_d1, $src_d2, $src_d3, $src_d4)=split(/\./, $src_net);
my ($dst_d1, $dst_d2, $dst_d3, $dst_d4)=split(/\./, $dst_net);
my $dst_net_int = ip2int("$dst_d1.$dst_d2.$dst_d3.0");
my $src_net_int = ip2int("$src_d1.$src_d2.$src_d3.0");


my $disparity  = $dst_net_int - $src_net_int;
print "Disparity: $disparity\n";

my $list = $Dhcphosts->hosts_list({ PAGE_ROWS => 1000000  });
foreach my $line ( @$list ) {
	print "$line->[0] LOGIN: $line->[1] IP: ". int2ip($line->[2]) ." $line->[3]\n" if ($debug>3);
	if ($line->[2] >= $src_net_int  && $line->[2] < $src_net_int + 256) {
	  print " ADD: $line->[0], $line->[1], ". int2ip($line->[2]) . 
     '->'. int2ip($line->[2] + $disparity) ." $line->[3]\n" if ($debug > 0);
	  if($debug < 4) {
	    $Dhcphosts->host_change({
			 	    ID      => $line->[0],
			 	    NETWORK => $network_id,
			 	    IP      => int2ip($line->[2] + $disparity)
			 	   });
	   
	    if($Dhcphosts->{errno}) {
	    	print "Error: ID: $line->[0] LOGIN: $line->[1] [$Dhcphosts->{errno}] $Dhcphosts->{errstr}";
	    	exit;
	     }
	   }
	 }
}

	
	
}

#**********************************************************
# get arp records
#**********************************************************
sub help {

print << "[END]";
ARP grabber. Grab all user IP/MAC information to Dhcphosts.
Version $version
  arp_grab.pl NETWORK=[Your dhcp network id]
    NETWORK    - [Your dhcp network id]. Autograb from arp table
    ASSIGN_NET - Asign Dhcprecords to Correct network
    FILE       - get users id from file (LOGIN[tab]IP)
    ADDRESS_SHIFT="NEW_NETWORK_ID:SRC_NET:DST_NET" shift addres to other network with mask /24
    DEBUG      - Debug mode
[END]

}
1
