snmp.pm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. package eyelib::snmp;
  2. #
  3. # Copyright (C) Roman Dmitiriev, 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 Net::SNMP;
  17. @ISA = qw(Exporter);
  18. @EXPORT = qw(
  19. snmp_set_int
  20. snmp_get_request
  21. get_arp_table
  22. get_fdb_table
  23. get_mac_table
  24. get_vlan_at_port
  25. get_switch_vlans
  26. get_snmp_ifindex
  27. get_ifmib_index_table
  28. getIpAdEntIfIndex
  29. get_interfaces
  30. get_router_state
  31. snmp_get_req
  32. snmp_get_oid
  33. snmp_walk_oid
  34. table_callback
  35. $ifAlias
  36. $ifName
  37. $ifDescr
  38. $ifIndex
  39. $ifIndex_map
  40. $arp_oid
  41. $ipNetToMediaPhysAddress
  42. $ipAdEntIfIndex
  43. $fdb_table_oid
  44. $fdb_table_oid2
  45. $cisco_vlan_oid
  46. $dot1qPortVlanEntry
  47. $fdb_table;
  48. $snmp_timeout
  49. setCommunity
  50. init_snmp
  51. );
  52. BEGIN
  53. {
  54. our $ifAlias ='.1.3.6.1.2.1.31.1.1.1.18';
  55. our $ifName ='.1.3.6.1.2.1.31.1.1.1.1';
  56. our $ifDescr ='.1.3.6.1.2.1.2.2.1.2';
  57. our $ifIndex ='.1.3.6.1.2.1.2.2.1.1';
  58. our $ifIndex_map ='.1.3.6.1.2.1.17.1.4.1.2';
  59. our $ipAdEntIfIndex ='.1.3.6.1.2.1.4.20.1.2';
  60. #RFC1213::atPhysAddress
  61. our $arp_oid ='.1.3.6.1.2.1.3.1.1.2';
  62. #RFC1213::ipNetToMediaPhysAddress
  63. our $ipNetToMediaPhysAddress = '.1.3.6.1.2.1.4.22.1.2';
  64. #RFC1493::dot1dTpFdbTable
  65. our $fdb_table_oid ='.1.3.6.1.2.1.17.4.3.1.2';
  66. #Q-BRIDGE-MIB::dot1qTpFdbPort
  67. our $fdb_table_oid2='.1.3.6.1.2.1.17.7.1.2.2.1.2';
  68. #Q-BRIDGE-MIB::dot1qPortVlanEntry
  69. our $dot1qPortVlanEntry ='.1.3.6.1.2.1.17.7.1.4.5.1.1';
  70. #CISCO-ES-STACK-MIB::
  71. our $cisco_vlan_oid='.1.3.6.1.4.1.9.9.46.1.3.1.1.2';
  72. our $fdb_table;
  73. our $snmp_timeout = 15;
  74. #---------------------------------------------------------------------------------
  75. sub snmp_get_request {
  76. my $ip = shift;
  77. my $oid = shift;
  78. my $snmp = shift;
  79. my $session = init_snmp ($ip,$snmp);
  80. return if (!defined($session) or !$session);
  81. my $result = $session->get_request( -varbindlist => [$oid]);
  82. $session->close;
  83. return if (!$result->{$oid});
  84. return $result->{$oid};
  85. }
  86. #---------------------------------------------------------------------------------
  87. sub snmp_set_int {
  88. my $ip = shift;
  89. my $oid = shift;
  90. my $value = shift;
  91. my $snmp = shift;
  92. my $session = init_snmp ($ip,$snmp,1);
  93. return if (!defined($session) or !$session);
  94. my $result = $session->set_request( -varbindlist => [$oid,INTEGER,$value]);
  95. $session->close;
  96. return $result->{$oid};
  97. }
  98. #-------------------------------------------------------------------------------------
  99. sub get_arp_table {
  100. my ($host,$snmp) = @_;
  101. my $session = init_snmp ($host,$snmp,0);
  102. return if (!defined($session) or !$session);
  103. $session->translate([-all]);
  104. my $arp;
  105. my $arp_table1 = $session->get_table($arp_oid);
  106. my $arp_table2 = $session->get_table($ipNetToMediaPhysAddress);
  107. $session->close();
  108. if ($arp_table1) {
  109. foreach my $row (keys(%$arp_table1)) {
  110. my ($mac_h) = unpack("H*",$arp_table1->{$row});
  111. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  112. my $mac;
  113. if (length($mac_h)==12) { $mac=lc $mac_h; }
  114. next if (!$mac);
  115. $row=trim($row);
  116. my $ip;
  117. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  118. next if (!$ip);
  119. $arp->{$ip}=$mac;
  120. };
  121. }
  122. if ($arp_table2) {
  123. foreach my $row (keys(%$arp_table2)) {
  124. my ($mac_h) = unpack("H*",$arp_table2->{$row});
  125. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  126. my $mac;
  127. if (length($mac_h)==12) { $mac=lc $mac_h; }
  128. next if (!$mac);
  129. $row=trim($row);
  130. my $ip;
  131. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  132. next if (!$ip);
  133. $arp->{$ip}=$mac;
  134. };
  135. }
  136. return $arp;
  137. }
  138. #-------------------------------------------------------------------------------------
  139. sub get_ifmib_index_table {
  140. my $ip = shift;
  141. my $snmp = shift;
  142. my $ifmib_map;
  143. my $is_mikrotik = snmp_get_request($ip, '.1.3.6.1.2.1.9999.1.1.1.1.0', $snmp);
  144. my $mk_ros_version = 0;
  145. if ($is_mikrotik=~/MikroTik/i) {
  146. my $mikrotik_version = snmp_get_request($ip, '.1.0.8802.1.1.2.1.3.4.0', $snmp);
  147. $mk_ros_version = 6491;
  148. #"MikroTik RouterOS 6.46.8 (long-term) CRS326-24S+2Q+"
  149. if ($mikrotik_version =~/RouterOS\s+(\d)\.(\d{1,3})\.(\d{1,3})\s+/) {
  150. $mk_ros_version = $1*1000 + $2*10 + $3;
  151. }
  152. }
  153. if (!$mk_ros_version or $mk_ros_version > 6468) {
  154. my $index_map_table = snmp_get_oid($ip, $snmp, $ifIndex_map);
  155. if (!$index_map_table) { $index_map_table = snmp_walk_oid($ip, $snmp, $ifIndex_map); }
  156. if ($index_map_table) {
  157. foreach my $row (keys(%$index_map_table)) {
  158. my $port_index = $index_map_table->{$row};
  159. next if (!$port_index);
  160. my $value;
  161. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  162. next if (!$value);
  163. $ifmib_map->{$value}=$port_index;
  164. }
  165. }
  166. }
  167. if (!$ifmib_map) {
  168. my $index_table = snmp_get_oid($ip, $snmp, $ifIndex);
  169. if (!$index_table) { $index_table = snmp_walk_oid($ip, $snmp, $ifIndex); }
  170. foreach my $row (keys(%$index_table)) {
  171. my $port_index = $index_table->{$row};
  172. next if (!$port_index);
  173. my $value;
  174. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  175. next if (!$value);
  176. $ifmib_map->{$value}=$value;
  177. };
  178. }
  179. return $ifmib_map;
  180. }
  181. #-------------------------------------------------------------------------------------
  182. sub get_mac_table {
  183. my ($host,$snmp,$oid,$index_map) = @_;
  184. my $fdb;
  185. #need for callback
  186. $fdb_table=$oid;
  187. my $fdb_table1 = snmp_get_oid($host,$snmp,$oid);
  188. if (!$fdb_table1) { $fdb_table1=snmp_walk_oid($host,$snmp,$oid,undef); }
  189. if ($fdb_table1) {
  190. foreach my $row (keys(%$fdb_table1)) {
  191. my $port_index = $fdb_table1->{$row};
  192. next if (!$port_index);
  193. my $mac;
  194. 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})$/) {
  195. $mac=sprintf "%02x%02x%02x%02x%02x%02x",$1,$2,$3,$4,$5,$6;
  196. }
  197. next if (!$mac);
  198. if ($index_map and exists $index_map->{$port_index}) { $port_index = $index_map->{$port_index}; }
  199. $fdb->{$mac}=$port_index;
  200. };
  201. return $fdb;
  202. }
  203. }
  204. #-------------------------------------------------------------------------------------
  205. sub get_fdb_table {
  206. my ($host,$snmp,$iflist) = @_;
  207. my $ifindex_map = get_ifmib_index_table($host,$snmp);
  208. my $fdb1=get_mac_table($host,$snmp,$fdb_table_oid,$ifindex_map);
  209. my $fdb2=get_mac_table($host,$snmp,$fdb_table_oid2,$ifindex_map);
  210. my $fdb3;
  211. if ($fdb2 and $iflist) {
  212. foreach my $mac (keys %$fdb2) {
  213. if (exists $iflist->{$fdb2->{$mac}}) { $fdb3->{$mac}=$iflist->{$fdb2->{$mac}}; }
  214. }
  215. }
  216. my $fdb;
  217. if ($fdb1 and !$fdb3) { $fdb = $fdb1; }
  218. if (!$fdb1 and $fdb3) { $fdb = $fdb3; }
  219. #join tables
  220. if ($fdb1 and $fdb3) { $fdb = { %$fdb1,%$fdb3 }; }
  221. my $snmp_cisco = $snmp;
  222. #maybe cisco?!
  223. if (!$fdb) {
  224. my $vlan_table=snmp_get_oid($host,$snmp,$cisco_vlan_oid);
  225. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$cisco_vlan_oid); }
  226. #fuck!
  227. if (!$vlan_table) { return; }
  228. my %fdb_vlan;
  229. foreach my $vlan_oid (keys %$vlan_table) {
  230. next if (!$vlan_oid);
  231. my $vlan_id;
  232. if ($vlan_oid=~/\.([0-9]{1,4})$/) { $vlan_id=$1; }
  233. next if (!$vlan_id);
  234. next if ($vlan_id>1000 and $vlan_id<=1009);
  235. $snmp_cisco->{'ro-community'} = $snmp->{'ro-community'}.'@'.$vlan_id;
  236. $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp_cisco,$fdb_table_oid,$ifindex_map);
  237. if (!$fdb_vlan{$vlan_id}) { $fdb_vlan{$vlan_id}=get_mac_table($host,$snmp_cisco,$fdb_table_oid2,$ifindex_map); }
  238. }
  239. foreach my $vlan_id (keys %fdb_vlan) {
  240. next if (!exists $fdb_vlan{$vlan_id});
  241. if (defined $fdb_vlan{$vlan_id}) {
  242. my %tmp=%{$fdb_vlan{$vlan_id}};
  243. foreach my $mac (keys %tmp) {
  244. next if (!$mac);
  245. $fdb->{$mac}=$tmp{$mac};
  246. }
  247. }
  248. }
  249. }
  250. return $fdb;
  251. }
  252. #-------------------------------------------------------------------------------------
  253. sub get_vlan_at_port {
  254. my ($host,$snmp,$port_index) = @_;
  255. my $vlan_oid=$dot1qPortVlanEntry.".".$port_index;
  256. my $vlan = snmp_get_req($host,$snmp,$vlan_oid);
  257. return "1" if (!$vlan);
  258. return "1" if ($vlan=~/noSuchObject/i);
  259. return "1" if ($vlan=~/noSuchInstance/i);
  260. return $vlan;
  261. }
  262. #-------------------------------------------------------------------------------------
  263. sub get_switch_vlans {
  264. my ($host,$snmp) = @_;
  265. my $result;
  266. #need for callback
  267. my $vlan_table = snmp_get_oid($host,$snmp,$dot1qPortVlanEntry);
  268. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$snmp,$dot1qPortVlanEntry); }
  269. if ($vlan_table) {
  270. foreach my $vlan_oid (keys %$vlan_table) {
  271. if ($vlan_oid=~/\.([0-9]*)$/) { $result->{$1} = $vlan_table->{$vlan_oid}; }
  272. }
  273. }
  274. return $result;
  275. }
  276. #-------------------------------------------------------------------------------------
  277. sub get_snmp_ifindex {
  278. my ($host,$snmp) = @_;
  279. my $session = init_snmp($host,$snmp,0);
  280. return if (!defined($session) or !$session);
  281. my $if_index = $session->get_table($ifIndex);
  282. my $result;
  283. foreach my $row (keys(%$if_index)) {
  284. my $value = $if_index->{$row};
  285. $row=~s/^$ifIndex\.//;
  286. $result->{$row}=$value;
  287. };
  288. $session->close();
  289. return $result;
  290. }
  291. #-------------------------------------------------------------------------------------
  292. #get ip interfaces
  293. sub getIpAdEntIfIndex {
  294. my ($host,$snmp) = @_;
  295. my $session = init_snmp ($host,$snmp,0);
  296. return if (!defined($session) or !$session);
  297. $session->translate([-timeticks]);
  298. my $if_ipaddr = $session->get_table($ipAdEntIfIndex);
  299. my $l3_list;
  300. foreach my $row (keys(%$if_ipaddr)) {
  301. my $ipaddr = $row;
  302. $ipaddr=~s/$ipAdEntIfIndex\.//;
  303. $l3_list->{$ipaddr}=$if_ipaddr->{$row};
  304. }
  305. $session->close();
  306. return $l3_list;
  307. }
  308. #-------------------------------------------------------------------------------------
  309. sub get_interfaces {
  310. my ($host,$snmp,$skip_empty) = @_;
  311. my $session = init_snmp ($host,$snmp,0);
  312. return if (!defined($session) or !$session);
  313. $session->translate([-timeticks]);
  314. my $if_name = $session->get_table($ifName);
  315. my $if_alias = $session->get_table($ifAlias);
  316. my $if_descr = $session->get_table($ifDescr);
  317. my $if_index = $session->get_table($ifIndex);
  318. $session->close();
  319. my $dev_cap;
  320. foreach my $row (keys(%$if_index)) {
  321. my $index = $if_index->{$row};
  322. next if ($if_name->{$ifName.".".$index} =~/^lo/i);
  323. next if ($if_name->{$ifName.".".$index} =~/^dummy/i);
  324. next if ($if_name->{$ifName.".".$index} =~/^enet/i);
  325. next if ($if_name->{$ifName.".".$index} =~/^Nu/i);
  326. # next if ($if_name->{$ifName.".".$index} =~/^Po/i);
  327. my $ifc_alias;
  328. $ifc_alias=$if_alias->{$ifAlias.".".$index} if ($if_alias->{$ifAlias.".".$index});
  329. my $ifc_name;
  330. $ifc_name=$if_name->{$ifName.".".$index} if ($if_name->{$ifName.".".$index});
  331. my $ifc_desc;
  332. $ifc_desc=$if_descr->{$ifDescr.".".$index} if ($if_descr->{$ifDescr.".".$index});
  333. $dev_cap->{$index}->{alias}=$ifc_alias if ($ifc_alias);
  334. $dev_cap->{$index}->{name}=$ifc_name if ($ifc_name);
  335. $dev_cap->{$index}->{desc}=$ifc_desc if ($ifc_desc);
  336. $dev_cap->{$index}->{index} = $index;
  337. };
  338. return $dev_cap;
  339. }
  340. #-------------------------------------------------------------------------------------
  341. sub get_router_state {
  342. my ($host,$snmp,$skip_empty) = @_;
  343. my $session = init_snmp ($host,$snmp,0);
  344. return if (!defined($session) or !$session);
  345. $session->translate([-timeticks]);
  346. my $router_status = $session->get_table("1.3.6.1.4.1.10.1");
  347. $session->close();
  348. return ($router_status);
  349. }
  350. #-------------------------------------------------------------------------------------
  351. sub snmp_get_req {
  352. my ($host,$snmp,$oid) = @_;
  353. my $session = init_snmp ($host,$snmp,0);
  354. return if (!defined($session) or !$session);
  355. $session->translate([-timeticks]);
  356. my $result = $session->get_request(-varbindlist => [$oid]) or return;
  357. $session->close();
  358. return $result->{$oid};
  359. }
  360. #-------------------------------------------------------------------------------------
  361. sub snmp_get_oid {
  362. my ($host,$snmp,$oid) = @_;
  363. my $port = 161;
  364. my $session = init_snmp ($host,$snmp,0);
  365. return if (!defined($session) or !$session);
  366. $session->translate([-timeticks]);
  367. my $table = $session->get_table($oid);
  368. $session->close();
  369. return $table;
  370. }
  371. #-------------------------------------------------------------------------------------
  372. sub snmp_walk_oid {
  373. my $host = shift;
  374. my $snmp = shift;
  375. my $oid = shift;
  376. my $rw = 'ro';
  377. ### open SNMP session
  378. my ($session, $error);
  379. if ($snmp->{version} <= 2) {
  380. ($session, $error) = Net::SNMP->session(
  381. -hostname => $host,
  382. -community => $snmp->{'ro-community'} ,
  383. -version => $snmp->{version},
  384. -port => $snmp->{port},
  385. -timeout => $snmp->{timeout},
  386. -nonblocking => 1,
  387. -translate => [-octetstring => 0],
  388. );
  389. } else {
  390. ($session, $error) = Net::SNMP->session(
  391. -hostname => $host,
  392. -version => 'snmpv3',
  393. -username => $snmp->{$rw.'-user'},
  394. -authprotocol => $snmp->{'auth-proto'},
  395. -privprotocol => $snmp->{'priv-proto'},
  396. -authpassword => $snmp->{$rw.'-password'},
  397. -privpassword => $snmp->{$rw.'-password'},
  398. -port => $snmp->{port},
  399. -timeout => $snmp->{timeout},
  400. -nonblocking => 1,
  401. -translate => [-octetstring => 0],
  402. );
  403. }
  404. return if (!defined($session) or !$session);
  405. my %table; # Hash to store the results
  406. my $result = $session->get_bulk_request(
  407. -varbindlist => [ $oid ],
  408. -callback => [ \&table_callback, \%table ],
  409. -maxrepetitions => 10,
  410. );
  411. snmp_dispatcher();
  412. $session->close();
  413. return \%table;
  414. }
  415. #-------------------------------------------------------------------------------------
  416. sub table_callback {
  417. my ($session, $table) = @_;
  418. my $list = $session->var_bind_list();
  419. if (!defined $list) {
  420. printf "ERROR: %s\n", $session->error();
  421. return;
  422. }
  423. my @names = $session->var_bind_names();
  424. my $next = undef;
  425. while (@names) {
  426. $next = shift @names;
  427. if (!oid_base_match($fdb_table, $next)) { return; }
  428. $table->{$next} = $list->{$next};
  429. }
  430. my $result = $session->get_bulk_request( -varbindlist => [ $next ], -maxrepetitions => 10);
  431. if (!defined $result) {
  432. printf "ERROR: %s.\n", $session->error();
  433. }
  434. return;
  435. }
  436. #-------------------------------------------------------------------------------------
  437. sub init_snmp {
  438. my ($host,$snmp,$rw) = @_;
  439. return if (!$host);
  440. my $community = $snmp->{'ro-community'};
  441. if (!$rw) { $rw = 'ro' }
  442. else {
  443. $rw = 'rw';
  444. $community = $snmp->{'rw-community'};
  445. }
  446. ### open SNMP session
  447. my ($session, $error);
  448. if ($snmp->{version} <=2) {
  449. ($session, $error) = Net::SNMP->session(
  450. -hostname => $host,
  451. -community => $community ,
  452. -version => $snmp->{'version'},
  453. -port => $snmp->{port},
  454. -timeout => $snmp->{timeout},
  455. );
  456. } else {
  457. ($session, $error) = Net::SNMP->session(
  458. -hostname => $host,
  459. -version => 'snmpv3',
  460. -username => $snmp->{$rw.'-user'},
  461. -authprotocol => $snmp->{'auth-proto'},
  462. -privprotocol => $snmp->{'priv-proto'},
  463. -authpassword => $snmp->{$rw.'-password'},
  464. -privpassword => $snmp->{$rw.'-password'},
  465. -port => $snmp->{port},
  466. -timeout => $snmp->{timeout},
  467. );
  468. }
  469. if ($error) {
  470. log_debug("SNMP init-request status for $host:");
  471. log_debug(Dumper($error));
  472. }
  473. return if (!defined($session) or !$session);
  474. return $session;
  475. }
  476. #-------------------------------------------------------------------------------------
  477. sub setCommunity {
  478. my $device = shift;
  479. $device->{snmp}->{'port'} = 161;
  480. $device->{snmp}->{'timeout'} = $snmp_timeout;
  481. $device->{snmp}->{'version'} = $device->{snmp_version} || '2';
  482. $device->{snmp}->{'ro-community'} = $device->{community} || $snmp_default_community;
  483. $device->{snmp}->{'rw-community'} = $device->{rw_community} || $snmp_default_community;
  484. #snmpv3
  485. $device->{snmp}->{'auth-proto'} = $device->{snmp3_auth_proto} || 'sha512';
  486. $device->{snmp}->{'priv-proto'} = $device->{snmp3_priv_proto} || 'aes128';
  487. $device->{snmp}->{'ro-user'} = $device->{snmp3_user_ro} || '';
  488. $device->{snmp}->{'rw-user'} = $device->{snmp3_user_rw} || '';
  489. $device->{snmp}->{'ro-password'} = $device->{snmp3_user_ro_password} || $snmp_default_community;
  490. $device->{snmp}->{'rw-password'} = $device->{snmp3_user_rw_password} || $snmp_default_community;
  491. }
  492. #-------------------------------------------------------------------------------------
  493. 1;
  494. }