fetch_new_arp.pl 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. #!/usr/bin/perl
  2. #
  3. # Copyright (C) Roman Dmitiriev, rnd@rajven.ru
  4. #
  5. use utf8;
  6. use open ":encoding(utf8)";
  7. use Encode;
  8. no warnings 'utf8';
  9. use English;
  10. use base;
  11. use FindBin '$Bin';
  12. use lib "/opt/Eye/scripts";
  13. use strict;
  14. use DBI;
  15. use Time::Local;
  16. use Net::Patricia;
  17. use Data::Dumper;
  18. use Date::Parse;
  19. use Socket;
  20. use eyelib::config;
  21. use eyelib::main;
  22. use eyelib::net_utils;
  23. use eyelib::snmp;
  24. use eyelib::database;
  25. use eyelib::common;
  26. use NetAddr::IP;
  27. use Fcntl qw(:flock);
  28. use Parallel::ForkManager;
  29. open(SELF,"<",$0) or die "Cannot open $0 - $!";
  30. flock(SELF, LOCK_EX|LOCK_NB) or exit 1;
  31. setpriority(0,0,19);
  32. if ($config_ref{config_mode}) { log_info("System in configuration mode! Skip discovery."); exit; }
  33. db_log_verbose($dbh,'Clearing empty records.');
  34. ##### clean empty user account and corresponded devices for dynamic users and hotspot ################
  35. log_info($dbh,'Clearing empty user account and corresponded devices for dynamic users and hotspot');
  36. my $u_sql = "SELECT * FROM User_list as U WHERE (U.ou_id=".$default_hotspot_ou_id." OR U.ou_id=".$default_user_ou_id.") AND (SELECT COUNT(*) FROM User_auth WHERE User_auth.deleted=0 AND User_auth.user_id = U.id)=0";
  37. my @u_ref = get_records_sql($dbh,$u_sql);
  38. foreach my $row (@u_ref) {
  39. db_log_info($dbh,"Remove empty dynamic user with id: $row->{id} login: $row->{login}");
  40. delete_user($dbh,$row->{id});
  41. }
  42. ##### clean empty user account and corresponded devices ################
  43. if ($config_ref{clean_empty_user}) {
  44. log_info($dbh,'Clearing empty user account and corresponded devices');
  45. my $u_sql = "SELECT * FROM User_list as U WHERE U.permanent=0 AND (SELECT COUNT(*) FROM User_auth WHERE User_auth.deleted=0 AND User_auth.user_id = U.id)=0 AND (SELECT COUNT(*) FROM auth_rules WHERE auth_rules.user_id = U.id)=0;";
  46. my @u_ref = get_records_sql($dbh,$u_sql);
  47. foreach my $row (@u_ref) {
  48. db_log_info($dbh,"Remove empty user with id: $row->{id} login: $row->{login}");
  49. delete_user($dbh,$row->{id});
  50. }
  51. }
  52. #clean temporary user auth records
  53. my $now = DateTime->now(time_zone=>'local');
  54. my $clear_time =$dbh->quote($now->strftime('%Y-%m-%d %H:%M:%S'));
  55. my $users_sql = "SELECT * FROM User_auth WHERE deleted=0 AND dynamic=1 AND `eof`<=".$clear_time;
  56. my @users_auth = get_records_sql($dbh,$users_sql);
  57. if (@users_auth and scalar @users_auth) {
  58. foreach my $row (@users_auth) {
  59. delete_user_auth($dbh,$row->{id});
  60. db_log_info($dbh,"Removed dynamic user auth record for auth_id: $row->{'id'} by eof time: $row->{'eof'}",$row->{'id'});
  61. my $u_count=get_count_records($dbh,'User_auth','deleted=0 and user_id='.$row->{user_id});
  62. if (!$u_count) { delete_user($dbh,$row->{'user_id'}); }
  63. }
  64. }
  65. my %mac_history;
  66. my ($sec,$min,$hour,$day,$month,$year,$zone) = localtime(time());
  67. $month += 1;
  68. $year += 1900;
  69. my $fork_count = $cpu_count*5;
  70. #disable fork for debug
  71. #if ($debug) { $fork_count = 0; }
  72. my $now_str=sprintf "%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec;
  73. my $now_day=sprintf "%04d-%02d-%02d",$year,$month,$day;
  74. db_log_verbose($dbh,'Arp discovery started.');
  75. if ($ARGV[0]) {
  76. db_log_verbose($dbh,'Active check started!');
  77. my $subnets=get_subnets_ref($dbh);
  78. my @fping_cmd=();
  79. foreach my $net (keys %$subnets) {
  80. next if (!$net);
  81. next if (!$subnets->{$net}{discovery});
  82. my $run_cmd="$fping -g $subnets->{$net}{subnet} -B1.0 -c 1 >/dev/null 2>&1";
  83. db_log_debug($dbh,"Checked network $subnets->{$net}{subnet}") if ($debug);
  84. push(@fping_cmd,$run_cmd);
  85. }
  86. $parallel_process_count = $cpu_count*2;
  87. run_in_parallel(@fping_cmd);
  88. }
  89. my @router_ref = get_records_sql($dbh,"SELECT * FROM devices WHERE deleted=0 AND (device_type=2 or device_type=0) AND discovery=1 AND snmp_version>0 ORDER by ip" );
  90. $dbh->disconnect;
  91. my @arp_array=();
  92. my $pm_arp = Parallel::ForkManager->new($fork_count);
  93. # data structure retrieval and handling
  94. $pm_arp -> run_on_finish (
  95. sub {
  96. my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
  97. if (defined($data_structure_reference)) {
  98. # children are not forced to send anything
  99. my $result = ${$data_structure_reference};
  100. push(@arp_array,$result);
  101. }
  102. }
  103. );
  104. DATA_LOOP:
  105. foreach my $router (@router_ref) {
  106. my $router_ip=$router->{ip};
  107. setCommunity($router);
  108. if (!HostIsLive($router_ip)) { log_info("Host id: $router->{id} name: $router->{device_name} ip: $router_ip is down! Skip."); next; }
  109. $pm_arp->start() and next DATA_LOOP;
  110. my $arp_table;
  111. my $tmp_dbh=init_db();
  112. if (apply_device_lock($tmp_dbh,$router->{id})) {
  113. $arp_table=get_arp_table($router_ip,$router->{snmp});
  114. unset_lock_discovery($tmp_dbh,$router->{id});
  115. }
  116. $tmp_dbh->disconnect;
  117. $pm_arp->finish(0, \$arp_table);
  118. }
  119. $pm_arp->wait_all_children;
  120. ########################### end fork's #########################
  121. $dbh=init_db();
  122. #get userid list
  123. my @authlist_ref = get_records_sql($dbh,"SELECT * FROM User_auth WHERE deleted=0 ORDER by ip_int" );
  124. my $users = new Net::Patricia;
  125. my %ip_list;
  126. my %oper_arp_list;
  127. foreach my $row (@authlist_ref) {
  128. $users->add_string($row->{ip},$row->{id});
  129. $ip_list{$row->{id}}->{id}=$row->{id};
  130. $ip_list{$row->{id}}->{ip}=$row->{ip};
  131. $ip_list{$row->{id}}->{mac}=mac_splitted($row->{mac}) || '';
  132. }
  133. foreach my $arp_table (@arp_array) {
  134. foreach my $ip (keys %$arp_table) {
  135. next if (!$arp_table->{$ip});
  136. my $mac=trim($arp_table->{$ip});
  137. $mac=mac_splitted($mac);
  138. next if (!$mac);
  139. next if ($mac=~/ff:ff:ff:ff:ff:ff/i);
  140. next if ($mac!~/(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/);
  141. my $simple_mac=mac_simplify($mac);
  142. $ip=trim($ip);
  143. my $ip_aton=StrToIp($ip);
  144. $mac_history{$simple_mac}{changed}=0;
  145. $mac_history{$simple_mac}{ip}=$ip;
  146. $mac_history{$simple_mac}{auth_id}=0;
  147. next if (!$office_networks->match_string($ip));
  148. db_log_debug($dbh,"Analyze ip: $ip mac: $mac") if ($debug);
  149. my $auth_id = $users->match_string($ip);
  150. my $arp_record;
  151. $arp_record->{ip} = $ip;
  152. $arp_record->{mac} = $mac;
  153. $arp_record->{type} = 'arp';
  154. $arp_record->{ip_aton} = $ip_aton;
  155. $arp_record->{hotspot} = is_hotspot($dbh,$ip);
  156. my $cur_auth_id=resurrection_auth($dbh,$arp_record);
  157. if (!$cur_auth_id) {
  158. db_log_warning($dbh,"Unknown record ".Dumper($arp_record));
  159. } else {
  160. $mac_history{$simple_mac}{auth_id}=$cur_auth_id;
  161. $arp_record->{auth_id}=$cur_auth_id;
  162. $arp_record->{updated}=0;
  163. $oper_arp_list{$cur_auth_id}=$arp_record;
  164. if ($auth_id ne $cur_auth_id) { $mac_history{$simple_mac}{changed}=1; }
  165. }
  166. }
  167. }
  168. db_log_verbose($dbh,'Mac discovery started.');
  169. my %connections=();
  170. my @connections_list=get_records_sql($dbh,"SELECT * FROM connections ORDER BY auth_id");
  171. foreach my $connection (@connections_list) {
  172. next if (!$connection);
  173. $connections{$connection->{auth_id}}{port}=$connection->{port_id};
  174. $connections{$connection->{auth_id}}{id}=$connection->{id};
  175. }
  176. my $auth_filter=" AND last_found >='".$now_day."' ";
  177. my $auth_sql="SELECT id,mac FROM User_auth WHERE mac IS NOT NULL AND deleted=0 $auth_filter ORDER BY id ASC";
  178. my @auth_list=get_records_sql($dbh,$auth_sql);
  179. my %auth_table;
  180. foreach my $auth (@auth_list) {
  181. next if (!$auth);
  182. my $auth_mac=mac_simplify($auth->{mac});
  183. $auth_table{oper_table}{$auth_mac}=$auth->{id};
  184. }
  185. $auth_sql="SELECT id,mac FROM User_auth WHERE mac IS NOT NULL AND deleted=0 ORDER BY last_found DESC";
  186. my @auth_full_list=get_records_sql($dbh,$auth_sql);
  187. foreach my $auth (@auth_full_list) {
  188. next if (!$auth);
  189. my $auth_mac=mac_simplify($auth->{mac});
  190. next if (exists $auth_table{full_table}{$auth_mac});
  191. $auth_table{full_table}{$auth_mac}=$auth->{id};
  192. }
  193. my @unknown_list=get_records_sql($dbh,"SELECT id,mac,port_id,device_id FROM Unknown_mac WHERE mac !='' ORDER BY mac");
  194. my %unknown_table;
  195. foreach my $unknown (@unknown_list) {
  196. next if (!$unknown);
  197. my $unknown_mac=mac_simplify($unknown->{mac});
  198. $unknown_table{$unknown_mac}{unknown_id}=$unknown->{id};
  199. $unknown_table{$unknown_mac}{port_id}=$unknown->{port_id};
  200. $unknown_table{$unknown_mac}{device_id}=$unknown->{device_id};
  201. }
  202. my @device_list = get_records_sql($dbh,"SELECT * FROM devices WHERE deleted=0 AND discovery=1 AND device_type<=2 AND snmp_version>0" );
  203. my @fdb_array=();
  204. my $pm_fdb = Parallel::ForkManager->new($fork_count);
  205. # data structure retrieval and handling
  206. $pm_fdb -> run_on_finish (
  207. sub {
  208. my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
  209. if (defined($data_structure_reference)) { # children are not forced to send anything
  210. my $result = ${$data_structure_reference}; # child passed a string reference
  211. push(@fdb_array,$result);
  212. }
  213. }
  214. );
  215. $dbh->disconnect;
  216. FDB_LOOP:
  217. foreach my $device (@device_list) {
  218. setCommunity($device);
  219. if (!HostIsLive($device->{ip})) { log_info("Host id: $device->{id} name: $device->{device_name} ip: $device->{ip} is down! Skip."); next; }
  220. my $int_list = get_snmp_ifindex($device->{ip},$device->{snmp});
  221. if (!$int_list) { log_info("Host id: $device->{id} name: $device->{device_name} ip: $device->{ip} interfaces not found by snmp request! Skip."); next; }
  222. $pm_fdb->start() and next FDB_LOOP;
  223. my $result;
  224. my $tmp_dbh = init_db();
  225. if (apply_device_lock($tmp_dbh,$device->{id})) {
  226. my $fdb=get_fdb_table($device->{ip},$device->{snmp});
  227. unset_lock_discovery($tmp_dbh,$device->{id});
  228. $result->{id}=$device->{id};
  229. $result->{fdb} = $fdb;
  230. }
  231. $tmp_dbh->disconnect;
  232. $pm_fdb->finish(0, \$result);
  233. }
  234. $pm_fdb->wait_all_children;
  235. my %fdb_ref;
  236. foreach my $fdb_table (@fdb_array){
  237. next if (!$fdb_table);
  238. $fdb_ref{$fdb_table->{id}}{fdb}=$fdb_table->{fdb};
  239. }
  240. ################################ end fork's ##############################
  241. $dbh=init_db();
  242. foreach my $device (@device_list) {
  243. my %port_snmp_index=();
  244. my %port_index=();
  245. my %mac_port_count=();
  246. my %mac_address_table=();
  247. my %port_links=();
  248. my $dev_id = $device->{id};
  249. my $dev_name = $device->{device_name};
  250. my $fdb=$fdb_ref{$dev_id}{fdb};
  251. next if (!$fdb);
  252. my @device_ports = get_records_sql($dbh,"SELECT * FROM device_ports WHERE device_id=$dev_id");
  253. foreach my $port_data (@device_ports) {
  254. my $fdb_port_index=$port_data->{port};
  255. my $port_id = $port_data->{id};
  256. if (!$port_data->{snmp_index}) { $port_data->{snmp_index} = $port_data->{port}; }
  257. $fdb_port_index=$port_data->{snmp_index};
  258. next if ($port_data->{skip});
  259. #snmp-индекс порта = номеру порта
  260. $port_snmp_index{$port_data->{snmp_index}}=$port_data->{port};
  261. # номер порта = id записи порта в таблице
  262. $port_index{$port_data->{port}}=$port_data->{id};
  263. # номер порта = id записи порта аплинка/даунлинка свича
  264. $port_links{$port_data->{port}}=$port_data->{target_port_id};
  265. $mac_port_count{$port_data->{port}}=0;
  266. }
  267. my $sw_mac;
  268. #for mikrotik - skip DL mac
  269. if ($device->{vendor_id} eq '9') {
  270. #get device mac
  271. my $sw_auth = get_record_sql($dbh,"SELECT mac FROM User_auth WHERE deleted=0 and ip='".$device->{ip}."'");
  272. $sw_mac = mac_simplify($sw_auth->{mac});
  273. $sw_mac =~s/.{2}$//s;
  274. }
  275. foreach my $mac (keys %$fdb) {
  276. #port from fdb table
  277. my $port = $fdb->{$mac};
  278. next if (!$port);
  279. #real port number
  280. if (exists $port_snmp_index{$port}) {
  281. #get real port number by snmp our snmp index
  282. $port=$port_snmp_index{$port};
  283. }
  284. if (!exists $port_index{$port}) { next; }
  285. #mikrotik patch - skip mikrotik device mac
  286. if ($sw_mac and $mac=~/^$sw_mac/i) { next; }
  287. $mac_port_count{$port}++;
  288. #мак = номер порта
  289. $mac_address_table{$mac}=$port;
  290. }
  291. # обновляем число маков на порту
  292. foreach my $port (keys %mac_port_count) {
  293. if (!$port) { next; }
  294. if (!exists $port_index{$port}) { next; }
  295. #skip uplink|downlink
  296. if ($port_links{$port}>0) { next; }
  297. my $dev_ports;
  298. $dev_ports->{last_mac_count}=$mac_port_count{$port};
  299. update_record($dbh,'device_ports',$dev_ports,"device_id=$dev_id and port=$port");
  300. }
  301. foreach my $mac (keys %mac_address_table) {
  302. my $port = $mac_address_table{$mac};
  303. if (!$port) { next; }
  304. if (!exists $port_index{$port}) { next; }
  305. #skip uplink|downlink
  306. if ($port_links{$port}>0) { next; }
  307. my $simple_mac=mac_simplify($mac);
  308. my $mac_splitted=mac_splitted($mac);
  309. $mac_history{$simple_mac}{port_id}=$port_index{$port};
  310. $mac_history{$simple_mac}{dev_id}=$dev_id;
  311. if (!$mac_history{$simple_mac}{changed}) { $mac_history{$simple_mac}{changed}=0; }
  312. my $port_id=$port_index{$port};
  313. if (exists $auth_table{full_table}{$simple_mac} or exists $auth_table{oper_table}{$simple_mac}) {
  314. my $auth_id;
  315. if (exists $auth_table{oper_table}{$simple_mac}) {
  316. $auth_id=$auth_table{oper_table}{$simple_mac};
  317. } else {
  318. $auth_id=$auth_table{full_table}{$simple_mac};
  319. db_log_debug($dbh,"Mac not found in oper ARP-table. Use old values auth_id: $auth_id [$simple_mac] at device $dev_name [$port]",$auth_id);
  320. }
  321. if (exists $connections{$auth_id}) {
  322. if ($port_id == $connections{$auth_id}{port}) {
  323. if (exists $auth_table{oper_table}{$simple_mac}) {
  324. my $auth_rec;
  325. $auth_rec->{last_found}=$now_str;
  326. $auth_rec->{arp_found}=$now_str;
  327. $oper_arp_list{$auth_id}{updated}=1;
  328. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  329. }
  330. next;
  331. }
  332. $connections{$auth_id}{port}=$port_id;
  333. $mac_history{$simple_mac}{changed}=1;
  334. $mac_history{$simple_mac}{auth_id}=$auth_id;
  335. db_log_info($dbh,"Found auth_id: $auth_id ip: $mac_history{$simple_mac}{ip} [$mac_splitted] at device $dev_name [$port]. Update connection",$auth_id);
  336. my $auth_rec;
  337. #for exists arp - update arp timestamp
  338. if (exists $auth_table{oper_table}{$simple_mac}) { $auth_rec->{arp_found}=$now_str; }
  339. $auth_rec->{last_found}=$now_str;
  340. $oper_arp_list{$auth_id}{updated}=1;
  341. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  342. my $conn_rec;
  343. $conn_rec->{port_id}=$port_id;
  344. $conn_rec->{device_id}=$dev_id;
  345. update_record($dbh,'connections',$conn_rec,"auth_id=$auth_id");
  346. } else {
  347. $mac_history{$simple_mac}{changed}=1;
  348. $mac_history{$simple_mac}{auth_id}=$auth_id;
  349. $connections{$auth_id}{port}=$port_id;
  350. db_log_info($dbh,"Found auth_id: $auth_id ip: $mac_history{$simple_mac}{ip} [$mac_splitted] at device $dev_name [$port]. Create connection.",$auth_id);
  351. my $auth_rec;
  352. #for exists arp - update arp timestamp
  353. if (exists $auth_table{oper_table}{$simple_mac}) { $auth_rec->{arp_found}=$now_str; }
  354. $auth_rec->{last_found}=$now_str;
  355. $oper_arp_list{$auth_id}{updated}=1;
  356. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  357. my $conn_rec;
  358. $conn_rec->{port_id}=$port_id;
  359. $conn_rec->{device_id}=$dev_id;
  360. $conn_rec->{auth_id}=$auth_id;
  361. insert_record($dbh,'connections',$conn_rec);
  362. }
  363. } else {
  364. if (exists $unknown_table{$simple_mac}{unknown_id}) {
  365. next if ($unknown_table{$simple_mac}{port_id} == $port_id and $unknown_table{$simple_mac}{device_id} == $dev_id);
  366. $mac_history{$simple_mac}{changed}=1;
  367. $mac_history{$simple_mac}{auth_id}=0;
  368. db_log_debug($dbh,"Unknown mac $mac_splitted moved to $dev_name [$port]") if ($debug);
  369. my $unknown_rec;
  370. $unknown_rec->{port_id}=$port_id;
  371. $unknown_rec->{device_id}=$dev_id;
  372. update_record($dbh,'Unknown_mac',$unknown_rec,"id=$unknown_table{$simple_mac}{unknown_id}");
  373. } else {
  374. $mac_history{$simple_mac}{changed}=1;
  375. $mac_history{$simple_mac}{auth_id}=0;
  376. db_log_debug($dbh,"Unknown mac $mac_splitted found at $dev_name [$port]") if ($debug);
  377. my $unknown_rec;
  378. $unknown_rec->{port_id}=$port_id;
  379. $unknown_rec->{device_id}=$dev_id;
  380. $unknown_rec->{mac}=$simple_mac;
  381. insert_record($dbh,'Unknown_mac',$unknown_rec);
  382. }
  383. }
  384. }
  385. }
  386. foreach my $auth_id (keys %oper_arp_list) {
  387. next if ($oper_arp_list{$auth_id}->{updated});
  388. my $auth_rec;
  389. $auth_rec->{last_found}=$now_str;
  390. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  391. db_log_debug($dbh,"Update by arp at unknown connect place for: ".Dumper($oper_arp_list{$auth_id})) if ($debug);
  392. }
  393. foreach my $mac (keys %mac_history) {
  394. next if (!$mac);
  395. next if (!$mac_history{$mac}->{changed});
  396. my $h_dev_id='';
  397. my $h_port_id='';
  398. my $h_ip='';
  399. $h_dev_id=$mac_history{$mac}->{dev_id} if ($mac_history{$mac}->{dev_id});
  400. $h_port_id=$mac_history{$mac}->{port_id} if ($mac_history{$mac}->{port_id});
  401. $h_ip=$mac_history{$mac}->{ip} if ($mac_history{$mac}->{ip});
  402. my $h_auth_id=$mac_history{$mac}->{auth_id} if ($mac_history{$mac}->{auth_id});
  403. if (!$h_auth_id) { $h_auth_id=0; }
  404. next if (!$h_dev_id);
  405. my $history_rec;
  406. $history_rec->{device_id}=$h_dev_id;
  407. $history_rec->{port_id}=$h_port_id;
  408. $history_rec->{mac}=$mac;
  409. $history_rec->{ip}=$h_ip;
  410. $history_rec->{auth_id}=$h_auth_id;
  411. insert_record($dbh,'mac_history',$history_rec);
  412. }
  413. $dbh->disconnect;
  414. exit 0;