SnmpKit Interactive Tour 🚀
Section
Welcome to SnmpKit v0.3.5!
This interactive Livebook will take you on a comprehensive tour of SnmpKit’s new unified API. We’ll start by creating a simulated SNMP device and then demonstrate all the powerful features against our own simulation - no external network required!
What you’ll learn:
- 🎯 Unified API - Clean, context-based modules
- 📡 SNMP Operations - get, walk, bulk, multi-target
- 📚 MIB Management - resolution, compilation, tree navigation
- 🧪 Device Simulation - realistic testing environments
- ⚡ Advanced Features - streaming, performance, analytics
Let’s get started! 🚀
Setup
First, let’s install SnmpKit and configure our environment:
Mix.install([
{:snmpkit, "~> 0.3.5"}
])
# Configure logging for our tour
Logger.configure(level: :info)
# Import the unified API modules for convenience
alias SnmpKit.{SNMP, MIB, Sim}
IO.puts("🎉 SnmpKit v0.3.5 loaded successfully!")
IO.puts("📚 Ready to explore the unified API!")
Chapter 1: Start Our Simulated Network 🖥️
Before we can demonstrate SNMP operations, let’s create our own simulated network! This is one of SnmpKit’s most powerful features - realistic device simulation for testing and development.
Create a Cable Modem Simulation
# Create a realistic DOCSIS cable modem with essential OIDs
cable_modem_oids = %{
# System Group
"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.2.25",
"1.3.6.1.2.1.1.3.0" => %{type: "TimeTicks", value: 0},
"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",
# Interface Group
"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, # docsCableMaclayer
"1.3.6.1.2.1.2.2.1.3.2" => 127,
# DOCSIS Specific OIDs
"1.3.6.1.2.1.10.127.1.1.1.1.3.2" => %{type: "INTEGER", value: 3}, # docsIfCmtsUpChannelId
"1.3.6.1.2.1.10.127.1.1.1.1.6.2" => %{type: "INTEGER", value: 6400000}, # docsIfCmtsUpChannelFrequency
"1.3.6.1.2.1.10.127.1.2.2.1.1.2" => %{type: "INTEGER", value: 1}, # docsIfCmStatusValue
"1.3.6.1.2.1.10.127.1.2.2.1.12.2" => %{type: "Counter32", value: 1000}, # docsIfCmStatusUnerroreds
# Cable Modem specific counters
"1.3.6.1.2.1.2.2.1.10.1" => %{type: "Counter32", value: 1500000}, # ifInOctets
"1.3.6.1.2.1.2.2.1.16.1" => %{type: "Counter32", value: 900000}, # ifOutOctets
"1.3.6.1.2.1.2.2.1.11.1" => %{type: "Counter32", value: 12000}, # ifInUcastPkts
"1.3.6.1.2.1.2.2.1.17.1" => %{type: "Counter32", value: 8000}, # ifOutUcastPkts
}
# Create the cable modem profile
{:ok, cable_modem_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(
:cable_modem,
{:manual, cable_modem_oids},
behaviors: [:counter_increment, :time_based_changes]
)
# Start our simulated cable modem on port 1161
{:ok, cable_modem} = Sim.start_device(cable_modem_profile, [
port: 1161,
community: "public"
])
# Define our target for easy reference
cable_modem_target = "127.0.0.1:1161"
IO.puts("✅ Cable modem simulation started on #{cable_modem_target}")
IO.puts("🎯 Ready for SNMP operations!")
# Make target available to other cells
cable_modem_target
Create a Router Simulation
Let’s also create a router simulation to demonstrate multi-target operations:
# Create a realistic enterprise router with essential OIDs
router_oids = %{
# System Group
"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: 0},
"1.3.6.1.2.1.1.4.0" => "IT Department ",
"1.3.6.1.2.1.1.5.0" => "router-001",
"1.3.6.1.2.1.1.6.0" => "Main Office - Server Room",
# Interface Group (more interfaces for a router)
"1.3.6.1.2.1.2.1.0" => 5, # ifNumber (more interfaces)
# FastEthernet0/0 (WAN)
"1.3.6.1.2.1.2.2.1.1.1" => 1,
"1.3.6.1.2.1.2.2.1.2.1" => "FastEthernet0/0",
"1.3.6.1.2.1.2.2.1.3.1" => 6, # ethernetCsmacd
"1.3.6.1.2.1.2.2.1.5.1" => %{type: "Gauge32", value: 100000000}, # 100Mbps
"1.3.6.1.2.1.2.2.1.8.1" => 1, # up
# FastEthernet0/1 (LAN)
"1.3.6.1.2.1.2.2.1.1.2" => 2,
"1.3.6.1.2.1.2.2.1.2.2" => "FastEthernet0/1",
"1.3.6.1.2.1.2.2.1.3.2" => 6,
"1.3.6.1.2.1.2.2.1.5.2" => %{type: "Gauge32", value: 100000000},
"1.3.6.1.2.1.2.2.1.8.2" => 1,
# Serial0/0/0 (WAN backup)
"1.3.6.1.2.1.2.2.1.1.3" => 3,
"1.3.6.1.2.1.2.2.1.2.3" => "Serial0/0/0",
"1.3.6.1.2.1.2.2.1.3.3" => 22, # propPointToPointSerial
"1.3.6.1.2.1.2.2.1.5.3" => %{type: "Gauge32", value: 1544000}, # T1 speed
"1.3.6.1.2.1.2.2.1.8.3" => 2, # down (backup interface)
# Loopback0
"1.3.6.1.2.1.2.2.1.1.4" => 4,
"1.3.6.1.2.1.2.2.1.2.4" => "Loopback0",
"1.3.6.1.2.1.2.2.1.3.4" => 24, # softwareLoopback
"1.3.6.1.2.1.2.2.1.8.4" => 1,
# Traffic counters for active interfaces
"1.3.6.1.2.1.2.2.1.10.1" => %{type: "Counter32", value: 0}, # ifInOctets WAN
"1.3.6.1.2.1.2.2.1.16.1" => %{type: "Counter32", value: 0}, # ifOutOctets WAN
"1.3.6.1.2.1.2.2.1.10.2" => %{type: "Counter32", value: 0}, # ifInOctets LAN
"1.3.6.1.2.1.2.2.1.16.2" => %{type: "Counter32", value: 0}, # ifOutOctets LAN
# IP routing info
"1.3.6.1.2.1.4.1.0" => 1, # ipForwarding (enabled)
"1.3.6.1.2.1.4.2.0" => 30, # ipDefaultTTL
# SNMP community info
"1.3.6.1.2.1.11.1.0" => %{type: "Counter32", value: 0}, # snmpInPkts
"1.3.6.1.2.1.11.2.0" => %{type: "Counter32", value: 0}, # snmpOutPkts
}
# Create the router profile
{:ok, router_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(
:router,
{:manual, router_oids},
behaviors: [:counter_increment, :time_based_changes]
)
{:ok, router} = Sim.start_device(router_profile, [
port: 1162,
community: "public"
])
router_target = "127.0.0.1:1162"
IO.puts("✅ Router simulation started on #{router_target}")
IO.puts("🌐 Now we have a complete simulated network!")
IO.puts("📊 Loaded #{map_size(router_oids)} router OIDs")
# Make targets available to other cells
{cable_modem_target, router_target}
Quick Connectivity Test
Let’s verify our simulated devices are responding:
# Get targets from previous cells
cable_modem_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
# Test cable modem
case SNMP.get(cable_modem_target, "sysDescr.0") do
{:ok, description} ->
IO.puts("📡 Cable Modem: #{description}")
{:error, reason} ->
IO.puts("❌ Cable modem error: #{inspect(reason)}")
end
# Test router
case SNMP.get(router_target, "sysDescr.0") do
{:ok, description} ->
IO.puts("🔀 Router: #{description}")
{:error, reason} ->
IO.puts("❌ Router error: #{inspect(reason)}")
end
IO.puts("\n🎉 Both devices are responding! Let's explore the API...")
Chapter 2: SnmpKit.SNMP - Protocol Operations 📡
Now let’s explore the comprehensive SNMP operations available through SnmpKit.SNMP
. All operations will work against our simulated devices!
Basic GET Operations
# Set targets for this cell
cable_modem_target = "127.0.0.1:1161"
IO.puts("=== Basic SNMP GET Operations ===\n")
# Standard GET operation
{:ok, system_desc} = SNMP.get(cable_modem_target, "sysDescr.0")
IO.puts("System Description: #{system_desc}")
# GET with type information
{:ok, {oid, type, value}} = SNMP.get_with_type(cable_modem_target, "sysUpTime.0")
IO.puts("System Uptime: #{value} (#{type}) at OID #{oid}") # Remove Enum.join
# GET with pretty formatting
{:ok, formatted_uptime} = SNMP.get_pretty(cable_modem_target, "sysUpTime.0")
IO.puts("Formatted Uptime: #{formatted_uptime}")
# GET system contact
{:ok, contact} = SNMP.get(cable_modem_target, "sysContact.0")
IO.puts("System Contact: #{contact}")
WALK Operations
WALK operations traverse the SNMP tree to get multiple related values:
# Set targets for this cell
cable_modem_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
IO.puts("\n=== SNMP WALK Operations ===\n")
# Walk the system group
{:ok, system_info} = SNMP.walk(cable_modem_target, "1.3.6.1.2.1.1.1")
IO.puts("System group contains #{length(system_info)} objects:")
# Display first few system objects
system_info
|> Enum.take(5)
|> Enum.each(fn {oid, type, value} ->
IO.puts(" #{oid} (#{type}) = #{inspect(value)}")
end)
# Walk with pretty formatting
{:ok, pretty_system} = SNMP.walk_pretty(cable_modem_target, "system")
IO.puts("\nPretty formatted system info:")
pretty_system
|> Enum.take(3)
|> Enum.each(fn {name, value} ->
IO.puts(" #{name}: #{value}")
end)
Interface Information
Let’s explore interface data, which is crucial for network monitoring:
# Set targets for this cell
cable_modem_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
IO.puts("\n=== Interface Information ===\n")
# Get interface count from cable modem
{:ok, cm_if_count} = SNMP.get(cable_modem_target, "ifNumber.0")
IO.puts("Cable Modem interfaces: #{cm_if_count}")
# Get interface count from router
{:ok, router_if_count} = SNMP.get(router_target, "ifNumber.0")
IO.puts("Router interfaces: #{router_if_count}")
# Get interface descriptions
{:ok, cm_if1_desc} = SNMP.get(cable_modem_target, "ifDescr.1")
{:ok, cm_if2_desc} = SNMP.get(cable_modem_target, "ifDescr.2")
IO.puts("\nCable Modem Interface Details:")
IO.puts(" Interface 1: #{cm_if1_desc}")
IO.puts(" Interface 2: #{cm_if2_desc}")
{:ok, router_if1_desc} = SNMP.get(router_target, "ifDescr.1")
{:ok, router_if2_desc} = SNMP.get(router_target, "ifDescr.2")
IO.puts("\nRouter Interface Details:")
IO.puts(" Interface 1: #{router_if1_desc}")
IO.puts(" Interface 2: #{router_if2_desc}")
Bulk Operations
For large amounts of data, bulk operations are much more efficient:
# Set targets for this cell
cable_modem_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
IO.puts("\n=== Bulk Operations ===\n")
# Standard bulk walk
{:ok, bulk_results} = SNMP.bulk_walk(cable_modem_target, "interfaces")
IO.puts("Bulk walk of interfaces returned #{length(bulk_results)} objects")
# Adaptive bulk walk (auto-optimizes performance)
{:ok, adaptive_results} = SNMP.adaptive_walk(cable_modem_target, "interfaces")
IO.puts("Adaptive walk returned #{length(adaptive_results)} objects")
# Get bulk with specific parameters
{:ok, bulk_specific} = SNMP.get_bulk(cable_modem_target, "interfaces", [
max_repetitions: 5,
timeout: 2000
])
IO.puts("Targeted bulk operation returned #{length(bulk_specific)} objects")
# Show some bulk results
bulk_results
|> Enum.take(3)
|> Enum.each(fn {oid, type, value} ->
IO.puts(" #{oid} (#{type}): #{inspect(value)}")
end)
Multi-Target Operations
One of SnmpKit’s powerful features is querying multiple devices simultaneously:
# Set targets for this cell
cable_modem_target = "127.0.0.1:1161"
router_target = "127.0.0.1:1162"
IO.puts("\n=== Multi-Target Operations ===\n")
# Query both devices for system information
multi_targets = [
{cable_modem_target, "sysDescr.0"},
{router_target, "sysDescr.0"},
{cable_modem_target, "sysUpTime.0"},
{router_target, "sysContact.0"}
]
multi_results = SNMP.get_multi(multi_targets)
IO.puts("Multi-target query results:")
IO.puts("Raw results: #{inspect(multi_results)}")
# Process results based on actual format
multi_results
|> Enum.with_index()
|> Enum.each(fn {result, index} ->
{target, oid} = Enum.at(multi_targets, index)
case result do
{:ok, value} ->
IO.puts(" ✅ #{target} #{oid}: #{inspect(value)}")
{:error, reason} ->
IO.puts(" ❌ #{target} #{oid}: #{inspect(reason)}")
_ ->
IO.puts(" ? #{target} #{oid}: #{inspect(result)}")
end
end)
# Multi-target walk operations
walk_targets = [
{cable_modem_target, "system"},
{router_target, "system"}
]
multi_walk_results = SNMP.walk_multi(walk_targets)
IO.puts("\nMulti-target walk completed for #{length(multi_walk_results)} targets")
# Display walk results
multi_walk_results
|> Enum.with_index()
|> Enum.each(fn {result, index} ->
{target, oid} = Enum.at(walk_targets, index)
case result do
{:ok, walk_data} ->
IO.puts(" ✅ #{target} #{oid}: #{length(walk_data)} objects")
# Show first few objects
walk_data
|> Enum.take(3)
|> Enum.each(fn {obj_oid, type, value} ->
oid_str = if is_list(obj_oid), do: Enum.join(obj_oid, "."), else: obj_oid
IO.puts(" #{oid_str} (#{type}) = #{inspect(value)}")
end)
{:error, reason} ->
IO.puts(" ❌ #{target} #{oid}: #{inspect(reason)}")
_ ->
IO.puts(" ? #{target} #{oid}: #{inspect(result)}")
end
end)
Chapter 3: SnmpKit.MIB - MIB Management 📚
The MIB (Management Information Base) system is the heart of SNMP. It defines the structure and meaning of SNMP data. Let’s explore SnmpKit’s powerful MIB capabilities!
OID Name Resolution
IO.puts("=== MIB Name Resolution ===\n")
# Resolve common SNMP object names to OIDs
common_objects = [
"sysDescr.0",
"sysUpTime.0",
"sysContact.0",
"ifNumber.0",
"ifDescr.1",
"ifInOctets.1",
"system",
"interfaces"
]
IO.puts("Common SNMP objects and their OIDs:")
Enum.each(common_objects, fn name ->
case MIB.resolve(name) do
{:ok, oid} ->
IO.puts(" #{name} → #{Enum.join(oid, ".")}")
{:error, reason} ->
IO.puts(" #{name} → Error: #{reason}")
end
end)
Reverse OID Lookup
IO.puts("\n=== Reverse OID Lookup ===\n")
# Convert OIDs back to names
test_oids = [
[1, 3, 6, 1, 2, 1, 1, 1, 0],
[1, 3, 6, 1, 2, 1, 1, 3, 0],
[1, 3, 6, 1, 2, 1, 2, 1, 0],
[1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1]
]
IO.puts("OID to name resolution:")
Enum.each(test_oids, fn oid ->
case MIB.reverse_lookup(oid) do
{:ok, name} ->
IO.puts(" #{Enum.join(oid, ".")} → #{name}")
{:error, reason} ->
IO.puts(" #{Enum.join(oid, ".")} → #{reason}")
end
end)
MIB Tree Navigation
IO.puts("\n=== MIB Tree Navigation ===\n")
# Get children of the system group
{:ok, system_oid} = MIB.resolve("system")
{:ok, system_children} = MIB.children(system_oid)
system_oid_str = if is_list(system_oid), do: Enum.join(system_oid, "."), else: inspect(system_oid)
IO.puts("System group (#{system_oid_str}) has #{length(system_children)} children:")
system_children
|> Enum.take(5)
|> Enum.each(fn child_oid ->
oid_str = if is_list(child_oid), do: Enum.join(child_oid, "."), else: inspect(child_oid)
case MIB.reverse_lookup(child_oid) do
{:ok, name} ->
IO.puts(" #{oid_str} (#{name})")
{:error, _} ->
IO.puts(" #{oid_str}")
end
end)
# Get parent of a specific OID
{:ok, sys_descr_oid} = MIB.resolve("sysDescr.0")
{:ok, parent_oid} = MIB.parent(sys_descr_oid)
{:ok, parent_name} = MIB.reverse_lookup(parent_oid)
parent_oid_str = if is_list(parent_oid), do: Enum.join(parent_oid, "."), else: inspect(parent_oid)
IO.puts("\nParent of sysDescr.0: #{parent_oid_str} (#{parent_name})")
Chapter 4: Creating Custom Device Simulations 🛠️
One of the most powerful features of SnmpKit is creating realistic device simulations without needing walk files. Let’s explore different approaches:
Enterprise Switch Simulation
IO.puts("=== Creating Enterprise Switch Simulation ===\n")
# Define a realistic 24-port enterprise switch
switch_oids = %{
# System Group
"1.3.6.1.2.1.1.1.0" => "Cisco IOS Software, C3560CX Software, Version 15.2(4)E10",
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.9.1.1208",
"1.3.6.1.2.1.1.3.0" => %{type: "TimeTicks", value: 0},
"1.3.6.1.2.1.1.4.0" => "Network Operations ",
"1.3.6.1.2.1.1.5.0" => "switch-core-01",
"1.3.6.1.2.1.1.6.0" => "Main Office - Network Closet A",
# Switch has many interfaces (24 ports + management)
"1.3.6.1.2.1.2.1.0" => 25, # ifNumber
}
# Add interfaces programmatically
switch_oids = Enum.reduce(1..24, switch_oids, fn port, acc ->
Map.merge(acc, %{
# Interface descriptions
"1.3.6.1.2.1.2.2.1.2.#{port}" => "GigabitEthernet0/#{port}",
"1.3.6.1.2.1.2.2.1.3.#{port}" => 6, # ethernetCsmacd
"1.3.6.1.2.1.2.2.1.5.#{port}" => %{type: "Gauge32", value: 1000000000}, # 1Gbps
"1.3.6.1.2.1.2.2.1.8.#{port}" => if(port <= 12, do: 1, else: 2), # First 12 up, rest down
# Traffic counters for active ports
"1.3.6.1.2.1.2.2.1.10.#{port}" => %{type: "Counter32", value: 0},
"1.3.6.1.2.1.2.2.1.16.#{port}" => %{type: "Counter32", value: 0},
})
end)
# Add management interface
switch_oids = Map.merge(switch_oids, %{
"1.3.6.1.2.1.2.2.1.2.25" => "Management0",
"1.3.6.1.2.1.2.2.1.3.25" => 6,
"1.3.6.1.2.1.2.2.1.5.25" => %{type: "Gauge32", value: 100000000}, # 100Mbps
"1.3.6.1.2.1.2.2.1.8.25" => 1,
})
# Create and start the switch
{:ok, switch_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(
:switch,
{:manual, switch_oids},
behaviors: [:counter_increment, :time_based_changes]
)
{:ok, switch_device} = Sim.start_device(switch_profile, port: 1163)
switch_target = "127.0.0.1:1163"
IO.puts("✅ Enterprise switch simulation started on #{switch_target}")
# Test the switch
{:ok, switch_desc} = SNMP.get(switch_target, "sysDescr.0")
{:ok, switch_interfaces} = SNMP.get(switch_target, "ifNumber.0")
IO.puts("Switch: #{switch_desc}")
IO.puts("Interfaces: #{switch_interfaces}")
# Check a few interface statuses
Enum.each([1, 5, 15, 25], fn port ->
case SNMP.get(switch_target, "ifDescr.#{port}") do
{:ok, desc} ->
case SNMP.get(switch_target, "ifOperStatus.#{port}") do
{:ok, status} ->
status_text = if status == 1, do: "UP", else: "DOWN"
IO.puts(" Port #{port}: #{desc} - #{status_text}")
_ -> nil
end
_ -> nil
end
end)
Wireless Access Point Simulation
IO.puts("\n=== Creating Wireless Access Point Simulation ===\n")
# Define a realistic dual-band wireless AP
wireless_ap_oids = %{
# System Group
"1.3.6.1.2.1.1.1.0" => "Ubiquiti UniFi AP AC Pro, Version 4.3.21.11325",
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.41112.1.4.7",
"1.3.6.1.2.1.1.3.0" => %{type: "TimeTicks", value: 0},
"1.3.6.1.2.1.1.4.0" => "Wireless Admin ",
"1.3.6.1.2.1.1.5.0" => "ap-lobby-01",
"1.3.6.1.2.1.1.6.0" => "Main Building - Lobby",
# Wireless interfaces: Management + 2.4GHz + 5GHz
"1.3.6.1.2.1.2.1.0" => 3,
# Management interface (Ethernet)
"1.3.6.1.2.1.2.2.1.2.1" => "eth0 (Management)",
"1.3.6.1.2.1.2.2.1.3.1" => 6, # ethernetCsmacd
"1.3.6.1.2.1.2.2.1.5.1" => %{type: "Gauge32", value: 1000000000}, # 1Gbps
"1.3.6.1.2.1.2.2.1.8.1" => 1,
# 2.4GHz radio
"1.3.6.1.2.1.2.2.1.2.2" => "wlan0 (2.4GHz)",
"1.3.6.1.2.1.2.2.1.3.2" => 71, # ieee80211
"1.3.6.1.2.1.2.2.1.5.2" => %{type: "Gauge32", value: 300000000}, # 300Mbps
"1.3.6.1.2.1.2.2.1.8.2" => 1,
# 5GHz radio
"1.3.6.1.2.1.2.2.1.2.3" => "wlan1 (5GHz)",
"1.3.6.1.2.1.2.2.1.3.3" => 71, # ieee80211
"1.3.6.1.2.1.2.2.1.5.3" => %{type: "Gauge32", value: 1300000000}, # 1.3Gbps
"1.3.6.1.2.1.2.2.1.8.3" => 1,
# Wireless-specific OIDs (simplified)
"1.3.6.1.4.1.41112.1.4.1.1.4.1" => 6, # 2.4GHz channel
"1.3.6.1.4.1.41112.1.4.1.1.4.2" => 36, # 5GHz channel
"1.3.6.1.4.1.41112.1.4.1.1.5.1" => 20, # TX power (dBm)
"1.3.6.1.4.1.41112.1.4.1.1.5.2" => 23, # TX power (dBm)
"1.3.6.1.4.1.41112.1.4.1.1.6.1" => 15, # Connected clients 2.4GHz
"1.3.6.1.4.1.41112.1.4.1.1.6.2" => 8, # Connected clients 5GHz
# Traffic counters
"1.3.6.1.2.1.2.2.1.10.2" => %{type: "Counter32", value: 0}, # 2.4GHz RX
"1.3.6.1.2.1.2.2.1.16.2" => %{type: "Counter32", value: 0}, # 2.4GHz TX
"1.3.6.1.2.1.2.2.1.10.3" => %{type: "Counter32", value: 0}, # 5GHz RX
"1.3.6.1.2.1.2.2.1.16.3" => %{type: "Counter32", value: 0} # 5GHz TX
}
# Create the wireless AP profile
{:ok, wireless_ap_profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(
:wireless_ap,
{:manual, wireless_ap_oids},
behaviors: [:counter_increment, :time_based_changes]
)
# NOTE: If you get port conflicts, restart your Livebook runtime to clean up previous devices
# Try to find an available port starting from 1164
{wireless_ap, wireless_ap_target} =
Enum.reduce_while(1164..1170, nil, fn port, _acc ->
case Sim.start_device(wireless_ap_profile, [port: port, community: "public"]) do
{:ok, device} ->
target = "127.0.0.1:#{port}"
{:halt, {device, target}}
{:error, :eaddrinuse} ->
IO.puts("Port #{port} in use, trying next...")
{:cont, nil}
{:error, reason} ->
IO.puts("Port #{port} failed: #{inspect(reason)}")
{:cont, nil}
end
end) ||
raise "Could not find available port for wireless AP. Try restarting runtime."
IO.puts("✅ Wireless AP simulation started on #{wireless_ap_target}")
# Test the wireless AP
{:ok, ap_desc} = SNMP.get(wireless_ap_target, "sysDescr.0")
{:ok, ap_interfaces} = SNMP.get(wireless_ap_target, "ifNumber.0")
IO.puts("Wireless AP: #{ap_desc}")
IO.puts("Interfaces: #{ap_interfaces}")
# Check wireless-specific data
case SNMP.get(wireless_ap_target, "1.3.6.1.4.1.41112.1.4.1.1.6.1") do
{:ok, clients_24} -> IO.puts("2.4GHz Clients: #{clients_24}")
_ -> IO.puts("2.4GHz Clients: Not available")
end
case SNMP.get(wireless_ap_target, "1.3.6.1.4.1.41112.1.4.1.1.6.2") do
{:ok, clients_5} -> IO.puts("5GHz Clients: #{clients_5}")
_ -> IO.puts("5GHz Clients: Not available")
end
wireless_ap_target
Congratulations! 🎉
You’ve completed the SnmpKit Interactive Tour! You’ve learned how to:
- 🎯 Use the Unified API - Clean, context-based modules for different operations
- 📡 Perform SNMP Operations - GET, WALK, bulk operations, and multi-target queries
- 📚 Work with MIBs - Resolve OIDs, navigate the MIB tree, and understand SNMP data
- 🧪 Create Device Simulations - Build realistic test environments without real hardware
- ⚡ Leverage Advanced Features - Streaming, performance optimization, and analytics
Next Steps
- Explore the Documentation: https://hexdocs.pm/snmpkit
-
Try the Examples: Check out the
examples/
directory for more practical use cases -
Read the Guides:
- MIB Guide - Deep dive into MIB management
- Testing Guide - Testing strategies and patterns
- Build Something Cool: Use SnmpKit in your own projects!
Community
- Issues & Questions: GitHub Issues
- Contributing: Contributing Guide
- Discussions: Share your SnmpKit projects and get help
Happy SNMP monitoring with SnmpKit! 🚀