net-snmp

repository·master·Indexed 42 days ago

https://github.com/markabrahams/node-net-snmp

A Node.js implementation of the Simple Network Management Protocol (SNMP) supporting versions 1, 2c, and 3. It allows developers to create command generators (NMS), command responders (Agents), notification originators/receivers, or proxy forwarders. Version 3.29.1 provides comprehensive support for OID operations, including get, getBulk, and getNext, as well as SNMPv3 security levels and authentication protocols.

Tokens
36.6K
Snippets
103
Records
135
Agent score
45%

What's inside net-snmp

  1. Implement a provider handler

    master

    A handler is a callback function called before a MIB request is processed. It receives a MibRequest object.

    Request Types and Handling:

    • GetRequest, GetNextRequest, GetBulkRequest: To serve dynamic values (like sysUpTime), you must assign the value to mibRequest.instanceNode.value before calling done(). Alternatively, call mib.setScalarValue(providerName, value).

    • SetRequest: The agent validates and casts the value into mibRequest.setValue before the handler runs. The agent commits the value to mibRequest.instanceNode.value automatically during the commit pass. You typically only need to call done() or done({errorStatus: ...}) to reject the set.

    MibRequest Properties:

    • done(error?): Completes the request. Pass an error object like {errorStatus: snmp.ErrorStatus.GeneralError} to signal failure.
    • oid: The instance OID being operated on.
    • operation: The request type (snmp.PduType).
    • setValue / setType: (SetRequest only) The value and type received in the varbind.
    • instanceNode.value: The location to assign read values for Get requests.
    // Example handler for a dynamic scalar
    const handler = (mibRequest) => {
        // For a GetRequest, assign the dynamic value
        mibRequest.instanceNode.value = computeDynamicValue();
        mibRequest.done();
    };
    
    // Example handler for a SetRequest with validation
    const handler = (mibRequest) => {
        if (someValidationFailed) {
            mibRequest.done({ errorStatus: snmp.ErrorStatus.GenErr });
        } else {
            mibRequest.done();
        }
    };
  2. Configure Simple Access Control for Agents

    master

    When an Agent is configured with snmp.AccessControlModelType.Simple, you can use the SimpleAccessControlModel to restrict what communities or users can do.

    Access the model via: var acm = agent.getAuthorizer().getAccessControlModel();

    Access Levels (snmp.AccessLevel):

    • snmp.AccessLevel.None: No access granted.
    • snmp.AccessLevel.ReadOnly: Allows Get, GetNext, and GetBulk requests; denies Set requests.
    • snmp.AccessLevel.ReadWrite: Allows all requests (Get, GetNext, GetBulk, and Set).
    var acm = agent.getAuthorizer().getAccessControlModel();
  3. Configure SNMPv3 Proxying with the Forwarder module

    master

    An Agent instance manages a single Forwarder instance (accessed via agent.getForwarder()). The Forwarder acts as an RFC 3413 proxy application, allowing the agent to forward requests to remote hosts using SNMPv3.

    You add proxies using forwarder.addProxy(options). Each proxy is configured with a context (the SNMPv3 context name) and user credentials for the target host.

    var forwarder = agent.getForwarder();
    forwarder.addProxy({
        context: "slatescontext",
        host: "bedrock",
        user: {
            name: "slate",
            level: snmp.SecurityLevel.authNoPriv,
            authProtocol: snmp.AuthProtocols.sha,
            authKey: "quarryandgravel"
        },
    }); 
    
    // Requests sent to the agent with context "slatescontext" 
    // will now be forwarded to "bedrock" using the "slate" credentials.
  4. Format OID Strings and Varbinds

    master

    The module supports two ways to specify OIDs:

    1. Simple OID Strings: A plain string representing the OID.

      var oid = "1.3.6.1.2.1.1.5.0";
    2. Varbinds: An object containing an OID, its type, and its value. This is used for operations like set().

      var varbind = {
          oid: "1.3.6.1.2.1.1.5.0",
          type: snmp.ObjectType.OctetString,
          value: new Buffer ("host1")
      };

    Varbind Type Rules

    • Boolean: Use JavaScript true or false.
    • Integers (Integer, Counter, Gauge, TimeTicks, Integer32, Counter32, Gauge32, Unsigned32): Use standard JavaScript numbers (e.g., -128 or 100).
    • Counter64: Because JavaScript lacks full 64-bit integer support, these must be handled as Node.js Buffer objects. Users are responsible for producing and consuming these buffers.
    • OID: Use dotted decimal strings (e.g., 1.3.6.1.2.1.1.5.0).
    • IpAddress: Use dotted quad formatted strings (e.g., 192.168.1.1).
    • OctetString / Opaque: Use Node.js Buffer objects. For OctetString, the module accepts JavaScript strings but will always return Buffer objects in callbacks.
    • Error Types: NoSuchObject, NoSuchInstance, and EndOfMibView are used to indicate error conditions.
    var varbind = {
        oid: "1.3.6.1.2.1.1.5.0",
        type: snmp.ObjectType.OctetString,
        value: new Buffer ("host1")
    };
  5. Implement an AgentX Subagent

    master

    An AgentX subagent (RFC 2741) extends a master agent by registering specific MIB regions.

    Crucial Registration Rule: When implementing a subagent, you must use subagent.registerProvider() instead of subagent.getMib().registerProvider(). Using the latter only updates the internal MIB object and fails to send the required Register PDU to the master agent.

    Supported PDUs:

    • Sent by Subagent to Master: Open, Close, Register, Unregister, Notify, Ping, AddAgentCaps, RemoveAgentCaps.
    • Received by Subagent from Master: Get, GetNext, GetBulk, TestSet, CommitSet, UndoSet, CleanupSet.

    Note: IndexAllocate and IndexDeallocate PDUs are currently unsupported.

  6. Configure constraints for SNMP objects

    master

    You can define constraints for integer-based enumerated types, integer ranges, and string sizes in a handler's constraints map or within table column definitions. These constraints are used to validate SetRequest operations; if a value violates a constraint, the operation is not actioned.

    Supported Constraint Types:

    1. Enumerations: Maps specific integer values to labels. Used for Integer types.
    2. Ranges: Limits allowable integer values using an array of maps. Each map can specify min, max, or both. Multiple maps represent a union of allowed values.
    3. Sizes: Limits the length of OCTET STRING types using an array of maps with min and/or max values.
    // Enumeration example
    constraints: {
        enumeration: {
            "1": "goodif",
            "2": "averageif",
            "3": "badif"
        }
    }
    
    // Ranges example (allows 1-3 or 5+)
    constraints: {
        ranges: [
            { min: 1, max: 3 },
            { min: 5 }
        ]
    }
    
    // Sizes example (allows string length 1-3 or 5+)
    constraints: {
        sizes: [
            { min: 1, max: 3 },
            { min: 5 }
        ]
    }
  7. Understand MIB structure and data types

    master

    The Mib instance (accessed via agent.getMib()) is a tree structure holding management information addressed by Object Identifiers (OIDs). There are two primary data structures:

    1. Scalar Data

    Scalar variables are stored at a specific node. The actual value is stored at a child node with the address suffix .0.

    • Example: If sysDescr is at 1.3.6.1.2.1.1.1, its value is at 1.3.6.1.2.1.1.1.0.

    2. Table Data

    Tables store data in columns and rows.

    • A table is identified by its OID.
    • An entry object is typically addressed as 1 directly below the table OID.
    • Columns are numbered (e.g., 1, 2, 3) below the entry.
    • Rows are indexed by one or more columns.
    • Example Hierarchy:
      • 1.3.6.1.2.1.2.2 (Table)
      • 1.3.6.1.2.1.2.2.1 (Entry)
      • 1.3.6.1.2.1.2.2.1.1 (Column 1)
      • 1.3.6.1.2.1.2.2.1.1.1 (Row 1, Column 1 value)
  8. Configure the Simple Access Control Model

    master

    The SimpleAccessControlModel provides three-level access control for communities and users. It is created internally by an Agent's Authorizer when the agent is initialized with accessControlModelType: snmp.AccessControlModelType.Simple.

    Access Levels

    Access levels are defined by snmp.AccessLevel:

    • snmp.AccessLevel.None: No access granted.
    • snmp.AccessLevel.ReadOnly: Allows Get, GetNext, and GetBulk requests, but denies Set requests.
    • snmp.AccessLevel.ReadWrite: Allows all requests (Get, GetNext, GetBulk, and Set).

    Note: By default, newly created communities or users in the Authorizer are assigned ReadOnly access.

    Available Methods

    • setCommunityAccess(community, accessLevel): Grants a specific level to a community.
    • removeCommunityAccess(community): Removes all access for a community.
    • getCommunityAccessLevel(community): Returns the current level for a community.
    • getCommunitiesAccess(): Returns all community access entries.
    • setUserAccess(userName, accessLevel): Grants a specific level to a user.
    • removeUserAccess(userName): Removes all access for a user.
    • getUserAccessLevel(userName): Returns the current level for a user.
    • getUsersAccess(): Returns all user access entries.
    var agent = snmp.createAgent({
        accessControlModelType: snmp.AccessControlModelType.Simple
    }, function (error, data) {});
    
    var authorizer = agent.getAuthorizer();
    authorizer.addCommunity("public");
    authorizer.addCommunity("private");
    authorizer.addUser({
        name: "fred",
        level: snmp.SecurityLevel.noAuthNoPriv
    });
    
    var acm = authorizer.getAccessControlModel();
    acm.setCommunityAccess("public", snmp.AccessLevel.ReadOnly);
    acm.setCommunityAccess("private", snmp.AccessLevel.ReadWrite);
    acm.setUserAccess("fred", snmp.AccessLevel.ReadWrite);
  9. Understand the MIB Structure and Data Types

    master

    An SNMP Agent contains a single Mib instance, accessible via agent.getMib(). The MIB is a tree structure where information is addressed by Object Identifiers (OIDs).

    There are two primary data structures in a MIB:

    1. Scalar Data: Variables stored at a specific node. The value is always stored at the child node with address .0. For example, if a scalar variable is at 1.3.6.1.2.1.1.1, its value is at 1.3.6.1.2.1.1.1.0.
    2. Table Data: Data stored in rows and columns. Tables typically have an entry object (at OID ... .1) which contains columns. Rows are identified by an index (which can be a single column or a combination of columns).
  10. How automatic object creation works for Scalars and Tables

    master

    Automatic creation allows the MIB to instantiate objects on-demand when an agent receives a request for a non-existent instance.

    Scalars

    If a provider's maxAccess is "read-create" (4) and a defVal is defined, the instance is created automatically with that value. You can override this by providing a createHandler(createRequest) function. The handler receives a createRequest object with a provider field and must return the new value or undefined to cancel creation.

    Table Rows

    Rows can be added/deleted if a column has rowStatus: true. When a row is requested via "createAndGo" (4) or "createAndWait" (5) and does not exist, it is created using the defVal of its columns.

    To customize this, provide a createHandler(createRequest) which receives:

    • provider: The table provider.
    • action: "createAndGo" or "createAndWait".
    • row: An array of column indices forming the table index.

    The handler must return an array of column values (one for each column in tableColumns) or undefined to cancel creation. Setting createHandler to null disables automatic row creation entirely.

  11. How AgentX Subagents work

    master

    An AgentX subagent implements RFC 2741 to extend the functionality of an SNMP 'Master Agent'. The subagent registers specific portions of the MIB tree to be managed by the master agent.

    Key Lifecycle & Communication:

    1. Connection: The subagent connects to a master agent using snmp.createSubagent(options).
    2. Session: Use subagent.open() to send an 'Open PDU' and establish a session.
    3. Registration: Use subagent.registerProvider(provider, callback) to register a MIB area. This sends a 'Register PDU' to the master agent. Note: You must use subagent.registerProvider() rather than calling it on the MIB instance directly to ensure the 'Register PDU' is sent to the master agent.
    4. Request Handling: The master agent will send 'Request Processing' PDUs (like Get, GetNext, GetBulk, TestSet, CommitSet) to the subagent, which the subagent handles via the provider's handler.
    5. Termination: Use subagent.close() to send a 'Close PDU' and end the session.
  12. How the Authorizer module works

    master

    The Authorizer is a singleton used by both the Receiver and the Agent to manage authorization lists for SNMP communities (v1/2c) and SNMP users (v3).

    Authorization Logic

    • v1/v2c: If a received community string is not in the authorization list, the receiver/agent returns a RequestFailedError including the sender's rinfo.
    • v3: If a user is not in the list, a RequestFailedError is returned.
    • Note on disableAuthorization: If this option is set, community-based and noAuthNoPriv notifications bypass the list check. However, authNoPriv and authPriv notifications still require the user to be in the list so the library can retrieve the necessary keys for authentication and encryption.

    Access Control Models (Agents only)

    When using snmp.AccessControlModelType.Simple, you can specify access levels for communities or users:

    • none
    • read-only
    • read-write