Gerenciando redes com Perl e SNMP

Muitas empresas utilizam softwares de código aberto nas mais diversas atividades. Em minha carreira deparei-me com sistemas de gerência de rede que não atendiam as necessidades do cliente. Minha intenção aqui é demonstrar de forma prática como o SNMP funciona, como entender as MIBs e principalmente, como usar o Perl para melhorar os sistemas de gerência, ou mesmo, criar o seu próprio.

[ Hits: 50.438 ]

Por: Perfil removido em 23/03/2010


Criando uma solução SNMP personalizada com o Perl



Você pode decidir criar um gerenciador SNMP para tender uma necessidade em particular. No meu caso, softwares gerenciadores de fabricantes específicos preferem usar os objetos dentro de iso.org.dod.internet.private e como já dissemos este espaço é reservado para características particulares dos fabricantes. Logo um gerenciador da CISCO não funciona perfeitamente com elementos da Siemens e vice-versa. Softwares que ofereçam gerência para elementos de diversos fabricantes podem ser muito caros - pelo menos para meus clientes - e ainda necessitariam da criação de alguns módulos para atender todas as necessidades de uma grande empresa.

Eu decidi por utilizar o Perl nestas tarefas pelos seguintes motivos:
  • É realmente uma linguagem multiplataforma. Inclusive os módulos para criar programas de rede funcionam tanto em Linux, Solaris ou Windows. Nós amamos o Linux. Nossos clientes, nem sempre;
  • Fácil de usar e atingiu seu desafio de manter as coisas simples, simples e as coisas difíceis, possíveis;
  • O CPAN - repositório do Perl em www.cpan.org - torna as coisas difíceis, simples. São milhares de módulos gratuitos e com o código aberto;
  • Gratuito e open source.

O Perl já vem instalado em máquinas GNU/Linux padrão. Porém para criar uma solução SNMP precisamos instalar o Net::SNMP. Você o encontra no repositório CPAN em http://search.cpan.org/~dtown/Net-SNMP-v6.0.0/lib/Net/SNMP.pm. No CPAN existem diversos módulos para gerenciamento via SNMP. Eu gosto particularmente deste pois é um módulo sem dependências externas. Não temos que instalar nada na máquina que irá executar o programa. Se ela for capaz de rodar o Perl, ela poderá usar o Net::SNMP e a nossa aplicação.

Depois de tanto blá-blá-blá estamos ansiosos para começar a codificar. Uma vez que você baixou e instalou o módulo Net::SNMP você está pronto para começar.

Como programadores organizados e por gostarmos de evitar retrabalho, vamos começar com um módulo genérico que obtém as informações de interface do elemento.

1  package SNMPRouter;
2  use strict;
3  use warnings;
4  use Net::SNMP;
5  use Carp qw/croak/;

Agora que definimos um nome para nosso pacote e adicionamos as diretivas comuns vamos codificar o construtor do objeto:

6  sub new{
7
8    
9     my ($class, $ip, $community) = @_;
10   
11    my $self = {
12          IP=>$ip,                     # Armazena o IP/nome do elemento
13          Session => undef,            # Armazena o Objeto SNMP gerado por connect
14          Community => $community,     # Armazena a community de segurança
15          SysName => undef,            #
16          SysDescription => undef,     #
17          NumberOfInterfaces => undef, # Armazena o número de Interfaces
18          InterfaceIndexNames => {},   # Relaciona o número da interface com o nome
19          InterfaceIPAddress => {},    # Relaciona o número da interface com o IP
20          InterfaceAlias => {},        # Relaciona o número da interface com o Alias
21          InterfaceAdminStatus => {},  #
22          InterfaceOperStatus => {},   #
23         
24    };
25   
26    bless $self, $class
27  }

O nosso objeto será basicamente um hash que ira armazenar as informações estratégicas das interfaces de um elemento de rede. A chave Session do hashref $self irá armazenar um objeto Net::SNMP.

28  sub connect{
29    my $self = shift;
30    $self->{Session} = Net::SNMP->session(Hostname => $self->{IP},
31       Community => $self->{Community},
32       ) or croak "Unable to create SNMP Session\n";    
33  }

No método connect, nós criamos um objeto SNMP e criamos a sessão com o elemento definido em IP, que foi passado como argumento ao construtor juntamente com a community. Com isto podemos solicitar ou escrever as primeiras informações do elemento via SNMP:

34  sub query_system_info{
35    my $self = shift;
36    my $mib2System = '1.3.6.1.2.1.1';
37   
38    my %mibSystemTable = (
39       Mib2SysName => $mib2System . '.5',
40       Mib2SysDescription => $mib2System . '.1',
41       );
42                          
43    my @queryList = values %mibSystemTable;                        
44   
45    my $result = $self->{Session}->get_next_request(Varbindlist => \@queryList);
46    croak "Erro ao tentar receber informações da MIB System\n" unless
47       defined $result;
48         
49    foreach my $key(keys %$result){
50      $self->{SysName} = $result->{$key} if
51                $key =~ /^$mibSystemTable{Mib2SysName}/;
52        $self->{SysDescription} = $result->{$key} if
53              $key =~ /^$mibSystemTable{Mib2SysDescription}/;
54    }    
55  }

Observe que o método query_info_system obtém as informações dos objetos ISO.org.dod.internet.mgmt.mib-2.system.name e system.description. Pode parecer um código um pouco longo e pouco prático para se obter apenas duas informações mas ele é bastante flexível. Se você quiser obter outros dados dentro do objeto system, como uptime ou services, basta adicionar mais chaves no hash %mibSystemTable e expressões regulares dentro do loop foreach para associar com a propriedade do objeto.

Agora vamos montar a tabela de interfaces. Como elementos de rede podem ter diferentes números de interfaces de diferentes tipos, nosso método primará pela flexibilidade do programa. Usamos as informações dos objetos mib-2.interfaces.ifNumber que informa o número de interfaces de rede do elemento - tanto físicas quanto lógicas - e mib-2.ifMIB. Usamos estes dados para montar uma tabela hash que relaciona o index da interface com o nome da mesma. Esta tabela ficará armazenada na propriedade InterfaceIndexName do objeto SNMPRouter.

Os nomes de interface são, por exemplo, em um servidor Linux, eth0 e lo, gi5/3 ou se0/1 num roteador CISCO e fe.1.2 ou ge.1.24 num switch Enterasys. Esta é a beleza do SNMP com o Perl. Não importam as diferenças entre os fabricantes, a raça a cor ou o sexo. Podemo obter as informações e formatá-las ao nosso gosto.

56 sub create_int_table{
57    my $self = shift;
58    my $mib2ifNumber = '1.3.6.1.2.1.2.1';
59    my $mib2ifIndex = '1.3.6.1.2.1.2.2.1.1';
60    my $mib2ifXMIB = '1.3.6.1.2.1.31.1.1.1';
61    my $mib2ifName = $mib2ifXMIB . '.1';
62   
63    my $ifNumber = $self->{Session}->get_next_request(
64           Varbindlist => [$mib2ifNumber]);
65   
66    $ifNumber = $ifNumber->{"$mib2ifNumber.0"};
67   
68    for(my $i = 1;$i <= $ifNumber; $i++){
69       my $ifIndex = $self->{Session}->get_request(
70           Varbindlist => ["$mib2ifIndex.$i"]);
71
72       if($ifIndex){
73             my $ifName = $self->{Session}->get_request(
74                  Varbindlist => ["$mib2ifName.$i"]);
75
76         $ifName = $ifName->{"$mib2ifName.$i"};        
77           $self->{InterfaceIndexNames}->{$i} = $ifName;
78       }else{$ifNumber++}
79    }
80  }

Muito bem, já conseguimos os nomes das interfaces, o nome e a descrição do elemento. Agora vamos obter os estados e a descrição da interfaces. Afinal, é importante saber quais são as interfaces dos elementos, mas também é muito importante saber se elas estão ativas e respondendo. Os hash %statusTable contém os valores retornados pelos objetos mib-2.interfaces.ifTable.ifEntry.ifAdminStatus e ifOperStatus relacionados com seu significado. Se você observar um arquivo de definição das mibs verá que a sintaxe dela é INTEGER. Este valor deve ser convertido para um texto com significado.

81 sub query_int_status{
82    my $self = shift;
83   
84    croak "Tabela de Interfaces não foi criada para este Objeto"
85       unless $self->{InterfaceIndexNames};
86   
87    my $mib2IfAdminStatus = '1.3.6.1.2.1.2.2.1.7';
88    my $mib2IfOperStatus = '1.3.6.1.2.1.2.2.1.8';
89    my $mib2ifAlias = '1.3.6.1.2.1.31.1.1.1.18';
90    my %statusTable = (
91                       1=>'up',
92                       2=>'down',
93                       3=>'testing',
94                       4=>'unknow',
95                       5=>'dormant',
96                       6=>'notPresent',
97                       8=>'lowerLayerDown',
98                       );
99                      
100   foreach my $key (keys %{$self->{InterfaceIndexNames}}){
101         my $status = $self->{Session}->get_request(
102         Varbindlist => ["$mib2IfOperStatus.$key",
103                         "$mib2IfAdminStatus.$key",
104                         "$mib2ifAlias.$key"
105                         ]);
106         $self->{InterfaceOperStatus}->{$key} =
107            $statusTable{$status->{"$mib2IfOperStatus.$key"}}; 
108         $self->{InterfaceAdminStatus}->{$key} =
109         $statusTable{$status->{"$mib2IfAdminStatus.$key"}};
110         $self->{InterfaceAlias}->{$key} = $status->{"$mib2ifAlias.$key"};    
111    }
112 }

Falta apenas o método que obtenha os endereços IPs das interfaces de rede. Este é um pouco mais confuso, pois ao contrário dos outros objetos da MIB, mib-2.ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex não relaciona o número da interface com o IP e sim o contrário. O IP é a referência do OID.

113  sub query_int_ip{
114   my $self = shift;
115   my $mib2IpAddrTable = '1.3.6.1.2.1.4.20';
116   my $mib2IpAddrxIntIndex = '1.3.6.1.2.1.4.20.1.2';
117   my $hshIpTable = $self->{Session}->get_table(Baseoid=>$mib2IpAddrTable)
118      or croak "Erro ao tentar acessar a mib IpAddrTable";
119     
120      foreach my $key (keys %{$hshIpTable}){
121         if ($key =~ $mib2IpAddrxIntIndex){
122            $key =~ s/$mib2IpAddrxIntIndex\.//;
123            my $intIndex = $hshIpTable->{$mib2IpAddrxIntIndex . "." . $key};
124            $self->{InterfaceIPAddress}->{$intIndex} = $key;
125
126         }
127      }
128  }

E, para terminar o módulo, não se esqueça de finalizar com 1.

129 1

Agora que você sente que teve um trabalhão para criar este módulo, vamos fazer um pequeno script de teste:

1  use strict;
2  use warnings;
3  use lib qw/./;
4
5  use SNMPRouter;
6
7  print "Digite o HOSTNAME ou IP do ROTEADOR: ";
8  chomp(my $host = <STDIN>);
9  my $community = 'public'; #Altere este valor para a sua community ou solicite ao
10                              #usuário para informar.
11 my $router = SNMPRouter->new($host,$community);
12 $router->connect();
13 $router->query_system_info();
14
15 print "Informações do elemento:\n","#"x80,"\n",
16      $router->{SysDescription},"\n",'#'x80,"\n\n\n";
17
18 print "\tElemento ",$router->{SysName},"\tIP: ",$router->{IP}, "\n";               
19 print "#"x80,"\n\n";
20
21 $router->create_int_table();
22 $router->query_int_status;
23 $router->query_int_ip();
24
25 foreach (sort{$a<=>$b} keys %{$router->{InterfaceIndexNames}}){
26    print  "$router->{InterfaceIndexNames}->{$_}\t",
27           "$router->{InterfaceOperStatus}->{$_}\t",
28           "$router->{InterfaceAdminStatus}->{$_}\t",
29           ($router->{InterfaceIPAddress}->{$_}||"\t"),
30           "\t",($router->{InterfaceAlias}->{$_}||"\t"),
31           "\n";
32 };
33 print '#'x80,"\n\nPressione ENTER para sair\n";
34 <STDIN>

Ao executar este script em diferentes elementos de rede teremos saídas um pouco diferentes. Por exemplo, um roteador gera a saída abaixo:

Digite o HOSTNAME ou IP do ROTEADOR: 10.10.10.1

Informações do elemento:

############################################################

Cisco Internetwork Operating System Software

IOS (tm) C2600 Software (C2600-JS-M), Version 12.2(13)T4,  RELEASE SOFTWARE (fc2)

TAC Support: http://www.cisco.com/tac

Copyright (c) 1986-2003 by cisco Systems, Inc.

Compiled Thu 01-May-03 03:53 by eaarma

############################################################

      Elemento RT01FRODO      IP: 10.10.10.1

############################################################

Fa0/0 up    up    10.10.11.254      REDE LOCAL

Se0/0 down  down                    RESERVADA

Fa0/1 up    up    10.10.12.254      REDE LOCAL

Se0/1 up    up    10.10.13.254      REDE EXTERNA

Nu0   up    up 

Lo100 up    up    10.10.14.100      INTERFACE ADMINISTRATIVA    

VoiceOverIpPeer108      up    up    *** Para ligacoes externas ***

VoiceOverIpPeer100      up    up    *** Para ligacoes DDD ***

VoiceEncapPeer6085      up    up    *** RAMAL do JOAO ***

VoiceEncapPeer6086      up    up    *** RAMAL da ANA ***

VoiceEncapPeer6087      up    up    *** RAMAL do CARLOS ***

VoiceEncapPeer6084      up    up 

Vi1   up    up 

Foreign Exchange Station 1/0/0     dormant     up 

Foreign Exchange Station 1/0/1     dormant     up 

Foreign Exchange Station 1/0/2     dormant     up 

############################################################

 

Pressione ENTER para sair

Se você tiver familiaridade com roteadores CISCO, perceberá que a saída é semelhante a do comando "show interface description". Apesar de otimizado para obter informações de roteadores, como eu disse, o Perl e o SNMP não são preconceituosos. Vamos agora mostrar o que acontece se eu tentar obter informações de um switch de uma concorrente da CISCO, Enterasys:

Digite o HOSTNAME ou IP do ROTEADOR: 10.10.11.1

Informações do elemento:

################################################

Enterasys Networks, Inc. A2H124-24 Rev 02.01.07.0003

################################################

      Elemento SW01SkyWalker  IP: 10.10.11.1

################################################
 

fe.1.1      up    up                     

fe.1.2      down  up                     

fe.1.3      down  up                     

fe.1.4      down  up                     

fe.1.5      down  up                     

fe.1.6      down  up                     

fe.1.7      down  up                     

fe.1.8      down  up                     

fe.1.9      down  up                     

fe.1.10     down  up                     

fe.1.11     down  up                     

fe.1.12     up    up                     

fe.1.13     up    up                     

fe.1.14     down  up                     

fe.1.15     up    up                     

fe.1.16     down  up                     

fe.1.17     up    up                     

fe.1.18     down  up                     

fe.1.19     up    up                     

fe.1.20     down  up                     

fe.1.21     up    up                     

fe.1.22     up    up                     

fe.1.23     up    up                     

fe.1.24     down  up                     

ge.1.25     up    up                     

ge.1.26     up    up                     

ge.1.27     down  up                     

ge.1.28     down  up                     

host  up    up    10.10.11.1       

lag.0.1     down  up                     

lag.0.2     down  up                     

lag.0.3     down  up                     

lag.0.4     down  up                     

lag.0.5     down  up                     

lag.0.6     down  up                     

################################################

Pressione ENTER para sair 

Página anterior     Próxima página

Páginas do artigo
   1. Gerenciando a rede com Perl e SNMP
   2. O que você deve saber sobre MIBs
   3. Legal! E como eu faço para minha rede responder ao protocolo SNMP?
   4. Criando uma solução SNMP personalizada com o Perl
   5. Moral da história...
Outros artigos deste autor

Instalação do Nessus 3.0

PuTTY - Release 0.66 - Parte II

Montando partições em memória

Linux: For Human Beings?

Qual o melhor Linux para eu utilizar?

Leitura recomendada

Enviando notificações do Nagios para o MSN

Impressão remota via WEB

Instalação de MRTG em ambiente Windows

Manipulação de sockets em Perl usando o IO::Socket::INET

Catalyst Framework Perl - Parte III

  
Comentários
[1] Comentário enviado por removido em 23/03/2010 - 09:37h

Se tiver dúvidas ou comentários pode me enviar um e-mail: thiago@nerdsland.net
Estamos às ordens!

[2] Comentário enviado por irado em 24/03/2010 - 17:55h

caramba, um artigo de fôlego, muito bem explicado. Parabéns, thiago.
ps: vou tomar coragem e experimentar - risos.

[3] Comentário enviado por removido em 24/03/2010 - 21:15h

Escrever também me deixou um pouco sem folego. Mas o feedback das pessoas tem sido positivo.
Muito obrigado.

[4] Comentário enviado por juniomundo em 25/03/2010 - 00:32h

Muito obrigado Thiago, foi de grande valia. Paz e saúde pra ti.

[5] Comentário enviado por removido em 25/03/2010 - 05:01h

Muito obrigado Júnio. É sempre um prazer ajudar.

[6] Comentário enviado por alexandre035 em 18/06/2011 - 15:35h

Excelente.

[7] Comentário enviado por j3f1nh0 em 21/09/2012 - 14:30h

Pow cara, muito bom! Meu ajudou muito para o meu TCC!


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts