SnmpKit.SnmpLib.Walker (snmpkit v0.6.4)

Efficient SNMP table walking with bulk operations and streaming support.

This module provides high-performance table walking capabilities using GETBULK operations for maximum efficiency. It's designed for collecting large amounts of SNMP data with minimal network overhead and memory usage.

Features

  • GETBULK Optimization: Uses GETBULK requests for 3-5x faster table walking
  • Streaming Support: Process large tables without loading all data into memory
  • Automatic Pagination: Handles table boundaries and end-of-mib-view conditions
  • Error Recovery: Graceful handling of partial responses and network issues
  • Adaptive Bulk Size: Automatically adjusts bulk size based on device capabilities
  • Memory Efficient: Lazy evaluation and streaming for large datasets

Table Walking Strategies

Uses GETBULK operations for maximum efficiency. Best for SNMPv2c devices.

Sequential Walking

Falls back to GETNEXT operations for SNMPv1 devices or when GETBULK fails.

Streaming Walking

Processes table rows as they arrive, ideal for very large tables.

Examples

# Walk entire interface table
{:ok, interfaces} = SnmpKit.SnmpLib.Walker.walk_table("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2])

# Stream large table to avoid memory issues
SnmpKit.SnmpLib.Walker.stream_table("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2, 1, 2])
|> Stream.each(fn {interface_oid, interface_value} ->
     IO.puts("Interface: " <> inspect(interface_oid) <> " = " <> inspect(interface_value))
   end)
|> Stream.run()

# Walk with custom bulk size and timeout
{:ok, data} = SnmpKit.SnmpLib.Walker.walk_table("10.0.0.1", "1.3.6.1.2.1.4.21",
                                        max_repetitions: 50, timeout: 15_000)

Summary

Functions

Estimates the size of a table by walking just the first column.

Streams table entries as they are retrieved, ideal for very large tables.

Walks a single table column efficiently.

Walks a subtree starting from the given OID.

Walks an entire SNMP table efficiently using GETBULK operations.

Types

host()

@type host() :: binary() | :inet.ip_address()

oid()

@type oid() :: [non_neg_integer()] | binary()

stream_chunk()

@type stream_chunk() :: [varbind()]

varbind()

@type varbind() :: {oid(), any()}

walk_opts()

@type walk_opts() :: [
  community: binary(),
  version: :v1 | :v2c,
  timeout: pos_integer(),
  max_repetitions: pos_integer(),
  max_retries: non_neg_integer(),
  retry_delay: pos_integer(),
  port: pos_integer(),
  adaptive_bulk: boolean(),
  chunk_size: pos_integer()
]

walk_result()

@type walk_result() :: {:ok, [varbind()]} | {:error, any()}

Functions

estimate_table_size(host, table_oid, opts \\ [])

@spec estimate_table_size(host(), oid(), walk_opts()) ::
  {:ok, non_neg_integer()} | {:error, any()}

Estimates the size of a table by walking just the first column.

Useful for determining table size before performing full table walks, helping with memory planning and progress estimation.

Parameters

  • host: Target device IP address or hostname
  • table_oid: Base OID of the table
  • opts: Walking options (typically with small max_repetitions)

Returns

  • {:ok, count}: Estimated number of table rows
  • {:error, reason}: Estimation failed

Examples

# Test that estimate_table_size function exists and handles invalid input properly
iex> match?({:error, _}, SnmpKit.SnmpLib.Walker.estimate_table_size("invalid.host", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100))
true

stream_table(host, table_oid, opts \\ [])

@spec stream_table(host(), oid(), walk_opts()) :: Enumerable.t()

Streams table entries as they are retrieved, ideal for very large tables.

Returns a Stream that yields chunks of varbinds as they are collected. This is memory-efficient for large tables as it doesn't load all data at once.

Parameters

  • host: Target device IP address or hostname
  • table_oid: Base OID of the table to stream
  • opts: Streaming options (chunk_size controls entries per chunk)

Returns

A Stream that yields stream_chunk() (lists of varbinds)

Examples

# Process large routing table in chunks
# SnmpKit.SnmpLib.Walker.stream_table("192.168.1.1", [1, 3, 6, 1, 2, 1, 4, 21])
# |> Stream.flat_map(& &1)  # Flatten chunks into individual varbinds
# |> Stream.filter(fn {_oid, value} -> value != 0 end)  # Filter active routes
# |> Enum.take(100)  # Take first 100 active routes
# returns list of active routes

# Test that stream_table function exists and returns a stream
iex> stream = SnmpKit.SnmpLib.Walker.stream_table("invalid.host", "1.3.6.1.2.1.2.2.1.1", timeout: 100)
iex> is_function(stream, 2)
true

walk_column(host, column_oid, opts \\ [])

@spec walk_column(host(), oid(), walk_opts()) :: walk_result()

Walks a single table column efficiently.

Optimized for retrieving a single column from an SNMP table by using the column OID directly and stopping at table boundaries.

Parameters

  • host: Target device IP address or hostname
  • column_oid: OID of the table column (e.g., [1,3,6,1,2,1,2,2,1,2] for ifDescr)
  • opts: Walking options

Returns

  • {:ok, column_data}: List of {index_oid, value} pairs for the column
  • {:error, reason}: Walking failed

Examples

# Test that walk_column function exists and handles invalid input properly
iex> match?({:error, _}, SnmpKit.SnmpLib.Walker.walk_column("invalid.host", [1, 3, 6, 1, 2, 1, 2, 2, 1, 2], timeout: 100))
true

walk_subtree(host, base_oid, opts \\ [])

@spec walk_subtree(host(), oid(), walk_opts()) :: walk_result()

Walks a subtree starting from the given OID.

Similar to walk_table/3 but continues until the OID prefix no longer matches, making it suitable for walking MIB subtrees that may contain multiple tables.

Parameters

  • host: Target device IP address or hostname
  • base_oid: Starting OID for the subtree walk
  • opts: Walking options

Returns

  • {:ok, varbinds}: All OIDs under the base_oid with their values
  • {:error, reason}: Walking failed

Examples

# Test that walk_subtree function exists and handles invalid input properly
iex> match?({:error, _}, SnmpKit.SnmpLib.Walker.walk_subtree("192.168.255.254", [1, 3, 6, 1, 2, 1, 1], timeout: 100))
true

walk_table(host, table_oid, opts \\ [])

@spec walk_table(host(), oid(), walk_opts()) :: walk_result()

Walks an entire SNMP table efficiently using GETBULK operations.

This is the most efficient way to retrieve a complete SNMP table. Uses GETBULK requests when possible (SNMPv2c) and automatically handles table boundaries.

Parameters

  • host: Target device IP address or hostname
  • table_oid: Base OID of the table to walk
  • opts: Walking options (see module docs)

Returns

  • {:ok, varbinds}: List of {oid, value} pairs for all table entries
  • {:error, reason}: Walking failed with reason

Examples

# Test that walk_table function exists and handles invalid input properly
iex> match?({:error, _}, SnmpKit.SnmpLib.Walker.walk_table("192.168.255.254", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 50))
true

# Walk with high bulk size for faster collection
# SnmpKit.SnmpLib.Walker.walk_table("10.0.0.1", "1.3.6.1.2.1.2.2", max_repetitions: 50)
# {:ok, [...]} returns many interface entries