fetch_new_arp.pl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #!/usr/bin/perl
  2. #
  3. # Copyright (C) Roman Dmitiriev, rnd@rajven.ru
  4. #
  5. use FindBin '$Bin';
  6. use lib "$Bin/";
  7. use strict;
  8. use DBI;
  9. use Time::Local;
  10. use Net::Patricia;
  11. use Data::Dumper;
  12. use Date::Parse;
  13. use Socket;
  14. use Rstat::config;
  15. use Rstat::main;
  16. use Rstat::net_utils;
  17. use Rstat::snmp;
  18. use Rstat::mysql;
  19. use NetAddr::IP;
  20. use Fcntl qw(:flock);
  21. use Parallel::ForkManager;
  22. open(SELF,"<",$0) or die "Cannot open $0 - $!";
  23. flock(SELF, LOCK_EX|LOCK_NB) or exit 1;
  24. setpriority(0,0,19);
  25. my %mac_history;
  26. my ($sec,$min,$hour,$day,$month,$year,$zone) = localtime(time());
  27. $month += 1;
  28. $year += 1900;
  29. my $fork_count = $cpu_count*5;
  30. #disable fork for debug
  31. #if ($debug) { $fork_count = 0; }
  32. my $now_str=sprintf "%04d-%02d-%02d %02d:%02d:%02d",$year,$month,$day,$hour,$min,$sec;
  33. my $now_day=sprintf "%04d-%02d-%02d",$year,$month,$day;
  34. if (!$arp_discovery) {
  35. db_log_verbose($dbh,'Arp discovery disabled by config');
  36. } else {
  37. db_log_verbose($dbh,'Arp discovery started.');
  38. if ($ARGV[0]) {
  39. db_log_verbose($dbh,'Active check started!');
  40. my $subnets=get_subnets_ref($dbh);
  41. my @fping_cmd=();
  42. foreach my $net (keys %$subnets) {
  43. next if (!$net);
  44. next if (!$subnets->{$net}{discovery});
  45. my $run_cmd="$fping -g $subnets->{$net}{subnet} -B1.0 -c 1 >/dev/null 2>&1";
  46. db_log_debug($dbh,"Checked network $subnets->{$net}{subnet}") if ($debug);
  47. push(@fping_cmd,$run_cmd);
  48. }
  49. $parallel_process_count = $cpu_count*2;
  50. run_in_parallel(@fping_cmd);
  51. }
  52. my @router_ref = get_records_sql($dbh,"SELECT * FROM devices WHERE deleted=0 AND device_type=2 AND discovery=1 AND snmp_version>0 ORDER by ip" );
  53. my @arp_array=();
  54. my $pm_arp = Parallel::ForkManager->new($fork_count);
  55. # data structure retrieval and handling
  56. $pm_arp -> run_on_finish (
  57. sub {
  58. my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
  59. if (defined($data_structure_reference)) { # children are not forced to send anything
  60. my $result = ${$data_structure_reference}; # child passed a string reference
  61. push(@arp_array,$result);
  62. }
  63. }
  64. );
  65. DATA_LOOP:
  66. foreach my $router (@router_ref) {
  67. my $router_ip=$router->{ip};
  68. my $snmp_version=$router->{snmp_version};
  69. my $community=$router->{community};
  70. $pm_arp->start() and next DATA_LOOP;
  71. my $arp_table=get_arp_table($router_ip,$community,$snmp_version);
  72. $pm_arp->finish(0, \$arp_table);
  73. }
  74. $pm_arp->wait_all_children;
  75. ########################### end fork's #########################
  76. $dbh=init_db();
  77. #get userid list
  78. my @authlist_ref = get_records_sql($dbh,"SELECT * FROM User_auth WHERE deleted=0 ORDER by ip_int" );
  79. my $users = new Net::Patricia;
  80. my %ip_list;
  81. foreach my $row (@authlist_ref) {
  82. $users->add_string($row->{ip},$row->{id});
  83. $ip_list{$row->{id}}->{id}=$row->{id};
  84. $ip_list{$row->{id}}->{ip}=$row->{ip};
  85. $ip_list{$row->{id}}->{mac}=mac_splitted($row->{mac}) || '';
  86. }
  87. foreach my $arp_table (@arp_array) {
  88. foreach my $ip (keys %$arp_table) {
  89. next if (!$arp_table->{$ip});
  90. my $mac=trim($arp_table->{$ip});
  91. $mac=mac_splitted($mac);
  92. next if (!$mac);
  93. next if ($mac=~/ff:ff:ff:ff:ff:ff/i);
  94. next if ($mac!~/(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/);
  95. my $simple_mac=mac_simplify($mac);
  96. $ip=trim($ip);
  97. my $ip_aton=StrToIp($ip);
  98. $mac_history{$simple_mac}{changed}=0;
  99. $mac_history{$simple_mac}{ip}=$ip;
  100. $mac_history{$simple_mac}{auth_id}=0;
  101. next if (!$office_networks->match_string($ip));
  102. db_log_debug($dbh,"Analyze ip: $ip mac: $mac") if ($debug);
  103. my $auth_id = $users->match_string($ip);
  104. my $cur_auth_id=resurrection_auth($dbh,$ip,$mac,'arp');
  105. $mac_history{$simple_mac}{auth_id}=$cur_auth_id;
  106. if ($auth_id ne $cur_auth_id) { $mac_history{$simple_mac}{changed}=1; }
  107. }
  108. }
  109. db_log_verbose($dbh,'Arp discovery stopped.');
  110. }
  111. #MAC Discavery
  112. if (!$mac_discovery) {
  113. db_log_verbose($dbh,'Mac discovery disabled by config');
  114. } else {
  115. sleep(1);
  116. db_log_verbose($dbh,'Mac discovery started.');
  117. my %connections=();
  118. my @connections_list=get_records_sql($dbh,"SELECT * FROM connections ORDER BY auth_id");
  119. foreach my $connection (@connections_list) {
  120. next if (!$connection);
  121. $connections{$connection->{auth_id}}{port}=$connection->{port_id};
  122. $connections{$connection->{auth_id}}{id}=$connection->{id};
  123. }
  124. my $auth_filter='';
  125. if ($arp_discovery) { $auth_filter=" AND last_found >='".$now_day."' "; }
  126. my $auth_sql="SELECT id,mac FROM User_auth WHERE mac IS NOT NULL AND deleted=0 $auth_filter ORDER BY id ASC";
  127. my @auth_list=get_records_sql($dbh,$auth_sql);
  128. my %auth_table;
  129. foreach my $auth (@auth_list) {
  130. next if (!$auth);
  131. my $auth_mac=mac_simplify($auth->{mac});
  132. $auth_table{oper_table}{$auth_mac}=$auth->{id};
  133. }
  134. $auth_sql="SELECT id,mac FROM User_auth WHERE mac IS NOT NULL AND deleted=0 ORDER BY id ASC";
  135. my @auth_full_list=get_records_sql($dbh,$auth_sql);
  136. foreach my $auth (@auth_full_list) {
  137. next if (!$auth);
  138. my $auth_mac=mac_simplify($auth->{mac});
  139. $auth_table{full_table}{$auth_mac}=$auth->{id};
  140. }
  141. my @unknown_list=get_records_sql($dbh,"SELECT id,mac,port_id,device_id FROM Unknown_mac WHERE mac !='' ORDER BY mac");
  142. my %unknown_table;
  143. foreach my $unknown (@unknown_list) {
  144. next if (!$unknown);
  145. my $unknown_mac=mac_simplify($unknown->{mac});
  146. $unknown_table{$unknown_mac}{unknown_id}=$unknown->{id};
  147. $unknown_table{$unknown_mac}{port_id}=$unknown->{port_id};
  148. $unknown_table{$unknown_mac}{device_id}=$unknown->{device_id};
  149. }
  150. my @device_list = get_records_sql($dbh,"SELECT * FROM devices WHERE deleted=0 AND discovery=1 AND snmp_version>0" );
  151. my @fdb_array=();
  152. my $pm_fdb = Parallel::ForkManager->new($fork_count);
  153. # data structure retrieval and handling
  154. $pm_fdb -> run_on_finish (
  155. sub {
  156. my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
  157. if (defined($data_structure_reference)) { # children are not forced to send anything
  158. my $result = ${$data_structure_reference}; # child passed a string reference
  159. push(@fdb_array,$result);
  160. }
  161. }
  162. );
  163. FDB_LOOP:
  164. foreach my $device (@device_list) {
  165. $pm_fdb->start() and next FDB_LOOP;
  166. my $fdb=get_fdb_table($device->{ip},$device->{community},$device->{snmp_version});
  167. my $vlans = get_switch_vlans($device->{ip},$device->{community},$device->{snmp_version});
  168. my $result;
  169. $result->{id}=$device->{id};
  170. $result->{fdb} = $fdb;
  171. $result->{vlans} = $vlans;
  172. $pm_fdb->finish(0, \$result);
  173. }
  174. $pm_fdb->wait_all_children;
  175. my %fdb_ref;
  176. foreach my $fdb_table (@fdb_array){
  177. next if (!$fdb_table);
  178. $fdb_ref{$fdb_table->{id}}{fdb}=$fdb_table->{fdb};
  179. $fdb_ref{$fdb_table->{id}}{vlans}=$fdb_table->{vlans};
  180. }
  181. ################################ end fork's ##############################
  182. $dbh=init_db();
  183. foreach my $device (@device_list) {
  184. my %port_snmp_index=();
  185. my %port_index=();
  186. my %mac_port_count=();
  187. my %mac_address_table=();
  188. my %port_links=();
  189. my $dev_id = $device->{id};
  190. my $dev_name = $device->{device_name};
  191. my $fdb=$fdb_ref{$dev_id}{fdb};
  192. my $vlans=$fdb_ref{$dev_id}{vlans};
  193. next if (!$fdb);
  194. my @device_ports = get_records_sql($dbh,"SELECT * FROM device_ports WHERE device_id=$dev_id");
  195. foreach my $port_data (@device_ports) {
  196. my $vlan = $port_data->{vlan};
  197. if (!$vlan) { $vlan=1; }
  198. if (!$port_data->{snmp_index}) { $port_data->{snmp_index} = $port_data->{port}; }
  199. my $port_index=$port_data->{snmp_index};
  200. my $current_vlan = $vlans->{$port_data->{snmp_index}};
  201. if (!$current_vlan) { $current_vlan=1; }
  202. if ($current_vlan != $vlan) {
  203. my $dev_ports;
  204. $dev_ports->{vlan}=$current_vlan;
  205. update_record($dbh,'device_ports',$dev_ports,"device_id=$dev_id and port=$port_data->{port}");
  206. db_log_verbose($dbh,"Vlan changed at device $dev_name [$port_data->{port}] old: $vlan current: $current_vlan");
  207. }
  208. next if ($port_data->{skip});
  209. $port_snmp_index{$port_data->{snmp_index}}=$port_data->{port};
  210. $port_index{$port_data->{port}}=$port_data->{id};
  211. $port_links{$port_data->{port}}=$port_data->{target_port_id};
  212. $mac_port_count{$port_data->{port}}=0;
  213. }
  214. foreach my $mac (keys %$fdb) {
  215. #port from fdb table
  216. my $port = $fdb->{$mac};
  217. next if (!$port);
  218. if (!exists $port_snmp_index{$port}) { next; }
  219. #real port number
  220. $port=$port_snmp_index{$port};
  221. $mac_port_count{$port}++;
  222. $mac_address_table{$mac}=$port;
  223. }
  224. foreach my $port (keys %mac_port_count) {
  225. if (!$port) { next; }
  226. if (!exists $port_index{$port}) { next; }
  227. #skip uplink|downlink
  228. if ($port_links{$port}>0) { next; }
  229. my $dev_ports;
  230. $dev_ports->{last_mac_count}=$mac_port_count{$port};
  231. update_record($dbh,'device_ports',$dev_ports,"device_id=$dev_id and port=$port");
  232. }
  233. foreach my $mac (keys %mac_address_table) {
  234. my $port = $mac_address_table{$mac};
  235. if (!$port) { next; }
  236. if (!exists $port_index{$port}) { next; }
  237. #skip uplink|downlink
  238. if ($port_links{$port}>0) { next; }
  239. my $simple_mac=mac_simplify($mac);
  240. $mac_history{$simple_mac}{port_id}=$port_index{$port};
  241. $mac_history{$simple_mac}{dev_id}=$dev_id;
  242. if (!$mac_history{$simple_mac}{changed}) { $mac_history{$simple_mac}{changed}=0; }
  243. my $port_id=$port_index{$port};
  244. if (exists $auth_table{full_table}{$mac} or exists $auth_table{oper_table}{$mac}) {
  245. my $auth_id;
  246. if (exists $auth_table{oper_table}{$mac}) { $auth_id=$auth_table{oper_table}{$mac}; } else {
  247. $auth_id=$auth_table{full_table}{$mac};
  248. if ($debug) {
  249. db_log_debug($dbh,"Mac not found in oper ARP-table. Use old values auth_id: $auth_id [$mac] at device $dev_name [$port]");
  250. }
  251. }
  252. if (exists $connections{$auth_id}) {
  253. if ($port_id == $connections{$auth_id}{port}) {
  254. if (exists $auth_table{oper_table}{$mac}) {
  255. my $auth_rec;
  256. $auth_rec->{last_found}=$now_str;
  257. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  258. }
  259. next;
  260. }
  261. $connections{$auth_id}{port}=$port_id;
  262. $mac_history{$simple_mac}{changed}=1;
  263. $mac_history{$simple_mac}{auth_id}=$auth_id;
  264. db_log_info($dbh,"Found auth_id: $auth_id [$mac] at device $dev_name [$port]. Update connection");
  265. my $auth_rec;
  266. $auth_rec->{last_found}=$now_str;
  267. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  268. my $conn_rec;
  269. $conn_rec->{port_id}=$port_id;
  270. $conn_rec->{device_id}=$dev_id;
  271. update_record($dbh,'connections',$conn_rec,"auth_id=$auth_id");
  272. } else {
  273. $mac_history{$simple_mac}{changed}=1;
  274. $mac_history{$simple_mac}{auth_id}=$auth_id;
  275. $connections{$auth_id}{port}=$port_id;
  276. db_log_info($dbh,"Found auth_id: $auth_id [$mac] at device $dev_name [$port]. Create connection.");
  277. my $auth_rec;
  278. $auth_rec->{last_found}=$now_str;
  279. update_record($dbh,'User_auth',$auth_rec,"id=".$auth_id);
  280. my $conn_rec;
  281. $conn_rec->{port_id}=$port_id;
  282. $conn_rec->{device_id}=$dev_id;
  283. $conn_rec->{auth_id}=$auth_id;
  284. insert_record($dbh,'connections',$conn_rec);
  285. }
  286. } else {
  287. if (exists $unknown_table{$simple_mac}{unknown_id}) {
  288. next if ($unknown_table{$simple_mac}{port_id} == $port_id and $unknown_table{$simple_mac}{device_id} == $dev_id);
  289. $mac_history{$simple_mac}{changed}=1;
  290. $mac_history{$simple_mac}{auth_id}=0;
  291. $mac=mac_splitted($mac);
  292. db_log_debug($dbh,"Unknown mac $mac moved to $dev_name [$port]") if ($debug);
  293. my $unknown_rec;
  294. $unknown_rec->{port_id}=$port_id;
  295. $unknown_rec->{device_id}=$dev_id;
  296. update_record($dbh,'Unknown_mac',$unknown_rec,"id=$unknown_table{$simple_mac}{unknown_id}");
  297. } else {
  298. $mac=mac_splitted($mac);
  299. $mac_history{$simple_mac}{changed}=1;
  300. $mac_history{$simple_mac}{auth_id}=0;
  301. db_log_debug($dbh,"Unknown mac $mac found at $dev_name [$port]") if ($debug);
  302. my $unknown_rec;
  303. $unknown_rec->{port_id}=$port_id;
  304. $unknown_rec->{device_id}=$dev_id;
  305. $unknown_rec->{mac}=$simple_mac;
  306. insert_record($dbh,'Unknown_mac',$unknown_rec);
  307. }
  308. }
  309. }
  310. }
  311. db_log_verbose($dbh,'Mac discovery stopped.');
  312. }
  313. foreach my $mac (keys %mac_history) {
  314. next if (!$mac);
  315. next if (!$mac_history{$mac}->{changed});
  316. my $h_dev_id='';
  317. $h_dev_id=$mac_history{$mac}->{dev_id} if ($mac_history{$mac}->{dev_id});
  318. my $h_port_id='';
  319. $h_port_id=$mac_history{$mac}->{port_id} if ($mac_history{$mac}->{port_id});
  320. my $h_ip='';
  321. $h_ip=$mac_history{$mac}->{ip} if ($mac_history{$mac}->{ip});
  322. my $h_auth_id=$mac_history{$mac}->{auth_id} if ($mac_history{$mac}->{auth_id});
  323. if (!$h_auth_id) { $h_auth_id=0; }
  324. next if (!$h_dev_id);
  325. my $history_rec;
  326. $history_rec->{device_id}=$h_dev_id;
  327. $history_rec->{port_id}=$h_port_id;
  328. $history_rec->{mac}=$mac;
  329. $history_rec->{ip}=$h_ip;
  330. $history_rec->{auth_id}=$h_auth_id;
  331. insert_record($dbh,'mac_history',$history_rec);
  332. }
  333. $dbh->disconnect;
  334. exit 0;