Skip to main content

Módulo SNMP

O módulo snmp fornece uma interface completa para monitoramento e gerenciamento de dispositivos de rede através do protocolo SNMP (Simple Network Management Protocol). Este módulo é útil para coleta de métricas de roteadores, switches, servidores, impressoras e qualquer dispositivo que suporte SNMP.

  • Suporte a SNMP v1, v2c e v3
  • Operações GET, GET BULK e WALK
  • Autenticação e criptografia SNMP v3
  • Configuração flexível de timeout e retentativas
  • Retorno seguro com tratamento de erros

Protocolos suportados:

  • SNMP v1: Protocolo básico com comunidade pública/privada
  • SNMP v2c: Melhorias de performance com operações BULK
  • SNMP v3: Segurança avançada com autenticação e criptografia

Configuração SNMP

Todas as funções SNMP requerem um objeto de configuração que define os parâmetros de conexão. A configuração é uma tabela Lua com os seguintes campos:

Campos de Configuração Básicos:

Campo Tipo Padrão Descrição
address string obrigatório Endereço IP ou hostname do dispositivo
snmpVersion número 1 Versão SNMP (1=v1, 2=v2c, 3=v3)
snmpPort número 161 Porta SNMP
snmpCommunity string nil Comunidade SNMP (v1/v2c)
snmpTimeout número 5 Timeout em segundos
snmpRetryCount número 3 Número de retentativas
snmpMaxBulkItems número nil Máximo de itens por operação BULK
snmpExponentialBackoff booleano false Habilitar backoff exponencial

Campos para SNMP v3:

Campo Tipo Descrição
snmpSecurityLevel string Nível de segurança: "NoAuthNoPriv", "AuthNoPriv", "AuthPriv"
snmpAuthProtocol string Protocolo de autenticação: "MD5", "SHA1"
snmpAuthUser string Usuário de autenticação
snmpAuthPassword string Senha de autenticação
snmpPrivProtocol string Protocolo de criptografia: "DES", "AES"
snmpPrivPassword string Senha de criptografia

Configuração via Tabela params:

O ambiente Lua inclui uma tabela predefinida chamada params que contém os detalhes do dispositivo atual. Esta tabela pode ser usada diretamente como configuração SNMP, pois já possui os campos necessários no formato esperado.

Exemplo de uso direto:

-- Usar a tabela device diretamente como configuração
local sys_descr = snmp.getex(device, "1.3.6.1.2.1.1.1.0")
print("Descrição do dispositivo:", sys_descr)

-- Versão que não lança erro
local valor, erro = snmp.get_safe(device, "1.3.6.1.2.1.1.3.0")
if not erro then
    print("Uptime do dispositivo:", valor)
end

Combinando com configurações adicionais:

-- Criar configuração baseada em device com ajustes
local config = {
    address = device.address,
    snmpVersion = device.snmpVersion,
    snmpCommunity = device.snmpCommunity,
    snmpTimeout = device.snmpTimeout or 5,  -- Usar padrão se não definido
    snmpRetryCount = 2,  -- Sobrescrever valor padrão
    snmpMaxBulkItems = 50  -- Adicionar configuração extra
}

-- Usar para operação BULK
local oids = {"1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.3.0"}
local resultados = snmp.get_bulk(config, oids)

Exemplos de Configuração:

-- Configuração básica SNMP v2c
local config_v2c = {
    address = "192.168.1.1",
    snmpVersion = 2,
    snmpCommunity = "public",
    snmpTimeout = 3,
    snmpRetryCount = 2
}

-- Configuração SNMP v3 com autenticação e criptografia
local config_v3 = {
    address = "10.0.0.254",
    snmpVersion = 3,
    snmpSecurityLevel = "AuthPriv",
    snmpAuthProtocol = "SHA1",
    snmpAuthUser = "monitor",
    snmpAuthPassword = "senha123",
    snmpPrivProtocol = "AES",
    snmpPrivPassword = "chave456",
    snmpTimeout = 5
}

-- Configuração para dispositivo com porta não padrão
local config_custom_port = {
    address = "switch.piso1.local",
    snmpVersion = 2,
    snmpCommunity = "internal",
    snmpPort = 8161,  -- Porta customizada
    snmpTimeout = 10  -- Timeout maior para rede lenta
}

Funções Disponíveis

1. snmp.getex(config, oid)

Realiza uma consulta SNMP GET para um OID específico.

Parâmetros:

  • config (tabela): Configuração SNMP (ver seção acima)
  • oid (string): OID a ser consultado (formato numérico ou nomeado)

Retorno:

  • valor: Valor retornado pelo dispositivo SNMP (número, string, etc.)

Erros:

  • Lança erro se o OID não existir ou houver falha na comunicação

Exemplo de Uso:

-- Consultar sysDescr (descrição do sistema)
local config = {
    address = "192.168.1.1",
    snmpVersion = 2,
    snmpCommunity = "public"
}

local sys_descr = snmp.getex(config, "1.3.6.1.2.1.1.1.0")
-- ou usando OID nomeado
local sys_descr = snmp.getex(config, ".1.3.6.1.2.1.1.1.0")

print("Descrição do sistema:", sys_descr)
-- Exemplo de saída: "Cisco IOS Software, C3750 Software (C3750-IPSERVICESK9-M), Version 12.2(55)SE10, RELEASE SOFTWARE (fc2)"

-- Consultar uptime do sistema
local sys_uptime = snmp.getex(config, "1.3.6.1.2.1.1.3.0")
print("Uptime:", sys_uptime, "centésimos de segundo")

-- Consultar nome do host
local sys_name = snmp.getex(config, "1.3.6.1.2.1.1.5.0")
print("Nome do host:", sys_name)

-- Consultar localização
local sys_location = snmp.getex(config, "1.3.6.1.2.1.1.6.0")
print("Localização:", sys_location)

2. snmp.get_safe(config, oid)

Versão segura de getex que não lança exceções, retornando erro como segundo valor.

Parâmetros:

  • config (tabela): Configuração SNMP
  • oid (string): OID a ser consultado

Retorno:

  • tuple: (valor, erro) onde:
    • valor (qualquer tipo ou nil): Valor retornado se bem-sucedido
    • erro (string ou nil): Mensagem de erro se falhar, nil se bem-sucedido

Exemplo de Uso:

local config = {
    address = "192.168.1.1",
    snmpVersion = 2,
    snmpCommunity = "public"
}

-- Consulta segura que não quebra o script em caso de erro
local valor, erro = snmp.get_safe(config, "1.3.6.1.2.1.1.1.0")

if erro then
    log.error("Falha na consulta SNMP:", erro)
    -- Tomar ação alternativa
else
    print("Valor obtido:", valor)
end

-- Consultar múltiplos OIDs com tratamento de erro individual
local oids = {
    "1.3.6.1.2.1.1.1.0",  -- sysDescr
    "1.3.6.1.2.1.1.3.0",  -- sysUpTime
    "1.3.6.1.2.1.1.5.0",  -- sysName
    "1.3.6.1.2.1.1.6.0"   -- sysLocation
}

local resultados = {}
for _, oid in ipairs(oids) do
    local valor, erro = snmp.get_safe(config, oid)
    if erro then
        log.warn("Falha no OID", oid, ":", erro)
        resultados[oid] = {erro = erro}
    else
        resultados[oid] = {valor = valor}
    end
end

3. snmp.get_bulk(config, oids)

Realiza operação SNMP GET BULK para múltiplos OIDs de uma vez (SNMP v2c/v3).

Parâmetros:

  • config (tabela): Configuração SNMP (deve ser v2 ou v3)
  • oids (array de strings): Lista de OIDs para consulta

Retorno:

  • tabela: Mapa OID → valor para todos os OIDs consultados

Comportamento:

  • Mais eficiente que múltiplas chamadas getex para muitos OIDs
  • Suportado apenas em SNMP v2c e v3
  • Usa snmpMaxBulkItems da configuração para limitar tamanho

Exemplo de Uso:

local config = {
    address = "192.168.1.1",
    snmpVersion = 2,  -- Deve ser v2 ou v3 para GET BULK
    snmpCommunity = "public",
    snmpMaxBulkItems = 50  -- Limitar a 50 OIDs por operação
}

-- Consultar múltiplas informações do sistema de uma vez
local oids = {
    "1.3.6.1.2.1.1.1.0",  -- sysDescr
    "1.3.6.1.2.1.1.3.0",  -- sysUpTime
    "1.3.6.1.2.1.1.5.0",  -- sysName
    "1.3.6.1.2.1.1.6.0",  -- sysLocation
    "1.3.6.1.2.1.1.7.0"   -- sysServices
}

local resultados = snmp.get_bulk(config, oids)

for oid, valor in pairs(resultados) do
    print("OID:", oid, "=", valor)
end

-- Consultar informações de múltiplas interfaces
local function obter_info_interfaces(config, indices)
    local oids = {}
    for _, idx in ipairs(indices) do
        table.insert(oids, "1.3.6.1.2.1.2.2.1.2." .. idx)   -- ifDescr
        table.insert(oids, "1.3.6.1.2.1.2.2.1.3." .. idx)   -- ifType
        table.insert(oids, "1.3.6.1.2.1.2.2.1.5." .. idx)
    end
    return snmp.get_bulk(config, oids)
end

4. snmp.get(oid)

Versão simplificada de snmp.getex que usa automaticamente a configuração do dispositivo atual (params).

Parâmetros:

  • oid (string): OID a ser consultado

Retorno:

  • valor: Valor retornado pelo dispositivo SNMP

Comportamento:

  • Usa params como configuração
  • Lança erro se o OID não existir ou houver falha na comunicação

Exemplo de Uso:

-- Consulta simplificada usando a configuração do dispositivo atual
local sys_descr = snmp.get("1.3.6.1.2.1.1.1.0")
print("Descrição do sistema:", sys_descr)

-- Consultar múltiplos OIDs
local uptime = snmp.get("1.3.6.1.2.1.1.3.0")
local hostname = snmp.get("1.3.6.1.2.1.1.5.0")

print("Uptime:", uptime, "Hostname:", hostname)

5. snmp.walk(oid, cache_ttl, enforce_ordering)

Realiza uma operação SNMP WALK com suporte a cache e ordenação.

Parâmetros:

  • oid (string): OID base para o walk
  • cache_ttl (número, opcional): Tempo de vida do cache em segundos
  • enforce_ordering (booleano, opcional): Forçar ordenação dos resultados (ordem natural)

Comportamento do cache:

  • Usa cache quando cache_ttl é especificado

Exemplo de Uso:

-- Walk com cache de 30 segundos
local interfaces = snmp.walk("1.3.6.1.2.1.2.2.1.2", 30)
for oid, ifname in pairs(interfaces) do
    print("Interface", oid, ":", ifname)
end

-- Walk com ordenação forçada (sem cache)
local ordered_interfaces = snmp.walk("1.3.6.1.2.1.2.2.1.2", nil, true)
print("Total de interfaces:", #ordered_interfaces)

Comportamento de Ordenação:

O parâmetro enforce_ordering controla como os resultados são estruturados:

  • Quando false (padrão): Os resultados são retornados como uma tabela Lua onde cada OID é uma chave que mapeia para seu valor. Esta estrutura é eficiente para acesso aleatório, mas perde a ordenação natural dos OIDs, pois as tabelas Lua não preservam a ordem de inserção das chaves.

  • Quando true: Os resultados são retornados como uma lista de pares (tabela de tabelas), onde cada elemento é uma tabela contendo 2 elementos. Esta estrutura preserva a ordem natural dos OIDs conforme retornados pelo dispositivo SNMP.

Exemplo de diferença:

-- Com enforce_ordering = false (padrão)
local resultado_tabela = snmp.walk("1.3.6.1.2.1.2.2.1.2", nil, false)
-- Estrutura: { ["1.3.6.1.2.1.2.2.1.2.1"] = "eth0", ["1.3.6.1.2.1.2.2.1.2.2"] = "eth1" }
-- A ordem das chaves não é garantida

-- Com enforce_ordering = true
local resultado_lista = snmp.walk("1.3.6.1.2.1.2.2.1.2", nil, true)
-- Estrutura: { {"1.3.6.1.2.1.2.2.1.2.1", "eth0"},
--              {"1.3.6.1.2.1.2.2.1.2.2", "eth1"} }
-- A ordem dos elementos é preservada

Quando usar cada modo:

  • Use enforce_ordering = false quando você só precisa acessar valores por OID específico e a ordem não importa.
  • Use enforce_ordering = true quando você precisa processar os resultados na mesma ordem em que foram retornados pelo dispositivo, como para:
    • Gerar relatórios ordenados
    • Processar sequências de índices consecutivos
    • Manter correspondência com outras listas ordenadas

Retorno:

  • tabela: Mapa OID → valor para todos os OIDs encontrados

6. snmp.walkex(device, oid, cache_ttl)

Função estendida de walk com sistema de cache avançado e prevenção de execuções concorrentes.

Parâmetros:

  • device (tabela): Configuração do dispositivo
  • oid (string): OID base para o walk
  • cache_ttl (número, opcional): Tempo de vida do cache em segundos

Retorno:

  • tabela: Mapa OID → valor para todos os OIDs encontrados

Comportamento:

  • Implementa cache global compartilhado entre execuções
  • Previne execuções concorrentes do mesmo walk
  • Usa registry para coordenar execuções simultâneas

Exemplo de Uso:

-- Walk estendido com cache de 60 segundos
local device_config = {
    address = "192.168.1.1",
    snmpVersion = 2,
    snmpCommunity = "public"
}

local sys_oids = snmp.walkex(device_config, "1.3.6.1.2.1.1", 60)
for oid, value in pairs(sys_oids) do
    print("OID:", oid, "Valor:", value)
end

7. snmp.count(oid)

Conta o número de itens retornados por um walk.

Parâmetros:

  • oid (string): OID base para contar

Retorno:

  • número: Quantidade de itens encontrados

Exemplo de Uso:

-- Contar número de interfaces
local num_interfaces = snmp.count("1.3.6.1.2.1.2.2.1.2")
print("Número de interfaces:", num_interfaces)

-- Contar número de processos
local num_processes = snmp.count("1.3.6.1.2.1.25.4.2.1.2")
print("Número de processos:", num_processes)

8. snmp.diff(typ, lhs, rhs)

Calcula a diferença entre dois valores, tratando rollover de contadores.

Parâmetros:

  • typ (número): Tipo do contador (32 ou 64 bits)
  • lhs (número): Valor atual
  • rhs (número): Valor anterior

Retorno:

  • número: Diferença entre os valores

Comportamento:

  • Trata rollover de contadores de 32 e 64 bits
  • Sinaliza RepeatPrevValue se a diferença for negativa

Exemplo de Uso:

-- Calcular diferença para contador de 32 bits
local current_bytes = snmp.get("1.3.6.1.2.1.2.2.1.10.1")  -- ifInOctets.1
local prev_bytes = prev("1.3.6.1.2.1.2.2.1.10.1")
local bytes_diff = snmp.diff(32, current_bytes, prev_bytes)

print("Bytes recebidos desde última leitura:", bytes_diff)

9. inst(oid)

Resolve dinamicamente OIDs de instâncias baseado no nome da instância.

Parâmetros:

  • oid (string): OID base (sem índice de instância)

Retorno:

  • string: OID completo com índice de instância

Comportamento:

  • Usa params.InstanceName e params.snmpOIDDesc para resolução
  • Suporta cache de instâncias
  • Lança erro se a instância não for encontrada

Exemplo de Uso:

-- Resolver OID para instância específica
-- params.InstanceName = "eth0"
-- params.snmpOIDDesc = "1.3.6.1.2.1.2.2.1.2"  -- ifDescr

local if_in_octets_oid = inst("1.3.6.1.2.1.2.2.1.10")  -- ifInOctets
print("OID resolvido:", if_in_octets_oid)
-- Saída: "1.3.6.1.2.1.2.2.1.10.1" (se eth0 for índice 1)

-- Consultar usando OID resolvido
local bytes_in = snmp.get(if_in_octets_oid)
print("Bytes recebidos na interface eth0:", bytes_in)

10. prev(oid)

Obtém o valor anterior de um OID armazenado.

Parâmetros:

  • oid (string): OID para obter valor anterior

Retorno:

  • qualquer tipo: Valor anterior armazenado, ou 0 se não existir

Comportamento:

  • Busca valor em store.get("snmp.value." .. oid)
  • Retorna 0 se não encontrar valor armazenado

Exemplo de Uso:

-- Obter valor anterior para cálculo de taxa
local current_value = snmp.get("1.3.6.1.2.1.2.2.1.16.1")  -- ifOutOctets.1
local previous_value = prev("1.3.6.1.2.1.2.2.1.16.1")

local bytes_out_diff = current_value - previous_value
print("Bytes enviados desde última leitura:", bytes_out_diff)

11. lapsed(oid)

Obtém o tempo decorrido desde a última leitura de um OID.

Parâmetros:

  • oid (string): OID para verificar tempo decorrido

Retorno:

  • número: Tempo em segundos desde última leitura, ou 1 se não houver registro

Exemplo de Uso:

-- Calcular taxa por segundo
local current_counter = snmp.get("1.3.6.1.2.1.2.2.1.10.1")  -- ifInOctets.1
local previous_counter = prev("1.3.6.1.2.1.2.2.1.10.1")
local time_elapsed = lapsed("1.3.6.1.2.1.2.2.1.10.1")

local bytes_per_second = (current_counter - previous_counter) / time_elapsed
print("Taxa de recebimento:", bytes_per_second, "bytes/segundo")

Exemplos Completos

Monitoramento de Interface de Rede:

-- Resolver OID da interface eth0
local if_index_oid = inst("1.3.6.1.2.1.2.2.1.1")  -- ifIndex
local if_descr_oid = inst("1.3.6.1.2.1.2.2.1.2")  -- ifDescr

-- Obter informações da interface
local interface_index = snmp.get(if_index_oid)
local interface_name = snmp.get(if_descr_oid)

print("Monitorando interface:", interface_name, "(índice", interface_index, ")")

-- Coletar estatísticas
local in_octets = snmp.get(inst("1.3.6.1.2.1.2.2.1.10"))  -- ifInOctets
local out_octets = snmp.get(inst("1.3.6.1.2.1.2.2.1.16")) -- ifOutOctets
local in_errors = snmp.get(inst("1.3.6.1.2.1.2.2.1.14"))  -- ifInErrors
local out_errors = snmp.get(inst("1.3.6.1.2.1.2.2.1.20")) -- ifOutErrors

-- Calcular diferenças desde última leitura
local time_elapsed = lapsed(inst("1.3.6.1.2.1.2.2.1.10"))
local prev_in = prev(inst("1.3.6.1.2.1.2.2.1.10"))
local prev_out = prev(inst("1.3.6.1.2.1.2.2.1.16"))

local in_rate = (in_octets - prev_in) / time_elapsed
local out_rate = (out_octets - prev_out) / time_elapsed

print("Taxa de entrada:", in_rate, "bytes/seg")
print("Taxa de saída:", out_rate, "bytes/seg")
print("Erros de entrada:", in_errors)
print("Erros de saída:", out_errors)

Inventário de Interfaces com Walk:

-- Listar todas as interfaces com walk
local interfaces = snmp.walk("1.3.6.1.2.1.2.2.1.2", 300)  -- ifDescr com cache de 5 minutos

print("=== Inventário de Interfaces ===")
for oid, ifname in pairs(interfaces) do
    -- Extrair índice da interface do OID
    local index = string.match(oid, "(%d+)$")

    -- Obter tipo e status da interface
    local iftype = snmp.get("1.3.6.1.2.1.2.2.1.3." .. index)   -- ifType
    local ifstatus = snmp.get("1.3.6.1.2.1.2.2.1.8." .. index)  -- ifOperStatus

    local status_text = "DOWN"
    if ifstatus == 1 then status_text = "UP" end

    print(string.format("Interface %s: %s (Tipo: %d, Status: %s)",
                       index, ifname, iftype, status_text))
end

print("Total de interfaces:", snmp.count("1.3.6.1.2.1.2.2.1.2"))

Monitoramento de Uso de CPU com Múltiplas Instâncias:

-- Usar walk para obter todas as CPUs
local cpu_oids = snmp.walk("1.3.6.1.2.1.25.3.3.1.2", 30)  -- hrProcessorLoad

local total_load = 0
local cpu_count = 0

for oid, load in pairs(cpu_oids) do
    cpu_count = cpu_count + 1
    total_load = total_load + load

    local cpu_index = string.match(oid, "(%d+)$")
    print(string.format("CPU %d: %d%%", cpu_index, load))
end

if cpu_count > 0 then
    local avg_load = total_load / cpu_count
    print(string.format("Média de uso de CPU: %.1f%%", avg_load))
end