SNMP Fundamentals
SNMP (Simple Network Management Protocol) is the standard protocol network devices use to report status information to monitoring systems, and in some cases to be reconfigured remotely. If you've ever seen a monitoring dashboard showing interface traffic, CPU load, or device uptime, there's a good chance SNMP is how that data got there. It's been the dominant protocol for this job since the late 1980s, and despite newer alternatives (streaming telemetry, NETCONF/YANG, vendor REST APIs), it's still supported on essentially every piece of network equipment made - routers, switches, firewalls, printers, UPS units, and more. That ubiquity is really SNMP's main selling point: it's the one management protocol you can almost always count on being there.
This guide covers the whole foundation at once: the manager/agent model, what MIBs and OIDs actually are, the version landscape, access control, and the core operations a manager can perform. For hands-on tool usage (installing net-snmp, configuring an agent, writing a trap handler), see the Using net-snmp guide; for a deeper dive into MIB internals specifically, see Understanding MIBs; and when something isn't working, Troubleshooting SNMP covers the failure modes in detail.
The basic model: managers and agents
SNMP works on a simple client/server-style relationship, though the terminology is specific to SNMP:
- An agent is software running on the managed device (a switch, a server, a UPS) that knows how to answer questions about that device's current state. On network hardware, the agent is usually built into the device's own operating system (IOS, JunOS, and so on) and can't be separated from it. On general-purpose servers, it's typically a separate installable service - net-snmp's
snmpdis the most common implementation on Linux, and Windows has its own built-in SNMP service. - A manager (also called an NMS, or Network Management Station) is the system that asks those questions - typically a monitoring platform polling dozens, hundreds, or thousands of agents on a regular schedule, collecting values like interface traffic and error counters and usually storing them over time for graphing, alerting, and capacity planning. Familiar examples include Zabbix, LibreNMS, PRTG, and Nagios (often via SNMP-specific plugins), plus purpose-built vendor tools - they all share the same underlying job: send requests, interpret responses, do something useful with the result.
The manager sends a request, the agent replies with the current value. That's the core of it. There's also a second, inverted flow: agents can proactively send unsolicited notifications to a manager when something notable happens (an interface going down, for example), rather than waiting to be asked - covered under TRAP and INFORM below.
It's easy to mix the terms up early on: the agent is on the device being monitored and answers questions; the manager is the monitoring system asking them. A single device is almost always just an agent. A dedicated monitoring platform is almost always just a manager. Some tools genuinely act as both (a monitoring system that also receives and forwards traps from other systems), but that's the exception, not the rule.
An agent doesn't answer just any request from anyone - see access control below for how that's gated. It's also usually configurable in terms of which MIBs are active and which OID subtrees are visible to whom; on net-snmp specifically, this all lives in /etc/snmp/snmpd.conf (covered hands-on in the Using net-snmp guide).
MIBs and OIDs: what's actually being asked for
Every piece of data SNMP can report on has a unique numeric address called an OID (Object Identifier) - a dotted sequence of numbers like 1.3.6.1.2.1.1.1.0. This is genuinely what gets sent over the wire; the protocol itself never transmits human-readable names. A MIB (Management Information Base) is the document that defines what a given OID actually means and gives it a name (that same OID is sysDescr.0, a plain-text description of the device). When you ask a manager to "get CPU utilization from this router," under the hood it's translating that into a specific OID and sending a request for it.
OIDs form a single, enormous global tree, and each number in the sequence is one step down from the root:
1-iso1.3-org1.3.6-dod(US Department of Defense, which originally funded early internet development)1.3.6.1-internet- essentially everything SNMP-relevant lives under here1.3.6.1.2.1-mib-2- the standard MIB-II subtree, defined by RFC 12131.3.6.1.2.1.1-system- basic system info1.3.6.1.2.1.1.1-sysDescr- the specific object1.3.6.1.2.1.1.1.0- the actual instance; the trailing.0marks this as a scalar (single-value) object
Every level exists because someone registered it - either a standards body for the common branches near the root, or an individual vendor for their own private subtree. Nothing about the numbering is arbitrary; it's all formally assigned. You'll sometimes see OIDs written with a word at the start instead of a number - iso.3.6.1.2.1.1.1.0 instead of the numeric form - some tools, particularly trap loggers, print them this way. iso, org, dod, internet, mib-2, enterprises, and a handful of others are just names for specific, fixed positions near the root.
That trailing .0 on a scalar matters. A column in a table works differently: the OID for the column itself isn't queryable on its own - you have to append an instance index identifying which row you want. Table structure and indexing are covered in depth in Understanding MIBs.
Standard MIBs vs. enterprise MIBs: standard MIBs are defined by RFCs and apply across vendors - things like IF-MIB (network interfaces), HOST-RESOURCES-MIB (CPU, memory, storage), and SNMPv2-MIB (basic system info every device should support). Enterprise (private) MIBs are defined by individual vendors for data specific to their own hardware - fan speeds, proprietary counters, custom features. These live under the 1.3.6.1.4.1 (enterprises) branch, with each vendor assigned their own number underneath it (Cisco is 9, Juniper is 2636, and so on) - see Understanding MIBs for the full breakdown of how that numbering works and why it matters for troubleshooting unresolved OIDs.
A device doesn't transmit its MIB over the wire - it only ever sends back raw OIDs and values. The MIB has to live on whatever's doing the asking (or, as with MIB Viewer, be looked up against an already-parsed database) in order to translate those numbers into anything meaningful. This is exactly the problem a tool like MIB Viewer exists to solve: browse a MIB's structure, look up what a specific OID means, without needing the file loaded locally in your own tooling.
Transport: UDP, not TCP
SNMP runs over UDP, traditionally on port 161 for requests (manager asking agent) and port 162 for traps (agent notifying manager). UDP was a deliberate choice: it's lightweight, and a management protocol shouldn't itself become a burden on a device that might already be struggling. The tradeoff is that SNMP has to handle its own retries and timeouts, since UDP doesn't guarantee delivery the way TCP does - which is a large part of why request timeouts are such a common thing to run into (see Troubleshooting SNMP).
SNMP versions
SNMP has three major versions in active use, and they aren't interchangeable - a device configured for v3 won't respond to a v1 request, and vice versa.
SNMPv1 (1988) - the original. Authentication is a single plain-text community string, no encryption, no real access control beyond "knows the string or doesn't." No GETBULK, making large table walks slower. 32-bit counters only, which wrap around much faster on high-speed interfaces - a real problem above roughly 1 Gbps.
SNMPv2c (1996) - the version most commonly deployed in practice ("c" for community-based). Kept v1's simple community-string authentication but added GETBULK (many rows in one exchange instead of one GETNEXT per row), 64-bit counters that wrap far less often, and more specific error codes. What it didn't fix: authentication is still a plain-text community string sent with every request. More capable than v1, but no more secure.
SNMPv3 (1998, still actively maintained) - the first version with real security, built around USM (User-based Security Model): individual users (not a shared string), each verified cryptographically (typically SHA); optional encryption of the payload (typically AES); and view-based access control restricting exactly which parts of the OID tree a given user can see or modify. It has three security levels - noAuthNoPriv (username only, not meaningfully better than v2c), authNoPriv (authenticated, payload still in the clear), and authPriv (authenticated and encrypted - the level actually worth using).
For anything beyond an isolated lab, SNMPv3 with authPriv is the right default today. v2c remains extremely common in practice - simpler to configure, and plenty of internal, trusted-network setups accept the tradeoff deliberately - but should generally be treated as suitable only for networks you'd also trust with unencrypted traffic in general. v1 at this point is mostly a compatibility fallback for old hardware that genuinely doesn't support anything newer. Getting the version wrong when connecting is one of the most common sources of a failed request - see Troubleshooting SNMP for what that looks like.
Access control: community strings
A community string is the access-control mechanism used by SNMPv1 and SNMPv2c - a plain-text string included with every request, functioning something like a shared password. If the string an agent receives doesn't match one it's configured to accept, the request is silently dropped or rejected. By convention (not any technical requirement), public is typically used for read-only access and private for read-write - defaults everyone recognizes, and leaving them unchanged on a production device is a well-known, still-common security mistake.
An agent can be configured with separate strings for different access levels: read-only (RO) permits GET/GETNEXT/GETBULK but can't change anything; read-write (RW) permits all of that plus SET, meaning it can actually reconfigure the device. Read-write strings deserve real caution - many environments deliberately avoid configuring one at all, preferring a dedicated configuration management system for actual changes.
The core problem with community strings generally: they're sent unencrypted, in every request, as literal plain text. Anything with visibility into that traffic can read it directly off the wire and reuse it, and there's no per-user accountability - everyone using the same string looks identical to the agent. This is precisely the gap SNMPv3 was designed to close. Practical guidance: never leave a device on default public/private strings, use something genuinely hard to guess, restrict which source IPs are permitted to use a given string where the platform supports it, and prefer SNMPv3 over community-string-based access entirely where possible, especially for write access.
The core operations
A manager can ask an agent for data a few different ways, and agents can push data back unprompted. All six operations share the same underlying request/response (or fire-and-forget) pattern; what differs is what each one is actually for.
GET
The most basic operation: ask for the current value of one or more specific OIDs. A single GET can bundle multiple OIDs into one request/response exchange rather than one round trip per value.
snmpget -v2c -c public 192.168.1.1 sysDescr.0 sysUpTime.0 sysContact.0
Each OID requested must be exact and existing - typically a scalar instance (ending in .0) or a specific row/column instance in a table. You can't GET a whole table or subtree at once; that's what a walk is for. Behavior on a missing OID differs meaningfully by version: in SNMPv1, if any requested OID doesn't exist, the entire request fails with a single error and you get nothing back, even for the OIDs that were valid. SNMPv2c/v3 fixed this - an individual varbind that doesn't resolve comes back with a specific exception value (noSuchObject or noSuchInstance, covered in Troubleshooting SNMP) while the rest of the response still contains real data.
GETNEXT
Doesn't ask for a specific OID's value directly - it asks for whatever comes immediately after a given OID, in the agent's own tree order. That's what makes it possible to walk an entire subtree without knowing every individual OID in it ahead of time. The classic walking pattern is a loop: start with a base OID, GETNEXT it, get back the next OID and its value, then GETNEXT that OID, and repeat until the returned OID falls outside the subtree you started in.
"Next" means lexicographic order across the whole OID tree, not simply "increment the last digit" - if you GETNEXT the very last OID under one branch, the response can legitimately jump into a completely different, unrelated branch. That's actually how a walking client detects it's gone past the end of what it meant to enumerate: the returned OID no longer starts with the expected prefix. If you GETNEXT past the very last OID the agent has at all, SNMPv2c/v3 return a specific value, endOfMibView, so a client can tell "there's genuinely nothing more" apart from a real error.
GETNEXT works, but it's one request and one response per single value - walking a thousand-row table means a thousand round trips. That's why GETBULK mostly replaced it for real work; GETNEXT is now mainly used for SNMPv1 devices (which don't support GETBULK) or one-off "what comes after this" lookups.
GETBULK
Introduced in SNMPv2c, does the same fundamental job as GETNEXT - retrieve whatever comes next - but can return many results in a single exchange. Only available in v2c and v3; SNMPv1 has to be walked the slower way. A GETBULK request takes two extra parameters: non-repeaters (how many OIDs get a plain, single "next" lookup) and max-repetitions (how many "next" values to return per remaining, repeating OID). For a typical table walk, non-repeaters is usually 0 and max-repetitions something like 10-50:
snmpbulkwalk -v2c -c public -Cn0 -Cr20 192.168.1.1 1.3.6.1.2.1.2.2.1.2
Most tools (like snmpbulkwalk above) handle the tuning automatically. Set max-repetitions too low and you lose most of the efficiency benefit; too high and a single response can exceed practical packet size limits, causing truncation or outright failure on constrained hardware - most tooling defaults to something reasonable (10-25 or so) and it's rarely worth tuning manually. If a device doesn't support GETBULK at all, well-behaved tooling falls back to GETNEXT automatically.
SET
The one core operation that changes something on the device, rather than just reading its current state:
snmpset -v2c -c private 192.168.1.1 1.3.6.1.2.1.2.2.1.7.1 i 2
That example sets ifAdminStatus for interface 1 to 2 (down) - a real, consequential change. The i tells the tool the value is an integer; SET requires specifying the correct data type for whatever's being written. Whether an object accepts SET at all is defined in the MIB's MAX-ACCESS clause - attempting to SET a read-only object fails regardless of your own access permissions, since the restriction is defined by the object itself. Because SET can actually reconfigure a device, write access deserves far more caution than read access - many environments deliberately configure only a read-only community and skip read-write entirely. A failed SET usually comes back with a specific, informative error (noAccess/notWritable if it can't be written to, wrongType/wrongValue if the data doesn't match), worth reading carefully rather than just retrying.
Beyond changing a scalar, SET is also how table rows get created and deleted, via a special column called RowStatus - a more involved, multi-step process covered in Understanding MIBs.
TRAP and INFORM
Both are notifications an agent sends on its own initiative, without being asked - the reverse of the usual manager-asks-first flow. Sent over UDP, conventionally to port 162 on the manager. The difference between them is reliability: a TRAP is fire-and-forget - the agent sends it once, with no acknowledgment, no confirmation of receipt, and no automatic retry. A dropped packet, a manager that's briefly down, a firewall rule blocking port 162 - the notification simply never arrives, and the agent has no way to know that happened. An INFORM does the same job but is acknowledged: the receiving manager confirms receipt, and if the sender doesn't get that confirmation in time, it retries - the same reliability an ordinary request/response already has. INFORM was introduced alongside GETBULK in SNMPv2c and is also available in v3; SNMPv1 has no equivalent at all.
That reliability isn't free - sending an INFORM means holding onto it, tracking that it's awaiting acknowledgment, re-sending on a timer, eventually giving up after some number of attempts, rather than firing a UDP packet and moving on. The practical rule of thumb: if losing this specific notification would genuinely matter - a security event, a hardware failure - INFORM is worth the overhead. For high-frequency, lower-stakes notifications where an occasional missed message isn't a real problem, a plain trap is the lighter-weight choice. Many real deployments use both, chosen per notification type.
The message format differs between versions too: SNMPv1 traps use a fixed, predefined set of generic types (coldStart, linkDown, linkUp, and a few others, plus a catch-all enterpriseSpecific). SNMPv2c/v3 instead identify which specific notification fired via a special varbind, snmpTrapOID, whose value is the OID of a notification type defined in some MIB - a much more flexible, MIB-defined way to describe what happened. Either way, a trap arrives as a bundle of OIDs and values just like any other SNMP data, and just as meaningless without the right MIB loaded to translate them - see Understanding MIBs for how notifications are actually defined, and the Using net-snmp guide for receiving and handling them in practice with snmptrapd.
An agent has to be explicitly told where to send traps or informs - a destination IP, port, and (for v1/v2c) the community string to include, or (for v3) the target user and security parameters. Without this configured, a device won't send anything unprompted, no matter what happens to it, even if it fully supports notifications in principle.