snmp.pm 15 KB

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