snmp.pm 17 KB

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