SNMP Operations Deep Dive
A comprehensive guide to all SNMP operations in SnmpKit.
Setup
Mix.install([
{:snmpkit, "~> 1.3"}
])
alias SnmpKit.{SNMP, Sim}
Create Test Devices
We’ll create two simulated devices to demonstrate various operations.
# Cable modem with traffic counters
cable_modem_oids = %{
"1.3.6.1.2.1.1.1.0" => "ARRIS SURFboard SB8200 DOCSIS 3.1 Cable Modem",
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.4115.1.20.1",
"1.3.6.1.2.1.1.3.0" => %{type: "TimeTicks", value: 987654},
"1.3.6.1.2.1.1.4.0" => "admin@example.com",
"1.3.6.1.2.1.1.5.0" => "cm-001",
"1.3.6.1.2.1.1.6.0" => "Home Network",
"1.3.6.1.2.1.2.1.0" => 2,
"1.3.6.1.2.1.2.2.1.1.1" => 1,
"1.3.6.1.2.1.2.2.1.1.2" => 2,
"1.3.6.1.2.1.2.2.1.2.1" => "cable-downstream0",
"1.3.6.1.2.1.2.2.1.2.2" => "cable-upstream0",
"1.3.6.1.2.1.2.2.1.3.1" => 127,
"1.3.6.1.2.1.2.2.1.3.2" => 127,
"1.3.6.1.2.1.2.2.1.10.1" => %{type: "Counter32", value: 1500000},
"1.3.6.1.2.1.2.2.1.16.1" => %{type: "Counter32", value: 900000}
}
{:ok, cm_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(:cable_modem, {:manual, cable_modem_oids})
{:ok, _cm} = Sim.start_device(cm_profile, port: 1161)
# Router with more interfaces
router_oids = %{
"1.3.6.1.2.1.1.1.0" => "Cisco IOS Software, C2900 Software, Version 15.1(4)M12a",
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.9.1.576",
"1.3.6.1.2.1.1.3.0" => %{type: "TimeTicks", value: 456789},
"1.3.6.1.2.1.1.4.0" => "netops@company.com",
"1.3.6.1.2.1.1.5.0" => "router-001",
"1.3.6.1.2.1.1.6.0" => "Data Center",
"1.3.6.1.2.1.2.1.0" => 4,
"1.3.6.1.2.1.2.2.1.1.1" => 1,
"1.3.6.1.2.1.2.2.1.1.2" => 2,
"1.3.6.1.2.1.2.2.1.1.3" => 3,
"1.3.6.1.2.1.2.2.1.1.4" => 4,
"1.3.6.1.2.1.2.2.1.2.1" => "GigabitEthernet0/0",
"1.3.6.1.2.1.2.2.1.2.2" => "GigabitEthernet0/1",
"1.3.6.1.2.1.2.2.1.2.3" => "Serial0/0/0",
"1.3.6.1.2.1.2.2.1.2.4" => "Loopback0",
"1.3.6.1.2.1.2.2.1.5.1" => %{type: "Gauge32", value: 1000000000},
"1.3.6.1.2.1.2.2.1.5.2" => %{type: "Gauge32", value: 1000000000},
"1.3.6.1.2.1.2.2.1.5.3" => %{type: "Gauge32", value: 1544000},
"1.3.6.1.2.1.2.2.1.8.1" => 1,
"1.3.6.1.2.1.2.2.1.8.2" => 1,
"1.3.6.1.2.1.2.2.1.8.3" => 2,
"1.3.6.1.2.1.2.2.1.8.4" => 1
}
{:ok, router_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(:router, {:manual, router_oids})
{:ok, _router} = Sim.start_device(router_profile, port: 1162)
cm_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
IO.puts("Devices ready:")
IO.puts(" Cable modem: #{cm_target}")
IO.puts(" Router: #{router_target}")
Basic GET Operations
Simple GET
cm_target = "127.0.0.1:1161"
# Get returns an enriched map with multiple fields
{:ok, result} = SNMP.get(cm_target, "sysDescr.0")
IO.puts("Enriched result:")
IO.puts(" OID: #{result.oid}")
IO.puts(" Type: #{result.type}")
IO.puts(" Raw value: #{inspect(result.value)}")
IO.puts(" Formatted: #{result.formatted}")
IO.puts(" Name: #{result.name}")
GET with Options
cm_target = "127.0.0.1:1161"
# Custom timeout and community
{:ok, result} = SNMP.get(cm_target, "sysUpTime.0",
timeout: 5000,
community: "public"
)
IO.puts("Uptime: #{result.formatted}")
GETNEXT
Get the next OID in the tree:
cm_target = "127.0.0.1:1161"
{:ok, result} = SNMP.get_next(cm_target, "sysDescr")
IO.puts("Next after sysDescr: #{result.oid} = #{result.formatted}")
{:ok, result2} = SNMP.get_next(cm_target, result.oid)
IO.puts("Next after that: #{result2.oid} = #{result2.formatted}")
WALK Operations
Basic Walk
cm_target = "127.0.0.1:1161"
{:ok, results} = SNMP.walk(cm_target, "system")
IO.puts("System group:")
Enum.each(results, fn %{oid: oid, type: type, formatted: value} ->
IO.puts(" #{oid} (#{type}) = #{value}")
end)
Walk with Pretty Output
router_target = "127.0.0.1:1162"
{:ok, results} = SNMP.walk_pretty(router_target, "ifDescr")
IO.puts("Interface descriptions:")
Enum.each(results, fn %{oid: oid, formatted: value} ->
IO.puts(" #{oid}: #{value}")
end)
Bulk Operations
Bulk operations are more efficient for retrieving large amounts of data.
GETBULK
router_target = "127.0.0.1:1162"
{:ok, results} = SNMP.get_bulk(router_target, "interfaces", max_repetitions: 10)
IO.puts("Bulk get returned #{length(results)} objects:")
Enum.take(results, 5) |> Enum.each(fn %{oid: oid, formatted: value} ->
IO.puts(" #{oid} = #{value}")
end)
Bulk Walk
router_target = "127.0.0.1:1162"
{:ok, results} = SNMP.bulk_walk(router_target, "interfaces")
IO.puts("Bulk walk returned #{length(results)} objects")
Adaptive Walk
Automatically optimizes bulk parameters based on device response:
router_target = "127.0.0.1:1162"
{:ok, results} = SNMP.adaptive_walk(router_target, "interfaces")
IO.puts("Adaptive walk returned #{length(results)} objects")
Multi-Target Operations
Query multiple devices simultaneously.
GET Multiple Targets
cm_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
requests = [
{cm_target, "sysDescr.0"},
{router_target, "sysDescr.0"},
{cm_target, "sysName.0"},
{router_target, "sysName.0"}
]
results = SNMP.get_multi(requests)
IO.puts("Multi-target results:")
Enum.zip(requests, results)
|> Enum.each(fn {{target, oid}, result} ->
case result do
{:ok, [%{formatted: value} | _]} ->
IO.puts(" #{target} #{oid}: #{value}")
{:error, reason} ->
IO.puts(" #{target} #{oid}: ERROR - #{inspect(reason)}")
end
end)
WALK Multiple Targets
cm_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
walk_requests = [
{cm_target, "system"},
{router_target, "system"}
]
results = SNMP.walk_multi(walk_requests)
IO.puts("Multi-target walk:")
Enum.zip(walk_requests, results)
|> Enum.each(fn {{target, oid}, result} ->
case result do
{:ok, data} ->
IO.puts(" #{target} #{oid}: #{length(data)} objects")
{:error, reason} ->
IO.puts(" #{target} #{oid}: ERROR - #{inspect(reason)}")
end
end)
Streaming Operations
For very large tables, use streaming to process data without loading everything into memory.
router_target = "127.0.0.1:1162"
stream = SNMP.walk_stream(router_target, "interfaces")
IO.puts("Streaming interface data:")
stream
|> Stream.take(5)
|> Enum.each(fn %{oid: oid, type: type, formatted: value} ->
IO.puts(" #{oid} (#{type}) = #{value}")
end)
Error Handling
# Timeout handling
case SNMP.get("192.168.255.255:161", "sysDescr.0", timeout: 1000) do
{:ok, result} -> IO.puts("Got: #{result.formatted}")
{:error, :timeout} -> IO.puts("Request timed out")
{:error, reason} -> IO.puts("Error: #{inspect(reason)}")
end
Next Steps
- MIB Management - OID resolution and custom MIBs
- Device Simulation - Build realistic test devices
- High Performance - Scale to thousands of devices