snmp.pm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. package eyelib::snmp;
  2. #
  3. # Copyright (C) Roman Dmitriev, rnd@rajven.ru
  4. #
  5. use utf8;
  6. use open ":encoding(utf8)";
  7. use strict;
  8. use English;
  9. use FindBin '$Bin';
  10. use lib "/opt/Eye/scripts";
  11. use base 'Exporter';
  12. use vars qw(@EXPORT @ISA);
  13. use Data::Dumper;
  14. use eyelib::config;
  15. use eyelib::main;
  16. use eyelib::logconfig;
  17. use Net::SNMP;
  18. @ISA = qw(Exporter);
  19. @EXPORT = qw(
  20. snmp_get_request
  21. snmp_set_int
  22. get_arp_table
  23. snmp_ping
  24. get_ifmib_index_table
  25. get_mac_table
  26. get_fdb_table
  27. get_vlan_at_port
  28. get_switch_vlans
  29. get_snmp_ifindex
  30. getIpAdEntIfIndex
  31. get_interfaces
  32. get_router_state
  33. snmp_get_req
  34. snmp_get_oid
  35. snmp_walk_oid
  36. oid_base_match
  37. snmp_oid_compare
  38. table_callback
  39. init_snmp
  40. setCommunity
  41. $ifAlias
  42. $ifName
  43. $ifDescr
  44. $ifIndex
  45. $ifIndex_map
  46. $ipAdEntIfIndex
  47. $arp_oid
  48. $ipNetToMediaPhysAddress
  49. $fdb_table_oid
  50. $fdb_table_oid2
  51. $dot1qPortVlanEntry
  52. $cisco_vlan_oid
  53. $sysUpTimeInstance
  54. $ifPortStatus
  55. $ifPortAdminStatus
  56. $bgp_prefixes
  57. $bgp_aslist
  58. $hrDeviceDescr
  59. $hrProcessorLoad
  60. $hrMemorySize
  61. $hrStorageIndex
  62. $hrStorageType
  63. $hrStorageDescr
  64. $hrStorageAllocationUnits
  65. $hrStorageSize
  66. $hrStorageUsed
  67. $fdb_table
  68. $snmp_timeout
  69. );
  70. BEGIN
  71. {
  72. our $ifAlias ='.1.3.6.1.2.1.31.1.1.1.18';
  73. our $ifName ='.1.3.6.1.2.1.31.1.1.1.1';
  74. our $ifDescr ='.1.3.6.1.2.1.2.2.1.2';
  75. our $ifIndex ='.1.3.6.1.2.1.2.2.1.1';
  76. our $ifIndex_map ='.1.3.6.1.2.1.17.1.4.1.2';
  77. our $ipAdEntIfIndex ='.1.3.6.1.2.1.4.20.1.2';
  78. #RFC1213::atPhysAddress
  79. our $arp_oid ='.1.3.6.1.2.1.3.1.1.2';
  80. #RFC1213::ipNetToMediaPhysAddress
  81. our $ipNetToMediaPhysAddress = '.1.3.6.1.2.1.4.22.1.2';
  82. #RFC1493::dot1dTpFdbTable
  83. our $fdb_table_oid ='.1.3.6.1.2.1.17.4.3.1.2';
  84. #Q-BRIDGE-MIB::dot1qTpFdbPort
  85. our $fdb_table_oid2='.1.3.6.1.2.1.17.7.1.2.2.1.2';
  86. #Q-BRIDGE-MIB::dot1qPortVlanEntry
  87. our $dot1qPortVlanEntry ='.1.3.6.1.2.1.17.7.1.4.5.1.1';
  88. #CISCO-ES-STACK-MIB::
  89. our $cisco_vlan_oid='.1.3.6.1.4.1.9.9.46.1.3.1.1.2';
  90. our $sysUpTimeInstance = '1.3.6.1.2.1.1.3.0';
  91. our $ifPortStatus = '1.3.6.1.2.1.2.2.1.8';
  92. our $ifPortAdminStatus = '1.3.6.1.2.1.2.2.1.7';
  93. our $bgp_prefixes = '1.3.6.1.4.1.9.9.187.1.2.4.1.1';
  94. our $bgp_aslist = '1.3.6.1.2.1.15.3.1.9';
  95. our $hrDeviceDescr = '1.3.6.1.2.1.25.3.2.1.3';
  96. our $hrProcessorLoad = '1.3.6.1.2.1.25.3.3.1.2';
  97. our $hrMemorySize = '1.3.6.1.2.1.25.2.2.0';
  98. our $hrStorageIndex = '1.3.6.1.2.1.25.2.3.1.1';
  99. our $hrStorageType = '1.3.6.1.2.1.25.2.3.1.2';
  100. our $hrStorageDescr = '1.3.6.1.2.1.25.2.3.1.3';
  101. our $hrStorageAllocationUnits = '1.3.6.1.2.1.25.2.3.1.4';
  102. our $hrStorageSize = '1.3.6.1.2.1.25.2.3.1.5';
  103. our $hrStorageUsed = '1.3.6.1.2.1.25.2.3.1.6';
  104. our $snmp_timeout = 15;
  105. #---------------------------------------------------------------------------------
  106. sub snmp_get_request {
  107. my $ip = shift;
  108. my $oid = shift;
  109. my $snmp = shift;
  110. my $session = init_snmp ($ip,$snmp);
  111. return if (!defined($session) or !$session);
  112. my $result = $session->get_request( -varbindlist => [$oid]);
  113. $session->close;
  114. return if (!$result->{$oid});
  115. return $result->{$oid};
  116. }
  117. #---------------------------------------------------------------------------------
  118. sub snmp_set_int {
  119. my $ip = shift;
  120. my $oid = shift;
  121. my $value = shift;
  122. my $snmp = shift;
  123. my $session = init_snmp ($ip,$snmp,1);
  124. return if (!defined($session) or !$session);
  125. my $result = $session->set_request( -varbindlist => [$oid,INTEGER,$value]);
  126. $session->close;
  127. return $result->{$oid};
  128. }
  129. #-------------------------------------------------------------------------------------
  130. sub get_arp_table {
  131. my ($host,$snmp) = @_;
  132. my $session = init_snmp ($host,$snmp,0);
  133. return if (!defined($session) or !$session);
  134. $session->translate([-all]);
  135. my $arp;
  136. my $arp_table1 = $session->get_table($arp_oid);
  137. my $arp_table2 = $session->get_table($ipNetToMediaPhysAddress);
  138. $session->close();
  139. if ($arp_table1) {
  140. foreach my $row (keys(%$arp_table1)) {
  141. my ($mac_h) = unpack("H*",$arp_table1->{$row});
  142. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  143. my $mac;
  144. if (length($mac_h)==12) { $mac=lc $mac_h; }
  145. next if (!$mac);
  146. $row=trim($row);
  147. my $ip;
  148. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  149. next if (!$ip);
  150. $arp->{$ip}=$mac;
  151. };
  152. }
  153. if ($arp_table2) {
  154. foreach my $row (keys(%$arp_table2)) {
  155. my ($mac_h) = unpack("H*",$arp_table2->{$row});
  156. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  157. my $mac;
  158. if (length($mac_h)==12) { $mac=lc $mac_h; }
  159. next if (!$mac);
  160. $row=trim($row);
  161. my $ip;
  162. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  163. next if (!$ip);
  164. $arp->{$ip}=$mac;
  165. };
  166. }
  167. return $arp;
  168. }
  169. #---------------------------------------------------------------------------------
  170. sub snmp_ping {
  171. my ($host,$snmp) = @_;
  172. my @test_oids = (
  173. '.1.3.6.1.2.1.1.1.0', # model
  174. '.1.3.6.1.2.1.1.3.0', # uptime
  175. '.1.3.6.1.2.1.1.5.0', # name
  176. );
  177. my $result;
  178. my $old_sig_alarm = $SIG{ALRM};
  179. $SIG{ALRM} = sub { die "Timeout 5 sec reached.\n" };
  180. alarm($WAIT_TIME // 5);
  181. eval {
  182. foreach my $oid (@test_oids) {
  183. $result = snmp_get_request($host,$oid,$snmp);
  184. last if defined $result;
  185. }
  186. };
  187. my $eval_error = $@;
  188. alarm(0);
  189. $SIG{ALRM} = $old_sig_alarm;
  190. return (defined $result && !$eval_error) ? 1 : 0;
  191. }
  192. #-------------------------------------------------------------------------------------
  193. sub get_ifmib_index_table {
  194. my $ip = shift;
  195. my $snmp = shift;
  196. my $ifmib_map;
  197. my $is_mikrotik = snmp_get_request($ip, '.1.3.6.1.2.1.9999.1.1.1.1.0', $snmp);
  198. my $mk_ros_version = 0;
  199. if ($is_mikrotik=~/MikroTik/i) {
  200. my $mikrotik_version = snmp_get_request($ip, '.1.0.8802.1.1.2.1.3.4.0', $snmp);
  201. $mk_ros_version = 6491;
  202. #"MikroTik RouterOS 6.46.8 (long-term) CRS326-24S+2Q+"
  203. if ($mikrotik_version =~/RouterOS\s+(\d)\.(\d{1,3})\.(\d{1,3})\s+/) {
  204. $mk_ros_version = $1*1000 + $2*10 + $3;
  205. }
  206. }
  207. if (!$mk_ros_version or $mk_ros_version > 6468) {
  208. my $index_map_table = snmp_get_oid($ip, $snmp, $ifIndex_map);
  209. if (!$index_map_table) { $index_map_table = snmp_walk_oid($ip, $snmp, $ifIndex_map); }
  210. if ($index_map_table) {
  211. foreach my $row (keys(%$index_map_table)) {
  212. my $port_index = $index_map_table->{$row};
  213. next if (!$port_index);
  214. my $value;
  215. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  216. next if (!$value);
  217. $ifmib_map->{$value}=$port_index;
  218. }
  219. }
  220. }
  221. if (!$ifmib_map) {
  222. my $index_table = snmp_get_oid($ip, $snmp, $ifIndex);
  223. if (!$index_table) { $index_table = snmp_walk_oid($ip, $snmp, $ifIndex); }
  224. foreach my $row (keys(%$index_table)) {
  225. my $port_index = $index_table->{$row};
  226. next if (!$port_index);
  227. my $value;
  228. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  229. next if (!$value);
  230. $ifmib_map->{$value}=$value;
  231. };
  232. }
  233. return $ifmib_map;
  234. }
  235. #-------------------------------------------------------------------------------------
  236. sub get_mac_table {
  237. my ($host,$snmp,$oid,$index_map) = @_;
  238. my $fdb;
  239. my $fdb_table1 = snmp_get_oid($host,$snmp,$oid);
  240. if (!$fdb_table1) { $fdb_table1=snmp_walk_oid($host,$snmp,$oid,undef); }
  241. if ($fdb_table1) {
  242. foreach my $row (keys(%$fdb_table1)) {
  243. my $port_index = $fdb_table1->{$row};
  244. next if (!$port_index);
  245. my $mac;
  246. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) {
  247. $mac=sprintf "%02x%02x%02x%02x%02x%02x",$1,$2,$3,$4,$5,$6;
  248. }
  249. next if (!$mac);
  250. if ($index_map and exists $index_map->{$port_index}) { $port_index = $index_map->{$port_index}; }
  251. $fdb->{$mac}=$port_index;
  252. };
  253. return $fdb;
  254. }
  255. }
  256. #-------------------------------------------------------------------------------------
  257. sub get_fdb_table {
  258. my ($host,$snmp) = @_;
  259. my $ifindex_map = get_ifmib_index_table($host,$snmp);
  260. # print "IFINDEX_MAP: " . Dumper($ifindex_map);
  261. my $fdb1=get_mac_table($host,$snmp,$fdb_table_oid,$ifindex_map);
  262. # print "FDB1: " . Dumper($fdb1);
  263. my $fdb2=get_mac_table($host,$snmp,$fdb_table_oid2,$ifindex_map);
  264. # print "FDB2: " . Dumper($fdb1);
  265. my $fdb;
  266. #join tables
  267. if (!$fdb1 and $fdb2) { $fdb = $fdb2; }
  268. if (!$fdb2 and $fdb1) { $fdb = $fdb1; }
  269. if ($fdb1 and $fdb2) { $fdb = { %$fdb1,%$fdb2 }; }
  270. my $snmp_cisco = $snmp;
  271. #maybe cisco?!
  272. if (!$fdb) {
  273. my $vlan_table=snmp_get_oid($host,$snmp,$cisco_vlan_oid);
  274. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$cisco_vlan_oid); }
  275. # just empty
  276. if (!$vlan_table) { return; }
  277. #fucking cisco!
  278. my %fdb_vlan;
  279. foreach my $vlan_oid (keys %$vlan_table) {
  280. next if (!$vlan_oid);
  281. my $vlan_id;
  282. if ($vlan_oid=~/\.([0-9]{1,4})$/) { $vlan_id=$1; }
  283. next if (!$vlan_id);
  284. next if ($vlan_id>1000 and $vlan_id<=1009);
  285. $snmp_cisco->{'ro-community'} = $snmp->{'ro-community'}.'@'.$vlan_id;
  286. $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp_cisco,$fdb_table_oid,$ifindex_map);
  287. if (!$fdb_vlan{$vlan_id}) { $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp_cisco,$fdb_table_oid2,$ifindex_map); }
  288. }
  289. foreach my $vlan_id (keys %fdb_vlan) {
  290. next if (!exists $fdb_vlan{$vlan_id});
  291. if (defined $fdb_vlan{$vlan_id}) {
  292. my %tmp=%{$fdb_vlan{$vlan_id}};
  293. foreach my $mac (keys %tmp) {
  294. next if (!$mac);
  295. $fdb->{$mac}=$tmp{$mac};
  296. }
  297. }
  298. }
  299. }
  300. return $fdb;
  301. }
  302. #-------------------------------------------------------------------------------------
  303. sub get_vlan_at_port {
  304. my ($host,$snmp,$port_index) = @_;
  305. my $vlan_oid=$dot1qPortVlanEntry.".".$port_index;
  306. my $vlan = snmp_get_req($host,$snmp,$vlan_oid);
  307. return "1" if (!$vlan);
  308. return "1" if ($vlan=~/noSuchObject/i);
  309. return "1" if ($vlan=~/noSuchInstance/i);
  310. return $vlan;
  311. }
  312. #-------------------------------------------------------------------------------------
  313. sub get_switch_vlans {
  314. my ($host,$snmp) = @_;
  315. my $result;
  316. #need for callback
  317. my $vlan_table = snmp_get_oid($host,$snmp,$dot1qPortVlanEntry);
  318. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$dot1qPortVlanEntry); }
  319. if ($vlan_table) {
  320. foreach my $vlan_oid (keys %$vlan_table) {
  321. if ($vlan_oid=~/\.([0-9]*)$/) { $result->{$1} = $vlan_table->{$vlan_oid}; }
  322. }
  323. }
  324. return $result;
  325. }
  326. #-------------------------------------------------------------------------------------
  327. sub get_snmp_ifindex {
  328. my ($host,$snmp) = @_;
  329. my $session = init_snmp($host,$snmp,0);
  330. return if (!defined($session) or !$session);
  331. my $if_index = $session->get_table($ifIndex);
  332. my $result;
  333. foreach my $row (keys(%$if_index)) {
  334. my $value = $if_index->{$row};
  335. $row=~s/^$ifIndex\.//;
  336. $result->{$row}=$value;
  337. };
  338. $session->close();
  339. return $result;
  340. }
  341. #-------------------------------------------------------------------------------------
  342. #get ip interfaces
  343. sub getIpAdEntIfIndex {
  344. my ($host,$snmp) = @_;
  345. my $session = init_snmp ($host,$snmp,0);
  346. return if (!defined($session) or !$session);
  347. $session->translate([-timeticks]);
  348. my $if_ipaddr = $session->get_table($ipAdEntIfIndex);
  349. my $l3_list;
  350. foreach my $row (keys(%$if_ipaddr)) {
  351. my $ipaddr = $row;
  352. $ipaddr=~s/$ipAdEntIfIndex\.//;
  353. $l3_list->{$ipaddr}=$if_ipaddr->{$row};
  354. }
  355. $session->close();
  356. return $l3_list;
  357. }
  358. #-------------------------------------------------------------------------------------
  359. sub get_interfaces {
  360. my ($host,$snmp,$skip_empty) = @_;
  361. my $session = init_snmp ($host,$snmp,0);
  362. return if (!defined($session) or !$session);
  363. $session->translate([-timeticks]);
  364. my $if_name = $session->get_table($ifName);
  365. my $if_alias = $session->get_table($ifAlias);
  366. my $if_descr = $session->get_table($ifDescr);
  367. my $if_index = $session->get_table($ifIndex);
  368. $session->close();
  369. my $dev_cap;
  370. foreach my $row (keys(%$if_index)) {
  371. my $index = $if_index->{$row};
  372. next if ($if_name->{$ifName.".".$index} =~/^lo/i);
  373. next if ($if_name->{$ifName.".".$index} =~/^dummy/i);
  374. next if ($if_name->{$ifName.".".$index} =~/^enet/i);
  375. next if ($if_name->{$ifName.".".$index} =~/^Nu/i);
  376. # next if ($if_name->{$ifName.".".$index} =~/^Po/i);
  377. my $ifc_alias;
  378. $ifc_alias=$if_alias->{$ifAlias.".".$index} if ($if_alias->{$ifAlias.".".$index});
  379. my $ifc_name;
  380. $ifc_name=$if_name->{$ifName.".".$index} if ($if_name->{$ifName.".".$index});
  381. my $ifc_desc;
  382. $ifc_desc=$if_descr->{$ifDescr.".".$index} if ($if_descr->{$ifDescr.".".$index});
  383. $dev_cap->{$index}->{alias}=$ifc_alias if ($ifc_alias);
  384. $dev_cap->{$index}->{name}=$ifc_name if ($ifc_name);
  385. $dev_cap->{$index}->{desc}=$ifc_desc if ($ifc_desc);
  386. $dev_cap->{$index}->{index} = $index;
  387. };
  388. return $dev_cap;
  389. }
  390. #-------------------------------------------------------------------------------------
  391. sub get_router_state {
  392. my ($host,$snmp,$skip_empty) = @_;
  393. my $session = init_snmp ($host,$snmp,0);
  394. return if (!defined($session) or !$session);
  395. $session->translate([-timeticks]);
  396. my $router_status = $session->get_table("1.3.6.1.4.1.10.1");
  397. $session->close();
  398. return ($router_status);
  399. }
  400. #-------------------------------------------------------------------------------------
  401. sub snmp_get_req {
  402. my ($host,$snmp,$oid) = @_;
  403. my $session = init_snmp ($host,$snmp,0);
  404. return if (!defined($session) or !$session);
  405. $session->translate([-timeticks]);
  406. my $result = $session->get_request(-varbindlist => [$oid]) or return;
  407. $session->close();
  408. return $result->{$oid};
  409. }
  410. #-------------------------------------------------------------------------------------
  411. sub snmp_get_oid {
  412. my ($host,$snmp,$oid) = @_;
  413. my $port = 161;
  414. my $session = init_snmp ($host,$snmp,0);
  415. return if (!defined($session) or !$session);
  416. $session->translate([-timeticks]);
  417. my $table = $session->get_table($oid);
  418. $session->close();
  419. return $table;
  420. }
  421. #-------------------------------------------------------------------------------------
  422. sub snmp_walk_oid {
  423. my ($host, $snmp, $oid, $opt) = @_;
  424. $opt ||= {};
  425. my $nonblocking = $opt->{nonblocking} // 1;
  426. log_debug("Starting SNMP walk on $host, OID: $oid, nonblocking=" . ($nonblocking ? 1 : 0)) if defined &log_debug;
  427. my $session = init_snmp($host, $snmp, 'ro', $nonblocking);
  428. unless ($session) {
  429. log_debug("Failed to initialize SNMP session for $host") if defined &log_debug;
  430. return;
  431. }
  432. my %table;
  433. if ($nonblocking) {
  434. # Async walk через callback
  435. log_debug("Sending first get_bulk_request for OID $oid") if defined &log_debug;
  436. my $result = $session->get_bulk_request(
  437. -varbindlist => [$oid],
  438. -callback => [\&table_callback, \%table, $oid, undef],
  439. -maxrepetitions => 10,
  440. );
  441. unless (defined $result) {
  442. log_debug("SNMP request error ($host): " . $session->error) if defined &log_debug;
  443. $session->close();
  444. return;
  445. }
  446. # Запускаем dispatcher для обработки async запросов
  447. eval {
  448. log_debug("Starting snmp_dispatcher for $host") if defined &log_debug;
  449. snmp_dispatcher();
  450. };
  451. if ($@) {
  452. log_debug("SNMP dispatcher exception ($host): $@") if defined &log_debug;
  453. $session->close();
  454. return;
  455. }
  456. }
  457. else {
  458. # Blocking walk через get_next
  459. log_debug("Starting blocking SNMP walk for OID $oid") if defined &log_debug;
  460. my $current_oid = $oid;
  461. while (1) {
  462. my $result = $session->get_next_request(-varbindlist => [$current_oid]);
  463. unless (defined $result) {
  464. log_debug("SNMP request error ($host): " . $session->error) if defined &log_debug;
  465. last;
  466. }
  467. my $list = $session->var_bind_list();
  468. last unless defined $list;
  469. my $stop = 0;
  470. for my $k (keys %$list) {
  471. unless (oid_base_match($oid, $k)) {
  472. log_debug("OID $k outside root $oid, stopping walk") if defined &log_debug;
  473. $stop = 1;
  474. last;
  475. }
  476. $table{$k} = $list->{$k};
  477. log_debug("Stored OID $k = $list->{$k}") if defined &log_debug;
  478. $current_oid = $k;
  479. }
  480. last if $stop;
  481. }
  482. }
  483. if ($session->error) {
  484. log_debug("SNMP runtime error ($host): " . $session->error) if defined &log_debug;
  485. }
  486. $session->close();
  487. log_debug("SNMP walk finished on $host, total OIDs collected: " . scalar keys %table) if defined &log_debug;
  488. return \%table;
  489. }
  490. #-------------------------------------------------------------------------------------
  491. # проверка что OID начинается с root
  492. sub oid_base_match {
  493. my ($base, $oid) = @_;
  494. return defined($oid) && defined($base) && index($oid, $base) == 0;
  495. }
  496. #-------------------------------------------------------------------------------------
  497. sub snmp_oid_compare {
  498. my ($oid1, $oid2) = @_;
  499. return 0 if !defined $oid1 && !defined $oid2;
  500. return 1 if !defined $oid2;
  501. return -1 if !defined $oid1;
  502. # Удаляем ведущую точку для единообразия
  503. $oid1 =~ s/^\.//;
  504. $oid2 =~ s/^\.//;
  505. my @a = split /\./, $oid1;
  506. my @b = split /\./, $oid2;
  507. my $len = @a < @b ? @a : @b;
  508. # Сравниваем покомпонентно как числа
  509. for (my $i = 0; $i < $len; $i++) {
  510. return -1 if $a[$i] < $b[$i];
  511. return 1 if $a[$i] > $b[$i];
  512. }
  513. # Если префиксы равны, сравниваем длину
  514. return @a <=> @b;
  515. }
  516. #-------------------------------------------------------------------------------------
  517. sub table_callback {
  518. my ($session, $table, $root_oid, $last_oid) = @_;
  519. my $list = $session->var_bind_list();
  520. unless (defined $list) {
  521. log_debug("SNMP error: " . $session->error) if defined &log_debug;
  522. log_debug("Exiting callback: undefined var_bind_list") if defined &log_debug;
  523. return;
  524. }
  525. my @names = sort { snmp_oid_compare($a, $b) } $session->var_bind_names();
  526. unless (@names) {
  527. log_debug("No OIDs returned in this callback") if defined &log_debug;
  528. return;
  529. }
  530. my $next;
  531. while (@names) {
  532. $next = shift @names;
  533. # Вышли за пределы таблицы
  534. unless (oid_base_match($root_oid, $next)) {
  535. log_debug("OID $next outside of root $root_oid. Exiting callback.") if defined &log_debug;
  536. return;
  537. }
  538. # Защита от OID not increasing
  539. if (defined $last_oid && snmp_oid_compare($next, $last_oid) <= 0) {
  540. log_debug("OID not increasing: $next <= $last_oid. Exiting callback.") if defined &log_debug;
  541. return;
  542. }
  543. my $value = $list->{$next};
  544. # endOfMibView
  545. unless (defined $value) {
  546. log_debug("endOfMibView reached at OID $next. Exiting callback.") if defined &log_debug;
  547. return;
  548. }
  549. $table->{$next} = $value;
  550. log_debug("Stored OID $next = $value") if defined &log_debug;
  551. $last_oid = $next;
  552. }
  553. # Запрос следующего блока
  554. my $result = $session->get_bulk_request(
  555. -varbindlist => [$next],
  556. -maxrepetitions => 10,
  557. -callback => [\&table_callback, $table, $root_oid, $last_oid],
  558. );
  559. unless (defined $result) {
  560. log_debug("SNMP get_bulk_request failed for OID $next: " . $session->error) if defined &log_debug;
  561. }
  562. else {
  563. log_debug("Scheduled next get_bulk_request starting from OID $next") if defined &log_debug;
  564. }
  565. }
  566. #-------------------------------------------------------------------------------------
  567. sub init_snmp {
  568. my ($host, $snmp, $rw, $nonblocking) = @_;
  569. return unless defined $host && $host ne '';
  570. $rw ||= 'ro';
  571. my $community = ($rw eq 'rw')
  572. ? $snmp->{'rw-community'}
  573. : $snmp->{'ro-community'};
  574. my %opts = (
  575. -hostname => $host,
  576. -port => $snmp->{port} // 161,
  577. -timeout => $snmp->{timeout} // 2,
  578. -translate => [-octetstring => 0],
  579. );
  580. $opts{-nonblocking} = 1 if $nonblocking;
  581. my ($session, $error);
  582. if (($snmp->{version} // 2) <= 2) {
  583. ($session, $error) = Net::SNMP->session(
  584. %opts,
  585. -community => $community,
  586. -version => $snmp->{version} // 2,
  587. );
  588. }
  589. else {
  590. ($session, $error) = Net::SNMP->session(
  591. %opts,
  592. -version => 'snmpv3',
  593. -username => $snmp->{$rw . '-user'},
  594. -authprotocol => $snmp->{'auth-proto'},
  595. -privprotocol => $snmp->{'priv-proto'},
  596. -authpassword => $snmp->{$rw . '-password'},
  597. -privpassword => $snmp->{$rw . '-password'},
  598. );
  599. }
  600. if (!defined $session) {
  601. log_debug("SNMP init failed for $host: $error") if defined &log_debug;
  602. return;
  603. }
  604. return $session;
  605. }
  606. #-------------------------------------------------------------------------------------
  607. sub setCommunity {
  608. my $device = shift;
  609. $device->{snmp}->{'port'} = 161;
  610. $device->{snmp}->{'timeout'} = $snmp_timeout;
  611. $device->{snmp}->{'version'} = $device->{snmp_version} || '2';
  612. $device->{snmp}->{'ro-community'} = $device->{community} || $snmp_default_community;
  613. $device->{snmp}->{'rw-community'} = $device->{rw_community} || $snmp_default_community;
  614. #snmpv3
  615. $device->{snmp}->{'auth-proto'} = $device->{snmp3_auth_proto} || 'sha512';
  616. $device->{snmp}->{'priv-proto'} = $device->{snmp3_priv_proto} || 'aes128';
  617. $device->{snmp}->{'ro-user'} = $device->{snmp3_user_ro} || '';
  618. $device->{snmp}->{'rw-user'} = $device->{snmp3_user_rw} || '';
  619. $device->{snmp}->{'ro-password'} = $device->{snmp3_user_ro_password} || $snmp_default_community;
  620. $device->{snmp}->{'rw-password'} = $device->{snmp3_user_rw_password} || $snmp_default_community;
  621. }
  622. #-------------------------------------------------------------------------------------
  623. 1;
  624. }