snmp.pm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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. get_bgp
  30. snmp_get_req
  31. snmp_get_oid
  32. snmp_walk_oid
  33. table_callback
  34. $ifAlias
  35. $ifName
  36. $ifDescr
  37. $ifIndex
  38. $ifIndex_map
  39. $bgp_prefixes
  40. $bgp_aslist
  41. $arp_oid
  42. $ipNetToMediaPhysAddress
  43. $fdb_table_oid
  44. $fdb_table_oid2
  45. $cisco_vlan_oid
  46. $port_vlan_oid
  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 $bgp_prefixes ='.1.3.6.1.4.1.9.9.187.1.2.4.1.1';
  58. our $bgp_aslist ='.1.3.6.1.2.1.15.3.1.9';
  59. our $arp_oid ='.1.3.6.1.2.1.3.1.1.2';
  60. our $ipNetToMediaPhysAddress = '.1.3.6.1.2.1.4.22.1.2';
  61. our $fdb_table_oid ='.1.3.6.1.2.1.17.4.3.1.2';
  62. our $fdb_table_oid2='.1.3.6.1.2.1.17.7.1.2.2.1.2';
  63. our $port_vlan_oid ='.1.3.6.1.2.1.17.7.1.4.5.1.1';
  64. our $cisco_vlan_oid='.1.3.6.1.4.1.9.9.46.1.3.1.1.2';
  65. our $fdb_table;
  66. our $snmp_timeout = 15;
  67. #---------------------------------------------------------------------------------
  68. sub snmp_get_request {
  69. my $ip = shift;
  70. my $oid = shift;
  71. my $community = shift || $snmp_default_community;
  72. my $port = shift || '161';
  73. my $snmp_version = shift || '2';
  74. my ($session, $error) = Net::SNMP->session(
  75. -hostname => $ip,
  76. -community => $community,
  77. -port => $port,
  78. -version => $snmp_version,
  79. -timeout => $snmp_timeout
  80. );
  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 $community = shift || $snmp_default_community;
  92. my $port = shift || '161';
  93. my $snmp_version = shift || '2';
  94. my ($session, $error) = Net::SNMP->session(
  95. -hostname => $ip,
  96. -community => $community,
  97. -port => $port,
  98. -version => $snmp_version,
  99. -timeout => $snmp_timeout
  100. );
  101. my $result = $session->set_request( -varbindlist => [$oid,INTEGER,$value]);
  102. $session->close;
  103. return $result->{$oid};
  104. }
  105. #-------------------------------------------------------------------------------------
  106. sub get_arp_table {
  107. my ($host,$community,$version) = @_;
  108. # return if (!HostIsLive($host));
  109. my $port = 161;
  110. my $timeout = 5;
  111. if (!$version) { $version='2'; }
  112. ### open SNMP session
  113. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community , -version=>$version, -timeout => $snmp_timeout);
  114. return if (!defined($snmp_session));
  115. $snmp_session->translate([-all]);
  116. my $arp;
  117. my $arp_table1 = $snmp_session->get_table($arp_oid);
  118. my $arp_table2 = $snmp_session->get_table($ipNetToMediaPhysAddress);
  119. if ($arp_table1) {
  120. foreach my $row (keys(%$arp_table1)) {
  121. my ($mac_h) = unpack("H*",$arp_table1->{$row});
  122. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  123. my $mac;
  124. if (length($mac_h)==12) { $mac=lc $mac_h; }
  125. next if (!$mac);
  126. $row=trim($row);
  127. my $ip;
  128. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  129. next if (!$ip);
  130. $arp->{$ip}=$mac;
  131. };
  132. }
  133. if ($arp_table2) {
  134. foreach my $row (keys(%$arp_table2)) {
  135. my ($mac_h) = unpack("H*",$arp_table2->{$row});
  136. next if (!$mac_h or $mac_h eq '000000000000' or $mac_h eq 'ffffffffffff');
  137. my $mac;
  138. if (length($mac_h)==12) { $mac=lc $mac_h; }
  139. next if (!$mac);
  140. $row=trim($row);
  141. my $ip;
  142. if ($row=~/\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/) { $ip=$1.".".$2.".".$3.".".$4; }
  143. next if (!$ip);
  144. $arp->{$ip}=$mac;
  145. };
  146. }
  147. return $arp;
  148. }
  149. #-------------------------------------------------------------------------------------
  150. sub get_mac_table {
  151. my ($host,$community,$oid,$version,$index_map) = @_;
  152. my $port = 161;
  153. my $timeout = 5;
  154. if (!$version) { $version='2'; }
  155. my $fdb;
  156. #need for callback
  157. $fdb_table=$oid;
  158. my $fdb_table1 = snmp_get_oid($host,$community,$oid,$version);
  159. if (!$fdb_table1) { $fdb_table1=snmp_walk_oid($host,$community,$oid,$version); }
  160. if ($fdb_table1) {
  161. foreach my $row (keys(%$fdb_table1)) {
  162. my $port_index = $fdb_table1->{$row};
  163. next if (!$port_index);
  164. my $mac;
  165. 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})$/) {
  166. $mac=sprintf "%02x%02x%02x%02x%02x%02x",$1,$2,$3,$4,$5,$6;
  167. }
  168. next if (!$mac);
  169. $port_index = $index_map->{$port_index};
  170. $fdb->{$mac}=$port_index;
  171. };
  172. return $fdb;
  173. }
  174. }
  175. #-------------------------------------------------------------------------------------
  176. sub get_ifmib_index_table {
  177. my $ip = shift;
  178. my $community = shift;
  179. my $version = shift;
  180. my $ifmib_map;
  181. my $index_table = snmp_get_oid($ip, $community, $ifIndex_map, $version);
  182. if (!%$index_table) { $index_table = snmp_walk_oid($ip, $community, $ifIndex_map, $version); }
  183. my $is_mikrotik = snmp_get_oid($ip, $community, '.1.3.6.1.2.1.9999.1.1.1.1.0', $version);
  184. if (%$index_table and $is_mikrotik) {
  185. if ($is_mikrotik) {
  186. foreach my $row (keys(%$index_table)) {
  187. my $port_index = $index_table->{$row};
  188. next if (!$port_index);
  189. my $value;
  190. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  191. next if (!$value);
  192. $ifmib_map->{$value}=$port_index;
  193. }
  194. }
  195. } else {
  196. # $index_table = snmp_get_oid($ip, $community, $ifIndex, $version);
  197. # if (!%$index_table) { $index_table = snmp_walk_oid($ip, $community, $ifIndex, $version); }
  198. if (%$index_table) {
  199. foreach my $row (keys(%$index_table)) {
  200. my $port_index = $index_table->{$row};
  201. next if (!$port_index);
  202. my $value;
  203. if ($row=~/\.([0-9]{1,10})$/) { $value = $1; }
  204. next if (!$value);
  205. $ifmib_map->{$value}=$value;
  206. };
  207. }
  208. }
  209. return $ifmib_map;
  210. }
  211. #-------------------------------------------------------------------------------------
  212. sub get_fdb_table {
  213. my ($host,$community,$version) = @_;
  214. # return if (!HostIsLive($host));
  215. my $port = 161;
  216. my $timeout = 5;
  217. if (!$version) { $version='2'; }
  218. my $ifindex_map = get_ifmib_index_table($host,$community,$version);
  219. my $fdb1=get_mac_table($host,$community,$fdb_table_oid,$version,$ifindex_map);
  220. my $fdb2=get_mac_table($host,$community,$fdb_table_oid2,$version,$ifindex_map);
  221. my $fdb;
  222. if ($fdb1) { $fdb = $fdb1; }
  223. if ($fdb2) {
  224. if (!$fdb) { $fdb = $fdb2; }
  225. else {
  226. foreach my $mac (keys %$fdb2) {
  227. if (!exists($fdb->{$mac})) { $fdb->{$mac}=$fdb2->{$mac}; }
  228. }
  229. }
  230. }
  231. #maybe cisco?!
  232. if (!$fdb) {
  233. my $vlan_table=snmp_get_oid($host,$community,$cisco_vlan_oid,$version);
  234. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$community,$cisco_vlan_oid,$version); }
  235. #fuck!
  236. if (!$vlan_table) { return; }
  237. my %fdb_vlan;
  238. foreach my $vlan_oid (keys %$vlan_table) {
  239. next if (!$vlan_oid);
  240. my $vlan_id;
  241. if ($vlan_oid=~/\.([0-9]{1,4})$/) { $vlan_id=$1; }
  242. next if (!$vlan_id);
  243. next if ($vlan_id>1000 and $vlan_id<=1009);
  244. $fdb_vlan{$vlan_id}=get_mac_table($host,$community.'@'.$vlan_id,$fdb_table_oid,$version,$ifindex_map);
  245. if (!$fdb_vlan{$vlan_id}) { $fdb_vlan{$vlan_id}=get_mac_table($host,$community.'@'.$vlan_id,$fdb_table_oid2,$version,$ifindex_map); }
  246. }
  247. foreach my $vlan_id (keys %fdb_vlan) {
  248. next if (!exists $fdb_vlan{$vlan_id});
  249. if (defined $fdb_vlan{$vlan_id}) {
  250. my %tmp=%{$fdb_vlan{$vlan_id}};
  251. foreach my $mac (keys %tmp) {
  252. next if (!$mac);
  253. $fdb->{$mac}=$tmp{$mac};
  254. }
  255. }
  256. }
  257. }
  258. return $fdb;
  259. }
  260. #-------------------------------------------------------------------------------------
  261. sub get_vlan_at_port {
  262. my ($host,$community,$version,$port_index) = @_;
  263. my $port = 161;
  264. my $timeout = 5;
  265. if (!$version) { $version='2'; }
  266. my $vlan_oid=$port_vlan_oid.".".$port_index;
  267. # print "$host,$community,$vlan_oid,$version\n";
  268. my $vlan = snmp_get_req($host,$community,$vlan_oid,$version);
  269. return "1" if (!$vlan);
  270. return "1" if ($vlan=~/noSuchObject/i);
  271. return "1" if ($vlan=~/noSuchInstance/i);
  272. return $vlan;
  273. }
  274. #-------------------------------------------------------------------------------------
  275. sub get_switch_vlans {
  276. my ($host,$community,$version) = @_;
  277. my $port = 161;
  278. my $timeout = 5;
  279. if (!$version) { $version='2'; }
  280. my $result;
  281. #need for callback
  282. my $vlan_table = snmp_get_oid($host,$community,$port_vlan_oid,$version);
  283. if (!$vlan_table) { $vlan_table=snmp_walk_oid($host,$community,$port_vlan_oid,$version); }
  284. if ($vlan_table) {
  285. foreach my $vlan_oid (keys %$vlan_table) {
  286. if ($vlan_oid=~/\.([0-9]*)$/) { $result->{$1} = $vlan_table->{$vlan_oid}; }
  287. }
  288. }
  289. return $result;
  290. }
  291. #-------------------------------------------------------------------------------------
  292. sub get_snmp_ifindex {
  293. my ($host,$community,$snmp) = @_;
  294. ### open SNMP session
  295. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community, -version => $snmp, -timeout => 5);
  296. return if (!defined($snmp_session));
  297. my $if_index = $snmp_session->get_table($ifIndex);
  298. my $result;
  299. foreach my $row (keys(%$if_index)) {
  300. my $value = $if_index->{$row};
  301. $row=~s/^$ifIndex\.//;
  302. $result->{$row}=$value;
  303. };
  304. return $result;
  305. }
  306. #-------------------------------------------------------------------------------------
  307. sub get_interfaces {
  308. my ($host,$community,$snmp,$skip_empty) = @_;
  309. # return if (!HostIsLive($host));
  310. my $port = 161;
  311. ### open SNMP session
  312. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community, -version => $snmp, -timeout => $snmp_timeout );
  313. return if (!defined($snmp_session));
  314. $snmp_session->translate([-timeticks]);
  315. my $if_name = $snmp_session->get_table($ifName);
  316. my $if_alias = $snmp_session->get_table($ifAlias);
  317. my $if_descr = $snmp_session->get_table($ifDescr);
  318. my $if_index = $snmp_session->get_table($ifIndex);
  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,$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 $router_status = $snmp_session->get_table("1.3.6.1.4.1.10.1");
  350. return ($router_status);
  351. }
  352. #-------------------------------------------------------------------------------------
  353. sub get_bgp {
  354. my ($host,$community,$snmp) = @_;
  355. return if (!HostIsLive($host));
  356. my $port = 161;
  357. ### open SNMP session
  358. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community, -version => $snmp, -timeout => $snmp_timeout );
  359. return if (!defined($snmp_session));
  360. $snmp_session->translate([-timeticks]);
  361. #bgp annonce counter exists?
  362. my $bgp_annonces;
  363. $bgp_annonces = $snmp_session->get_table($bgp_prefixes);
  364. return if (!$bgp_annonces);
  365. my $bgp_status = $snmp_session->get_table($bgp_aslist);
  366. return if (!$bgp_status);
  367. my $as;
  368. foreach my $row (keys(%$bgp_status)) {
  369. my $as_ip=$row;
  370. $as_ip=~s/1\.3\.6\.1\.2\.1\.15\.3\.1\.9\.//;
  371. $as->{$as_ip}=$bgp_status->{$row};
  372. }
  373. return $as;
  374. }
  375. #-------------------------------------------------------------------------------------
  376. sub snmp_get_req {
  377. my ($host,$community,$oid,$version) = @_;
  378. #return if (!HostIsLive($host));
  379. if (!$version) { $version='2'; }
  380. ### open SNMP session
  381. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community , -version=>$version, -timeout => $snmp_timeout );
  382. return if (!defined($snmp_session));
  383. $snmp_session->translate([-timeticks]);
  384. my $result = $snmp_session->get_request(-varbindlist => [$oid]) or return;
  385. $snmp_session->close();
  386. return $result->{$oid};
  387. }
  388. #-------------------------------------------------------------------------------------
  389. sub snmp_get_oid {
  390. my ($host,$community,$oid,$version) = @_;
  391. #return if (!HostIsLive($host));
  392. if (!$version) { $version='2'; }
  393. ### open SNMP session
  394. my ($snmp_session, $error) = Net::SNMP->session( -hostname => $host, -community => $community , -version=>$version , -timeout => $snmp_timeout, );
  395. return if (!defined($snmp_session));
  396. $snmp_session->translate([-timeticks]);
  397. my $table = $snmp_session->get_table($oid);
  398. $snmp_session->close();
  399. return $table;
  400. }
  401. #-------------------------------------------------------------------------------------
  402. sub snmp_walk_oid {
  403. my $host = shift;
  404. my $community = shift;
  405. my $oid = shift;
  406. my $version = shift || '2c';
  407. #return if (!HostIsLive($host));
  408. my ($session, $error) = Net::SNMP->session(
  409. -hostname => $host,
  410. -community => $community,
  411. -nonblocking => 1,
  412. -translate => [-octetstring => 0],
  413. -version => $version,
  414. -timeout => $snmp_timeout,
  415. );
  416. if (!defined $session) {
  417. printf "ERROR: %s.\n", $error;
  418. return;
  419. }
  420. my %table; # Hash to store the results
  421. my $result = $session->get_bulk_request(
  422. -varbindlist => [ $oid ],
  423. -callback => [ \&table_callback, \%table ],
  424. -maxrepetitions => 10,
  425. );
  426. snmp_dispatcher();
  427. $session->close();
  428. return \%table;
  429. }
  430. #-------------------------------------------------------------------------------------
  431. sub table_callback {
  432. my ($session, $table) = @_;
  433. my $list = $session->var_bind_list();
  434. if (!defined $list) {
  435. printf "ERROR: %s\n", $session->error();
  436. return;
  437. }
  438. my @names = $session->var_bind_names();
  439. my $next = undef;
  440. while (@names) {
  441. $next = shift @names;
  442. if (!oid_base_match($fdb_table, $next)) { return; }
  443. $table->{$next} = $list->{$next};
  444. }
  445. my $result = $session->get_bulk_request( -varbindlist => [ $next ], -maxrepetitions => 10);
  446. if (!defined $result) {
  447. printf "ERROR: %s.\n", $session->error();
  448. }
  449. return;
  450. }
  451. #-------------------------------------------------------------------------------------
  452. 1;
  453. }