snmp-discovery
Orb snmp discovery backend, which is a wrapper over NMAP scanner.
Usage
Usage of snmp-discovery:
-diode-app-name-prefix string
diode producer_app_name prefix
-diode-client-id string
diode client ID. Environment variable can be used by wrapping it in ${} (e.g. ${DIODE_CLIENT_ID})
-diode-client-secret string
diode client secret. Environment variable can be used by wrapping it in ${} (e.g. ${DIODE_CLIENT_SECRET})
-diode-target string
diode target. Environment variable can be used by wrapping it in ${} (e.g. ${DIODE_TARGET})
-dry-run
run in dry-run mode, do not ingest data
-dry-run-output-dir string
output dir for dry-run mode. Environment variable can be used by wrapping it in ${} (e.g. ${DRY_RUN_OUTPUT_DIR})
-help
show this help
-host string
server host (default "0.0.0.0")
-log-format string
log format (default "TEXT")
-log-level string
log level (default "INFO")
-otel-endpoint string
OpenTelemetry exporter endpoint (e.g. localhost:4317). Environment variable can be used by wrapping it in ${} (e.g. ${OTEL_ENDPOINT})
-otel-export-period int
Period in seconds between OpenTelemetry exports (default 10)
-port int
server port (default 8070)
Policy RFC
policies:
snmp_network_1:
config:
schedule: "0 */6 * * *" # Cron expression - every 6 hours
timeout: 300 # Timeout for policy in seconds (default 2 minutes)
snmp_timeout: 300 # Timeout for SNMP operations in seconds (default 5 seconds)
snmp_probe_timeout: 1 # Timeout for SNMP probe operations in seconds (default 1 second)
retries: 3 # Number of retries
defaults:
tags: ["snmp-discovery", "orb"]
site: "datacenter-01"
location: "rack-42"
role: "network"
ip_address:
description: "SNMP discovered IP"
role: "management"
tenant: "network-ops"
vrf: "management"
interface:
description: "Auto-discovered interface"
if_type: "ethernet"
device:
description: "SNMP discovered device"
comments: "Automatically discovered via SNMP"
interface_patterns: # (Optional) Custom interface type patterns
- match: "^mgmt-"
type: "1000base-t"
- match: "^uplink-"
type: "100gbase-x-qsfp28"
- match: "^Po\\d+"
type: "lag"
lookup_extensions_dir: "/opt/orb/snmp-extensions" # (Optional) Specifies an override for the directory containing device data yaml files (see below). Defaults to `/etc/snmp-discovery/lookup-extensions
scope:
targets:
- host: "192.168.1.1/24" # subnet support
- host: "192.168.2.1-20" # range support
- host: "10.0.0.1"
port: 162 # Non-standard SNMP port
authentication:
protocol_version: "v2c"
community: "public"
# For SNMPv3, use these fields instead:
# security_level: "authPriv"
# username: "${SNMP_USERNAME}"
# auth_protocol: "SHA"
# auth_passphrase: "${SNMP_AUTH_PASS}"
# priv_protocol: "AES"
# priv_passphrase: "${SNMP_PRIV_PASS}"
**Note:** The following authentication fields support environment variable substitution using the `${VARNAME}` syntax:
- `community`
- `username`
- `auth_passphrase`
- `priv_passphrase`
For example:
```yaml
authentication:
protocol_version: "v3"
security_level: "authPriv"
username: "${SNMP_USERNAME}"
auth_protocol: "SHA"
auth_passphrase: "${SNMP_AUTH_PASS}"
priv_protocol: "AES"
priv_passphrase: "${SNMP_PRIV_PASS}"
If the referenced environment variable is not set, the service will exit with an error.
Per-Target Authentication
SNMP discovery supports both policy-level and per-target authentication, allowing you to use different credentials for different network devices.
How It Works
- Target-level authentication: Specify credentials directly on individual targets
- Policy-level authentication: Define fallback credentials at the scope level
- Automatic fallback: If a target doesn't have authentication, it uses the policy-level credentials
Example Configuration
policies:
mixed_credentials:
config:
defaults:
site: "datacenter-01"
scope:
targets:
# Target with its own SNMPv2c credentials
- host: "192.168.1.1"
port: 161
authentication:
protocol_version: "SNMPv2c"
community: "switch-community"
# Target with its own SNMPv3 credentials
- host: "core-router.example.com"
port: 161
authentication:
protocol_version: "SNMPv3"
security_level: "authPriv"
username: "router-user"
auth_protocol: "SHA"
auth_passphrase: "${ROUTER_AUTH_PASS}"
priv_protocol: "AES"
priv_passphrase: "${ROUTER_PRIV_PASS}"
# Target without auth - uses policy-level fallback
- host: "192.168.1.100"
port: 161
# Policy-level authentication (fallback)
authentication:
protocol_version: "SNMPv2c"
community: "public"
Per-Target Override Defaults
SNMP discovery supports per-target default overrides, allowing you to customize site, role, tags, and other entity defaults for individual targets while maintaining policy-wide defaults as fallbacks.
Example Configuration
policies:
multi_site:
config:
defaults:
site: "New York"
role: "switch"
tags: ["snmp", "network"]
scope:
targets:
# Uses policy defaults
- host: "192.168.1.1"
# Override site and role for this target
- host: "192.168.1.2"
override_defaults:
site: "New York/DC-A"
role: "router"
tags: ["core", "production"]
# Override nested defaults
- host: "192.168.1.3"
override_defaults:
site: "Boston"
device:
description: "Core Router"
interface:
if_type: "1000base-t"
ip_address:
role: "loopback"
# Works with IP ranges - all IPs inherit the override
- host: "192.168.2.0/24"
override_defaults:
site: "Chicago"
role: "access-switch"
authentication:
protocol_version: "SNMPv2c"
community: "public"
Interface Type Pattern Matching
SNMP discovery supports flexible interface type detection through a six-tier priority system that intelligently combines SNMP protocol data with pattern matching:
Priority System
- Subinterface detection (highest priority - structural) - Automatic detection of logical interfaces
- User-defined patterns - Your custom pattern rules
- SNMP ifType mapping - Protocol-specific intelligence from SNMP data
- Built-in patterns - 46 vendor-agnostic patterns included by default
- Speed-based detection - Automatic detection for Ethernet interfaces based on speed
- Default fallback - Configured
if_typeor "other"
This priority order ensures that structural relationships (subinterfaces) are always detected first, followed by user intent, while still leveraging SNMP protocol data and providing intelligent fallbacks.
Configuration
Interface patterns are defined at the defaults level (not under defaults.interface). Patterns use Go regex syntax (RE2):
defaults:
interface:
if_type: "other" # Fallback type for unmatched interfaces
interface_patterns: # At defaults level
- match: "^mgmt-"
type: "1000base-t"
- match: "^uplink-"
type: "100gbase-x-qsfp28"
- match: "^Po\\d+"
type: "lag"
Pattern Rules
- User patterns always win: Your patterns override even SNMP ifType data
- Most specific match wins: Within each priority tier, the longest matching pattern is used
- Case-sensitive: Patterns are matched case-sensitively
- Regex syntax: Uses Go's RE2 regex engine (see syntax reference)
- Invalid patterns: Will cause the policy to fail at load time with a clear error message
Built-in Patterns
The following vendor patterns are included automatically and cover 80-90% of common deployments:
Cisco IOS/IOS-XE:
HundredGig*,FortyGig*,TenGig*,GigabitEthernet*,FastEthernet*TwentyFiveGig*,FiveGig*,TwoGig*
Juniper JunOS:
ge-*,xe-*,et-*(Gigabit, 10G, 40G/100G Ethernet)ae*,lo*(Aggregated Ethernet, Loopback)
Cross-Vendor:
- LAG:
Port-channel*,Bundle-Ether*,ae* - Virtual:
Loopback*,Vlan*,Tunnel*,irb - Management:
Management*,mgmt*,fxp*,em*
Linux/Cumulus:
eth*,ens*,enp*,swp*
See interface_patterns.go for the complete list.
Examples
Override management interface detection:
defaults:
interface_patterns:
- match: "^mgmt-eth"
type: "1000base-t"
Identify uplink interfaces:
defaults:
interface_patterns:
- match: "^(uplink|trunk)-"
type: "100gbase-x-qsfp28"
Custom naming convention:
defaults:
interface_patterns:
- match: "^CORE-"
type: "100gbase-x-qsfp28"
- match: "^ACCESS-"
type: "1000base-t"
- match: "^MGMT-"
type: "1000base-t"
How It Works
For each interface, the system evaluates in order:
- Check for subinterface: If the interface name contains
.or:separators, classify as "virtual" immediately (see Subinterface Detection) - Check user patterns: If any user pattern matches, use that type immediately
- Check SNMP ifType: If SNMP reports a known interface type (e.g., LAG, virtual), use it
- For Ethernet interfaces, if speed is available, use speed-based detection
- Check built-in patterns: Fall back to vendor patterns if no SNMP match
- Use speed if Ethernet: For unknown Ethernet types, infer from interface speed
- Use default: Fall back to
defaults.interface.if_typeor "other"
This ensures maximum flexibility while maintaining intelligent defaults.
Subinterface Detection
SNMP discovery automatically detects and handles subinterfaces (also known as logical interfaces, VLAN interfaces, or sub-interfaces) across all major network vendors.
How It Works
Subinterfaces are identified by the presence of specific separators in the interface name:
- Dot (
.) separator - Common for Cisco, Juniper, Arista, Nokia - Colon (
:) separator - Legacy Juniper style
When a subinterface is detected:
- Type is set to "virtual" - Regardless of SNMP ifType or speed
- Parent interface is tracked - The parent-child relationship is maintained
- Works across all vendors - No vendor-specific configuration needed
Supported Formats
Cisco IOS/IOS-XE:
GigabitEthernet0/0.100 → Parent: GigabitEthernet0/0
TenGigabitEthernet1/1/1.200 → Parent: TenGigabitEthernet1/1/1
Port-channel1.100 → Parent: Port-channel1
Juniper JunOS:
ge-0/0/0.0 → Parent: ge-0/0/0
xe-1/2/3.100 → Parent: xe-1/2/3
ae0.100 → Parent: ae0
ge-0/0/0:0 → Parent: ge-0/0/0 (legacy colon style)
Arista EOS:
Ethernet1/1.100 → Parent: Ethernet1/1
Nokia SROS:
1/1/1.100 → Parent: 1/1/1
Generic/Linux:
eth0.100 → Parent: eth0
eth0:1 → Parent: eth0 (legacy alias style)
Priority System
Subinterface detection operates at Tier 0 (highest priority) in the interface type resolution system:
- Subinterface detection ← Always evaluated first
- User-defined patterns
- SNMP ifType mapping
- Built-in patterns
- Speed-based detection
- Default fallback
This means subinterfaces always receive the "virtual" type, even if:
- SNMP reports a different ifType
- A user pattern would match the interface name
- Speed-based detection would assign a different type
Module / ModuleBay Discovery
Opt-in emission of NetBox Module and ModuleBay entities on modular chassis, sourced from the standard ENTITY-MIB entPhysicalTable. Vendor-neutral — works on any device that populates the MIB (Cisco IOS / IOS-XE / NX-OS, Juniper, Arista, Aruba CX, Nokia SROS, and others).
Controlled by the discover_modules option under config.options:
| Mode | Behaviour |
|---|---|
off (default) | No module / module-bay entities emitted. Zero behaviour change versus prior releases. |
linecards | One ModuleBay + Module per top-level chassis slot — line cards and supervisors only. PSU and fan modules are classified for metric labelling but NOT emitted. |
full | Everything linecards emits, plus one ModuleBay + Module per transceiver sub-bay. Physical interfaces backed by a transceiver carry an Interface.Module reference for per-port optic visibility. |
Virtual-chassis-of-modular targets are supported from day one: when the device reports 2+ chassis members, modules are dispatched per-member using the same chassis inventory the VC path produces.
Sub-bay rendering note (transient, full mode only). Transceiver sub-bays are currently emitted device-rooted rather than nested under the parent line card, due to a Diode reconciler limitation. The transceiver Module still installs into the sub-bay via Module.ModuleBay, so per-port optic visibility is preserved; only the bay-under-linecard visual hierarchy is missing. This will be restored once the reconciler supports single-batch plan-and-apply.
Example
policies:
snmp_modular_chassis:
config:
schedule: "0 */6 * * *"
discover_modules: linecards # off | linecards | full
defaults:
site: "datacenter-01"
scope:
targets:
- host: "10.0.0.1"
authentication:
protocol_version: "v2c"
community: "public"
Device Model Lookup
The lookup_extensions_dir specifies a directory containing device data YAML files that map SNMP device OIDs to human-readable device names. This allows snmp-discovery to provide meaningful device identification instead of raw OID values. This only needs to be set if additional or modified files are being provided instead of the ones that are included with orb-discovery and orb-agent.
File Format
Device lookup files must be in YAML format with a .yaml or .yml extension. Each file should contain a devices section that maps SNMP device OIDs to device names:
devices:
.1.3.6.1.4.1.9.1.1215: ciscoMwr2941DCA
.1.3.6.1.4.1.9.1.489: catalyst2955C12
.1.3.6.1.4.1.9.1.2101: ciscoASR92024TZM
.1.3.6.1.4.1.9.1.2874: ciscoCat930048H
.1.3.6.1.4.1.9.1.2276: ciscoC6840xle
Example Device Lookup Files
The repository includes several pre-built device lookup files for popular vendors. These are included in the orb-discovery and orb-agent images.
- Cisco devices:
cisco.yaml- Contains mappings for Cisco routers, switches, and other networking equipment - TP-Link devices:
tplink.yaml- Contains mappings for TP-Link switches and routers - Dell Networking:
dell-networking.yaml- Contains mappings for Dell networking equipment - Lenovo devices:
lenovo.yaml- Contains mappings for Lenovo networking equipment - Ruckus devices:
ruckus.yaml- Contains mappings for Ruckus wireless equipment
The full list of vendor device files is available here.
Creating Custom Device Lookup Files
You can create custom device lookup files for your specific hardware or to override the name of a device model by:
- Identifying the SNMP device ObjectIDs for your equipment (usually found in vendor MIB files)
- Creating a YAML file with the format shown above. Ensure that ObjectIDs have a
.prefix. - Placing the file in your
lookup_extensions_dirdirectory
# Clone the repository to get device lookup files
git clone https://github.com/netboxlabs/orb-agent.git
cd orb-agent/orb-discovery/snmp-discovery/data/lookup_extensions/
# Copy the files to your lookup extensions directory
cp *.yaml /opt/orb/snmp-extensions/
How It Works
When snmp-discovery encounters a device during scanning, it:
- Retrieves the device's SNMP system object ID (sysObjectID)
- Searches through all YAML files in the
lookup_extensions_dir - If a match is found, uses the human-readable device name instead of the raw OID
- If no match is found, falls back to the original OID value
This provides much more meaningful device identification in your discovery results, making it easier to understand what equipment has been discovered on your network.
Primary IP Assignment
When an SNMP discovery scan completes, snmp-discovery populates the device's primary_ip4 field with the target host address — but only if that address was also discovered as an interface IP during the walk. The verification step avoids recording unreachable or unvalidated addresses in NetBox.
Hostname targets are resolved via DNS once per scan (2s timeout); only IPv4 results are considered for matching. If the target cannot be resolved, is IPv6, or no discovered interface IP matches, the device is emitted with primary_ip4 left unset. Only IPv4 is supported today.
Run snmp-discovery
snmp-discovery can be run by cloning its git repo
git clone https://github.com/netboxlabs/orb-agent.git
cd orb-agent/orb-discovery/snmp-discovery/
make bin
build/snmp-discovery --diode-target grpc://192.168.31.114:8080/diode --diode-client-id '${DIODE_CLIENT_ID}' --diode-client-secret '${DIODE_CLIENT_SECRET}'
⚠️ Warning
Be AWARE that executing a policy with only targets defined will use default SNMP parameters:
- Protocol Version: v2c (if not specified)
- Community: "public" (if not specified for v1/v2c)
- Port: 161 (standard SNMP port, if not specified)
Always ensure proper authentication is configured for production environments to avoid security risks.
Docker Image
snmp-discovery is built from source into the netboxlabs/orb-agent image
(from orb-discovery/snmp-discovery in this repo); there is no standalone
snmp-discovery image. Run it via the agent image, which launches the backend
on demand, or use orb-test-lab for local testing.
Routes (v1)
Get runtime and capabilities information
GET /api/v1/status (gets network runtime data)
GET /api/v1/capabilities (gets snmp-discovery capabilities)
Policies Management
POST /api/v1/policies (Creates a new policy)
Parameters
name type data type description None required YAML object yaml format specified in Policy RFC
Responses
http code content-type response 201application/json; charset=UTF-8{"detail":"policy 'policy_name' was started"}400application/json; charset=UTF-8{ "detail": "invalid Content-Type. Only 'application/x-yaml' is supported" }400application/json; charset=UTF-8Any other policy error 403application/json; charset=UTF-8{ "detail": "config field is required" }409application/json; charset=UTF-8{ "detail": "policy 'policy_name' already exists" }
Example cURL
curl -X POST -H "Content-Type: application/x-yaml" --data-binary @policy.yaml http://localhost:8073/api/v1/policies
DELETE /api/v1/policies/{policy_name} (delete a existing policy)
Parameters
name type data type description policy_namerequired string The unique policy name
Responses
http code content-type response 200application/json; charset=UTF-8{ "detail": "policy 'policy_name' was deleted" }400application/json; charset=UTF-8Any other policy deletion error 404application/json; charset=UTF-8{ "detail": "policy 'policy_name' not found" }
Example cURL
curl -X DELETE http://localhost:8073/api/v1/policies/policy_name