Access to SNMP

This is the core part of LuaSNMP. It provides routines for the following purposes:

session, err = snmp.open([CONFIG])

The Function snmp.open is used for creating, or opening, SNMP sessions. It is a Lua constructor that validates the provided session configuration parameters given as a Lua table CONFIG, creates a Lua table for storing them, and allocates resources for controlling the SNMP operations invoked for the session. Default values are used when one, or more, configuration parameters are omitted.

Default values are provided by the following sources with the stated precedence:

  1. Given CONFIG table
  2. Net-SNMP configuration files snmp.conf (see snmp.conf(5) manual page for details)
  3. LuaSNMP default values

Lua SNMP default values:

All other unconfigured parameters are either set to nil or have no meaning for a version 2 session.

If a new Shttp://lua-users.org/lists/lua-l/http://lua-users.org/lists/lua-l/http://lua-users.org/lists/lua-l/NMP session is successfully created, snmp.open returns a reference to this session. The returned reference must be specified as a handle in all invocations of SNMP operations for the session.

If the provided configuration is invalid or an Net-SNMP internal error occurs, no session is created. In this case, snmp.open  returns nil plus an error message.

Note, that all SNMP set and get functions are also mapped to a successfully created session, which allows to perform SNMP requests as a direct method call to a session. See also below.

The following example illustrates the creation of an SNMP session that can be used by a management application for invoking SNMP operations on the agent identified by its host name "goofy". All SNMP messages sent to the agent will use the community string "private".  SNMP version 2c (default value). A user-defined function trap_cb will be called whenever an SNMP trap originated by this agent is received. Another user defined function default_cb is called for asynchronous requests (see snmp.asynch_get)

hub1, err = snmp.open{
peer = "goofy",
community = "private",
trap = trap_cb,
callback = default_cb,
}
assert(hub1, err)

session, err = snmp.clone(PARENT [, CONFIG])
session, err = PARENT:clone([CONFIG])

Clones a valid session. The new session inherits all configuration parameters from it's parent session PARENT. If a configuration table CONFIG is provided, this configuration table overrides the parent's configuration.

After successful creation the new session is completely independent from the parent session. In case of an error, the function snmp.clone returns nil and an error message.

hub2, err = snmp.clone(hub1, {peer = "localhost"})
assert(hub2, err)
-- or simpler
hub2, err = hub1:clone{peer = "localhost"}
-- or event simpler including error handling
hub2 = assert(hub1:clone{peer = "localhost"})

retval = snmp.getversion(SESSION) 
retval = SESSION:getversion()

Returns the SNMP version of the given SESSION as string. If the session does not exist or does not own a valid valid version configuration parameter, the function returns nil plus an error message.

retval = snmp.gettrapd()

Returns a string with the name of the trapdaemon for which LuaSNMP has been configured during compile time. The string contains either "straps" or "snmptrapd" (default).

err = snmp.close(SESSION)
err = SESSION:close()

Terminates an active SNMP session. The function receives the SESSION previously created by snmp.open or snmp.close as handle. The function returns 1 on success. In case of an error the function returns an nil plus an error message.

In the following example, the SNMP session previously described is terminated.

-- Form 1
err = snmp.close(hub1)
-- Simplified form:
err = hub1:close()

NOTE: If the sssion that shall be closed is waiting for an asynchronous request (either get or set) to complete, the session is not immediately closed, but put into a queue and will be closed once the request has completed. Any associated callback function (either default or request specific) is called before the session is actually closed and all it's data is garbage collected.

Error codes returned by snmp.close  can be safely discarded.

varbind, err, errindex = snmp.get(SESSION, VARS)
varbind, err, errindex = SESSION:get(VARS)

The function snmp.get  is used to perform SNMP get operations for the specified SESSION. One or more MIB variable instances can be retieved by a single call. 

While snmp.get  performs a synchronous get operation which returns only when the operation completes,  the function snmp.asynch_get (see below) operates asynchronous and returns immediately to the caller. 

The first argument SESSION is a reference to an SNMP session, returned from a previous call to snmp.open or snmp.clone. The second argument VARS identifies the MIB variable instance(s) to be retrieved.

A single MIB variable instance can be identified by:

A string containing either an object identifier (OID in dotted notation) or the name of the variable to be retrieved. Notice that an instance identifier must always be specified. As an example, both strings "sysName.0" and "1.3.6.1.2.1.1.4.0" can be used for retrieving the current value of the MIB-II variable sysName.

A list of MIB variable instances to be retrieved can be identified by:

snmp.get  returns a varbind table or a varbind list, if the operation completes successfully. In case of errors detected locally the function returns nil plus an error message. In case of errors detected by the agent the returned varbind list may be empty or may contain less values as requested, if one or more values couldn't be retrieved by the agent. Two additional values are then returned: a string containing the error message and an error index indicating the first instance that couldn't be retrieved.

If a varbind list is returned, each element of this varbind list is a fully specified varbind table. Please notice that the oid attributes in the varbind tables returned by snmp.get  always contain object identifiers in dotted notation.

The following example illustrates the use of snmp.get  for retrieving the values of some of the variables in the system group of MIB-II:

vlist, err, index = snmp.get(hub1, {"sysName.0","sysContact.0"}) 
if not err then
print(string.format("Contact for %s : %s",
vlist[1].value, vlist[2].value))
else
if index then
print(string.format("Error : %s in index %d", err, index))
else
print(string.format("Error : %s", err))
end
end

For convenience, LuaSNMP provides a means to perform SNMP requests in form of a method call to a valid session:

-- Request single varbind
vbind, err = hub1:get("sysContact.0")

-- Request list of varbinds
vlist, err, index = hub1:get{"sysName.0","sysContact.0"}
...

reqid, err = snmp.asynch_get(SESSION, VARS [,CALLBACK [,MAGIC]])
reqid, err = SESSION:asynch_get(VARS [,CALLBACK [,MAGIC]])

The function snmp.asynch_get  performs the same operation as snmp.get. However, instead of waiting for the request to complete, the function returns immediately after sending a get-request message to the agent. When this request completes,  a user-defined CALLBACK function is invoked. 

The  CALLBACK argument is optional. When present, it specifies a user-defined callback function to be invoked when the asynchronous operation completes. When this argument is omitted, the default callback function, specified when the SNMP session is created, is used.

Each asynchronous operation can be associated with an opaque value MAGIC, which is transparently transported to the callback function. Note, that you can define a request specific magic value even if the request utilises the session's default callback function.

snmp.asynch_get  returns a number containing the request id, if the request started successfully. If not, the function returns nil plus an error message.

When the get operation completes, the user-defined CALLBACK function is invoked, receiving up to  6 arguments. The first three arguments are similar to the values returned from snmp.get: a varbind table (or a varbind list), a string containint the error status or nil, and an error index, if an error occurred. The fourth argument is a number with the request-id associated to the completed asynchronous request. The fifth  argument contains a reference to the SNMP session. The last argument is the opaque magic value, which can be of any type. 

If no response is received, LuaSNMP performs the configured number of retries for the request. If none of these retries succeeds, the callback function is called with an error status indicating a timeout.

The following example illustrates the use of snmp.asynch_get  in a very simple application. In this example, we assume that function poll is called periodically. Based on a list of records that describes a group of SNMP agents (the Lua table agents), function poll invokes asynchronous get operations for testing these agents' current status. Another Lua table (polled) maintains associations between pending requests and agents. The callback function poll_cb is invoked when an asynchronous request is completed. For simplicity, error conditions are mostly ignored.

local snmp = require "snmp"

polled = {}
local count = 3

local function poll_cb(vb, err, index, reqid, session, magic)
local agent = polled[reqid]
polled[reqid] = nil
if vb then
agent.status = "alive"
else
if err == "snmp: timeout" then
agent.status = "no response"
else
agent.status = "?"
end
end
count = count - 1
end

function poll(agents)
local i, agent = next(agents,nil)
while i do
local reqid = snmp.asynch_get(agent.session,
"sysUpTime.0", poll_cb)
if reqid then
polled[reqid] = agent
end
i, agent = next(agents,i)
end
end

local agents = {
{ session = snmp.open{peer = "goofy"}, name = "goofy" },
{ session = snmp.open{peer = "192.168.99.1"}, name = "192.168.99.1"},
{ session = snmp.open{peer = "localhost"}, name = "localhost" }
}


poll(agents)
while count > 0 do
snmp.event()
end

for _, agent in ipairs(agents) do
print(string.format("Agent status of %s: %s",
agent.name, agent.status or 'failure'))
agent.session:close()
end

Since only the first value returned by snmp.asynch_get  is necessary (the request-id), the remaining values can be  discarded. Lua adjusts the list of returned values at run-time. Lua also dynamically adjusts the list of arguments when a function is called. As the sessionindex and magic arguments of the callback function are not used in this implementation, the definition of poll_cb could be simplified to:

function poll_cb(vb, err, _, reqid)
...
end

varbind, err, errindex = snmp.getnext(SESSION, VARS)
varbind, err, errindex = SESSION:getnext(VARS)

The function snmp.getnext  is used to perform SNMP get-next operations for the specified session. It retrieves the values of the lexicographical successors of a list of MIB objects.

While snmp.getnext  performs a synchronous get-next operation, which returns only when the operation completes, the function snmp.asynch_getnext operates asynchronously and returns immediately to the caller.

The first argument SESSION is a reference to a valid SNMP session. The second argument VARS identifies one or more MIB objects. MIB objects are identified as described for snmp.get. A single object is identified by a string or by a varbind table, containing either an object identifier or a MIB label, like "1.3.6.1.2.1.1" or "system". Note that instance identifiers are not necessary. A list of objects is identified by a list of strings or by a varbind list.

snmp.getnext  returns a varbind table  or a varbind list, if the operation completes successfully. In case of errors detected locally the function returns nil plus an error message. In case of errors detected by the agent the returned varbind list may be empty or may contain less values as requested, if one or more values couldn't be retrieved by the agent. Two additional values are then returned: a string containing the error message and an error index indicating the first instance that couldn't be retrieved.

If a varbind list is returned, each element of this varbind list is a fully specified varbind table. Please notice that the oid attributes in the varbind tables returned by snmp.getnext  always contain object identifiers in dotted notation.

The following example illustrates the use of snmp.getnext  for retrieving the whole MIB tree implemented by an agent.

require "snmp"
hub1 = assert(snmp.open{peer = "goofy"})
repeat
vb, err = snmp.getnext (hub1, vb or {oid = "1"})
if not err and vb.type ~= snmp.ENDOFMIBVIEW then
print(snmp.sprintvar(vb))
end
until vb.type == snmp.ENDOFMIBVIEW

reqid, err = snmp.asynch_getnext(SESSION, VARS, [,CALLBACK [,MAGIC]])
reqid, err = SESSION:asynch_getnext(VARS [,CALLBACK [, MAGIC]])

The function snmp.asynch_getnext  performs the same operation as snmp.getnext . However, instead of waiting for the request to complete, the function returns immediately after sending a getnext-request message to the agent. When this request completes,  a user-defined CALLBACK function is invoked. 

The  CALLBACK argument is optional. When present, it specifies a user-defined callback function to be invoked when the asynchronous operation completes. When this argument is omitted, the default callback function, specified when the SNMP session is created, is used.

Each asynchronous operation can be associated with an opaque value MAGIC, which is transparently transported to the callback function. Note, that you can define a request specific magic value even if the request utilises the session's default callback function.

snmp.asynch_getnext  returns a number containing the request id, if the request started successfully. If not, the function returns nil plus an error message.

When the get operation completes, the user-defined CALLBACK function is invoked, receiving up to  6 arguments. The first three arguments are similar to the values returned from snmp.get: a varbind table (or a varbind list), a string containint the error status or nil, and an error index, if an error occurred. The fourth argument is a number with the request-id associated to the completed asynchronous request. The fifth  argument contains a reference to the SNMP session. The last argument is the opaque magic value, which can be of any type. 

If no response is received, LuaSNMP performs the configured number of retries for the request. If none of these retries succeeds, the callback function is called with an error status indicating a timeout.

The following example illustrates an asynchronous version of the MIB traversal shown above. 

require "snmp"

local count = 0

hub1 = assert(snmp.open{peer = "goofy"})

function next_cb(vb, err, _, _, session)
if not err and vb.type ~= snmp.ENDOFMIBVIEW then
count = count + 1
print(snmp.sprintvar(vb))
session:asynch_getnext(vb, next_cb)
else
print(string.format("%d object instances retrieved", count))
os.exit(0)
end
end

-- First call initialises retrieval of complete tree.
hub1:asynch_getnext("1", next_cb)
snmp.loop()

varbind, err, errindex = snmp.set(SESSION, VARBIND | VARLIST)
varbind, err, errindex = SESSION:set(VARBIND | VARLIST)

The function snmp.set  is used to perform SNMP set operations for the specified SESSION. One or more MIB variable instances can be modified by a single call. 

While snmp.set  performs a synchronous set operation which returns only when the operation completes,  the function snmp.asynch_set (see below) operates asynchronous and returns immediately to the caller. 

The first argument SESSION is a reference to an SNMP session, returned from a previous call to snmp.open or snmp.clone. The second argument VARBIND is varbind table. You can also specificy a varbind list VARLIST. See varbinds for details. The type field is optional.

The function returns the given varbind or varlist as returned by the SNMP agent. See snmp.get for more details. An error message is additionally returned in case of errors.

The following example illustrates the use of snmp.set :

local snmp = require "snmp"

hub1, err = snmp.open{
  peer = "goofy",
  community = "private",
}
assert(hub1, err)

vbIn = {
  {oid = "sysContact.0", value = "root"},  
{oid = "sysLocation.0", value="MyHome"}
}
vbOut, err, index = hub1:set(vbIn)
assert(vbOut, err)
print(vbOut[1])
print(vbOut[2])

Note, that there is a metatable attached to the varbinds returned in the varbind list, which allows to convert the varbind into a readable string using the tostring  Lua function, which is implicitly called by print. Here is the output of the script above:

luasnmp$ lua doc/examples/set.lua
SNMPv2-MIB::sysContact.0 = STRING: root
SNMPv2-MIB::sysLocation.0 = STRING: MyHome

reqid, err = snmp.asynch_set(SESSION, VARBIND | VARLIST [,CALLBACK [, MAGIC]])
reqid, err = SESSION:set(VARBIND | VARLIST [,CALLBACK [, MAGIC]])

The function snmp.asynch_get  performs the same operation as snmp.get. However, instead of waiting for the request to complete, the function returns immediately after sending a get-request message to the agent. When this request completes,  a user-defined CALLBACK function is invoked. 

Refer to snmp.asynch_get for more details on asychronous operation.

varbind, err = snmp.getbulk(SESSION, NR, MR, VARS)
varbind, err = SESSION:getbulk(NR, MR, VARS)

The functions snmp.getbulk  performs a SNMPv2 get-bulk operation for the specified session. The function is not available for SNMPv1 sessions. Similarly to get-next, a get-bulk operation retrieves the lexicographical successors of a list of MIB objects. The difference is that, by using get-bulk operations, multiple lexicographical successors of a MIB object can be retrieved in a single protocol operation.

While snmp.getbulk  performs a synchronous get-bulk operation, which returns only when the operation completes, the function snmp.asynch_getbulk operates asynchronously and returns immediately to the caller.

The first argument SESSION of snmp.getbulk  is a reference to a valid SNMP session. The fourth argument, VARS, identifies a list of MIB objects, as described for snmp.getnext (a list of strings or a varbind list). The other two arguments, NR and MR, specify the number of lexicographical successors to be retrieved in the following way:

snmp.getbulk  returns a varbind table  or a varbind list, if the operation completes successfully. In case of errors detected locally the function returns nil plus an error message. In case of errors detected by the agent the returned varbind list may be empty or may contain less values as requested, if one or more values couldn't be retrieved by the agent. Two additional values are then returned: a string containing the error message and an error index indicating the first instance that couldn't be retrieved.

If a varbind list is returned, each element of this varbind list is a fully specified varbind table. Please notice that the oid attributes in the varbind tables returned by snmp.getbulk  always contain object identifiers in dotted notation.

The following example illustrates the use of snmp_getbulk. A single invokation of snmp_getbulk retrieves the description and type of the first three interfaces of the system. Note in this example the nr argument set to 0. 

local snmp = require "snmp"

local ifNum = 3

hub1 = assert(snmp.open{peer = "goofy"})

ifList, err, index = hub1:getbulk(0, ifNum, {"ifDescr","ifType"})
assert(ifList, err)

local types = snmp.mib.enums("ifType")
i = 1; last=ifNum * 2
while i < last do
print(ifList[i].value..": "..types[ifList[i+1].value])
i = i + 2
end

table.foreach(ifList, print)

The above script produces the following output.

luasnmp$ lua doc/examples/getbulk.lua
lo: softwareLoopback
eth0: ethernetCsmacd
eth1: ethernetCsmacd
1 IF-MIB::ifDescr.1 = STRING: lo
2 IF-MIB::ifType.1 = INTEGER: softwareLoopback(24)
3 IF-MIB::ifDescr.2 = STRING: eth0
4 IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)
5 IF-MIB::ifDescr.3 = STRING: eth1
6 IF-MIB::ifType.3 = INTEGER: ethernetCsmacd(6)

reqid, err = snmp.asynch_getbulk(SESSION, NR, MR, VARS, [,CALLBACK [,MAGIC]])
reqid, err = SESSION:asynch_getbulk(VARS, NR, MR [,CALLBACK [, MAGIC]])

The function snmp.asynch_getbulk  performs the same operation as snmp.getbulk. However, instead of waiting for the request to complete, the function returns immediately after sending a getnext-request message to the agent. When this request completes,  a user-defined CALLBACK function is invoked. 

The  CALLBACK argument is optional. When present, it specifies a user-defined callback function to be invoked when the asynchronous operation completes. When this argument is omitted, the default callback function, specified when the SNMP session is created, is used.

Each asynchronous operation can be associated with an opaque value MAGIC, which is transparently transported to the callback function. Note, that you can define a request specific magic value even if the request utilises the session's default callback function.

snmp.asynch_getbulk  returns a number containing the request id, if the request started successfully. If not, the function returns nil plus an error message.

When the  operation completes, the user-defined CALLBACK function is invoked, receiving up to  6 arguments. The first three arguments are similar to the values returned from snmp.get: a varbind table (or a varbind list), a string containint the error status or nil, and an error index, if an error occurred. The fourth argument is a number with the request-id associated to the completed asynchronous request. The fifth  argument contains a reference to the SNMP session. The last argument is the opaque magic value, which can be of any type. 

If no response is received, LuaSNMP performs the configured number of retries for the request. If none of these retries succeeds, the callback function is called with an error status indicating a timeout.

vlist, err, index = snmp.walk(SESSION, NODE)
vlist, err, index = SESSION:walk(NODE)

The function snmp.walk  uses get-next SNMP primitives to query an agent for a tree of information. It behaves the same way as the NetSNMP command snmpwalk(1) does. 

The first parameter SESSION is the reference to a valid SNMP session. The second parameter NODE specifies which portion of the object identifier space will be searched. If NODE is not specified snmp.walk will search in SNMPv2-MIB::mib-2.

The function returns a list of varbinds, if the operation succeeds. In case of an error the list of varbinds may be nil or partially filled. An error message is additionally returned.

Here is an example that prints a list of interface types for all interfaces present in the system:

require "snmp"

hub1 = assert(snmp.open{peer = "goofy"})

vlist = assert(hub1:walk("ifType"))
table.foreach(vlist, function(k,v) print(v) end)

Here is the output:

IF-MIB::ifType.1 = INTEGER: softwareLoopback(24)
IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.3 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.4 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.5 = INTEGER: tunnel(131)

Normally snmp.walk  uses get-next requests starting with the OID you specified  and returns  all  results  in  the  MIB subtree rooted at that OID. If you want to include the OID specified in the NODE parameter on the commandline, configure the session with the parameter includeroot = true.

If you do so, the example above produces the following output.

IF-MIB::ifType = No Such Instance currently exists at this OID
IF-MIB::ifType.1 = INTEGER: softwareLoopback(24)
IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.3 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.4 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifType.5 = INTEGER: tunnel(131)

vlist, err, errindex = snmp.inform(SESSION, TRAPOID, VARLIST)
vlist, err, errindex = SESSION:inform(TRAPOID, VARLIST)

The function snmp.inform sends a solicited trap to another SNMP manager. 

While snmp.inform  performs a synchronous set operation which returns only when the operation completes,  the function snmp.asynch_inform (see below) operates asynchronous and returns immediately to the caller. 

The first parameter SESSION defines a valid SNMP session, which must have been configured to send on a port on which a trap sink listens for traps. This will be typically port 162. The second parameter TRAPOID identifies the trap to be sent. The third parameter VARLIST is a varbind list containing additional information for the recipient.

Example without trap callback:

require "snmp"

hub1 = assert(snmp.open{peer = "localhost"})
hub1trap = hub1:clone{port = 162}

local vlist = assert(hub1:get{"sysContact.0", "sysUpTime.0"})
local result, err = hub1trap:inform("sysName.0", vlist)
table.foreach(result, print)

This will produce the following output:

luasnmp$ lua doc/examples/inform.lua
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (0) 0:00:00.00
SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-MIB::sysName.0
SNMPv2-MIB::sysContact.0 = STRING: root
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (5199693) 14:26:36.93

reqid, err = snmp.asynch_inform(SESSION, TRAPOID, VARLIST [,CALLBACK [, MAGIC]])
reqid, err = SESSION:asynch_inform(TRAPOID, VARLIST [,CALLBACK [, MAGIC]])

The function snmp.asynch_inform  performs the same operation as snmp.inform. However, instead of waiting for the request to complete, the function returns immediately after sending a getnext-request message to the agent. When this request completes,  a user-defined CALLBACK function is invoked. 

The  CALLBACK argument is optional. When present, it specifies a user-defined callback function to be invoked when the asynchronous operation completes. When this argument is omitted, the default callback function, specified when the SNMP session is created, is used.

Each asynchronous operation can be associated with an opaque value MAGIC, which is transparently transported to the callback function. Note, that you can define a request specific magic value even if the request utilises the session's default callback function.

snmp.asynch_inform returns a number containing the request id, if the request started successfully. If not, the function returns nil plus an error message.

When the  operation completes, the user-defined CALLBACK function is invoked, receiving up to  6 arguments. The first three arguments are similar to the values returned from snmp.get: a varbind table (or a varbind list), a string containint the error status or nil, and an error index, if an error occurred. The fourth argument is a number with the request-id associated to the completed asynchronous request. The fifth  argument contains a reference to the SNMP session. The last argument is the opaque magic value, which can be of any type. 

err = snmp.wait(SESSION)
err = SESSION:wait()

The function snmp.wait waits until all asynchronous events pending for the given SESSION have completed. The function receives a reference to a valid SESSION as parameter. Note, that although snmp.wait waits until ALL pending events of the GIVEN session have completed, responses of other session are processed as well.

The example script and it's output show the behaviour of snmp.wait. During the wait phase of hub1 the first request of hub2 is also processed. Since there is only one request from hub1, hub1:wait  completes after this and the second request of hub2 is process during hub2:wait 

local snmp = require "snmp"

function func_cb(vb, status, index, reqid, session, magic)
if magic == "goofy" then
print("Callback: from goofy")
else
print("Callback: from localhost")
end
print(vb)
end

hub1 = assert(snmp.open{peer = "goofy"})
hub2 = assert(hub1:clone{peer = "localhost"})

local reqid1 = hub1:asynch_get("sysContact.0", func_cb, "goofy")
local reqid2 = hub2:asynch_get("sysContact.0", func_cb, "localhost")
local reqid2 = hub2:asynch_get("sysName.0", func_cb, "localhost")

print("Waiting for hub1 ...")
hub1:wait()
print("Waiting for hub2 ...")
hub2:wait()

Output:

luasnmp$ lua doc/examples/wait.lua
Waiting for hub1 ...
Callback: from localhost
SNMPv2-MIB::sysContact.0 = STRING: root
Callback: from goofy
SNMPv2-MIB::sysContact.0 = STRING: root
Waiting for hub2 ...
Callback: from localhost
SNMPv2-MIB::sysName.0 = STRING: goofy

snmp.idle()
snmp.loop()

The function snmp.loop or snmp.idle provides an event loop. A LuaSNMP scripts remains in snmp.loop as long as there are uncompleted asynchronous operations and as long as there is a session with a trap callback function waiting for trap notifications. The function returns, when all asynchronous requests are completed and all trap capturing sessions have been closed.

snmp.event()

The function snmp.event checks for incoming responses from agents. It does NOT wait for responses. The function is useful in case you want to wait for different events (e.g. from a GUI) in a single loop.

while true do
- Look for SNMP responses and invoke callback functions accordingly.
snmp.event()
- Wait for events from other packages
...
end

SNMPv3 User Management

var, err = snmp.createuser(SESSION, USER [,CLONEFROM])

The function snmp.createuser creates a new SNMPv3 USER  on the remote machine, which is reachable via the given SESSION. The newly created user will not be active until an existing CLONEFROM user has been assigned to USER. Cloning assigns authentication parameters from the existing user CLONEFROM to USER. This can happen in 2 ways:

The function returns a varbind (CLONEFROM omitted) containing the object usmUserStatus or a varlist containing the objects userCloneFrom and userSecurityName. If an error occurs the varbind or varlist may be incomplete or nil and  the function also returns  an error message.

Example:

local snmp = require "snmp"
local mib = snmp.mib

--
-- We will use this frequently
--
local check = snmp.check

--
-- Lets define a convenient print function
--
function printf(fmt, ...)
print(string.format(fmt, unpack(arg)))
end

--
-- User to create
--
local user = "popey"

--
-- User to clone from
--
local clonefromuser = "ronja"

--
-- Open a working session
--
local sess, err = snmp.open{
peer = "goofy",
version = snmp.SNMPv3,
user = "leuwer",
password = "leuwer2006"
}
check(sess, err)

--
-- Create the user 'popey'
--
local vl, err = check(sess:createuser(user, clonefromuser))
for _,v in ipairs(vl) do print(v) end

--
-- Read and print popey's usmUserStatus
--
print(check(sess:get(mib.oid("usmUserStatus") .. snmp.instance(sess.contextEngineID, user))))

--
-- Delete the user again
--
local vb, err = check(sess:deleteuser(user))

--
-- Read and print popey's usmUserStatus
--
print(check(sess:get(mib.oid("usmUserStatus") .. mib.instance(sess.contextEngineID, user))))

--
-- Close the working session
--
check(not sess:close())

varlist, err = snmp.clonefromuser(SESSION, USER, CLONEFROM)

The function snmp.clonefromuser clones authentication parameters from an exisiting and active user CLONEFROM to a newly created USER. Note, that both users must exist before snmp.clonefromuser is called.

The function returns a varlist containing the objects  userCloneFrom and userSecurityName. If an error occurs the varlist may not be complete or even nil and the function also returns an error message.

varbind, err = snmp.deleteuser(SESSION, USER)

The function snmp.deleteuser deletes the given USER on the remote machine that is reachable via the given SESSION. The user to be deleted may be in any state. 

The function returns a the object usmUserStatus as varbind,  with a value = snmp.rowStatus.destroy. If an error occurs varbind may be nil or incomplete. An error message is additionally returned. 

varlist, err = snmp.newpassword(SESSION, OLDPW, NEWPW, FLAG, USER)

The function snmp.newpassword changes an SNMPv3 user's password in a remote machine, which is reachable via the given SESSION. The password of the given USER is changed from OLDPW to NEWPW. The optional parameter flag takes one of the following values 

If USER is omitted, the password of the given SESSION user is changed.

Upon success the function returns a varlist containing the corresponding key change objects. In case of any error an error message indicating an error index  is returned. 

Note, that previous session based on OLDPW become unusable after changing the password. Any request using such a session results in an authentication error. To continue work with the changed password a new session has to be created. 

Example:

local snmp = require "snmp"

-- User who's password to change
local user = "ronja"

-- Old an new passwords
local oldpw = "ronja2006"
local newpw = "mydog2006"

--
-- Create a work session which we use to change the password
--
local sess, err = snmp.open{
peer = "goofy",
version = snmp.SNMPv3,
user = "leuwer",
password = "leuwer2006"
}

--
-- Create an "old" session using OLDPW
--
local sessold = assert(snmp.open{
peer = "localhost",
version = snmp.SNMPv3,
user = user,
password = oldpw})

--
-- Change password implicit using the user's session.
--
local vl = assert(sessold:newpassword(oldpw, newpw, "a"))
for _,v in ipairs(vl) do print(v) end

--
-- Create a "new" session using NEWPW for the user
--
local sessnew = assert(sessold:clone{password = newpw})

--
-- Use the "new" session
--
print(assert(sessnew:get("sysContact.0")))

--
-- Change password back from NEWPW to OLDPW explicitly
-- using the worker session
--
vl = assert(sess:newpassword(newpw, oldpw, "a", user))
for _,v in ipairs(vl) do print(v) end

--
-- Reopen the old session. This will reuse OLDPW.
--
sessold2 = assert(sessold:clone())

--
-- Use the reopened session
--
vb = assert(sessold2:get("sysContact.0"))
print(vb)

--
-- Close all sessions created
--
assert(not sessold:close())
assert(not sessnew:close())
assert(not sessold2:close())
assert(not sess:close())

key, keylen = snmp.createkey(SESSION, PASSWORD [,HASHTYPE])

The function snmp.createkey creates an authentication key from a given PASSWORD according to RFC 2274. The generator uses the algorithm defined in HASHTYPE for key generation. I this parameter is omitted, the function determines the hash type from the given SESSION.

The function returns the key as Lua string (note, that this string may contain embedded zeros) and the length of the key as number. In case of errors the function return a nil key and an error message in keylen.

The following hash types are supported:

This function is internally used in higher level USM management routines like snmp.newpassword.

Note, that localized keys instead of passwords are currently not supported.

key, keylen = snmp.createlocalkey(SESSION, KEY [,HASHTYPE] [,ENGINEID])

The function snmp.createlocalkey creates a localised  form of KEY at ENGINEID according to RFC2274. KEY is a Lua string containing an authentication key produced by snmp.createkey. The generator uses the algorithm defined in HASHTYPE for key generation (see snmp.createkey) . I this parameter is omitted, the function determines the hash type from the given SESSION. The context engine ID is either explicitly defined as ENGINEID or taken from the given SESSION

The function returns the key as Lua string (note, that this string may contain embedded zeros) and the length of the key as number. In case of errors the function return a nil key and an error message in keylen.

This function is internally used in higher level USM management routines like snmp.newpassword.

kcstring, kcstringlen = snmp.keychange(SESSION, OLDKEY, OLDKEYLEN, NEWKEY, NEWKEYLEN, [,HASHTYPE])

The function snmp.keychange uses OLDKEY and acquired random bytes to encode NEWKEY into kcstring and kcstringlen according to the rules of the KeyChange TC described in RFC 2274.

This function is internally used to encode a password change from old to new in snmp.newpassword.

View based Access Control

varlist, err = snmp.createsectogroup(SESSION, SECMODEL, SECNAME, GROUPNAME)

The function snmp.createsectogroup creates an entry in the security name to group table of a remote element reachable via a valid SESSION. The function receives the security model SECMODEL ("SNMPv1", "SNMPv2" or "USM")  and a security name SECNAME as string parameter. These two values are then used as index to create an entry for the group with name GROUPNAME, given as string.

The group is later referenced by snmp.createaccess.

If the operation succeeds, snmp.createsectogroup returns a varlist of two varbinds containing the agent's response, which is SNMP-VIEW-BASED-ACM-MIB::vacmSecurityToGroupStatus with value rowStatus.createAndGo and SNMP-VIEW-BASED-ACM-MIB::vacmGroupName carrying the group's name. In case of an error nil plus an error message is returned indicating the type and location (varbind index in varlist) of the error.

See snmp.createaccess for a complete example on how to use the VACM functions.

varbind, err = snmp.deletesectogroup(SESSION, SECMODEL, SECNAME)

Deletes an entry in the name to group security table SNMP-VIEW-BASED-ACM-MIB::vacmSecuritytoGroupStatus of a remote element addressed by SESSION. 

If the entry could be successfully deleted, the function returns a varbind containing SNMP-VIEW-BASED-ACM-MIB::vacmSecuritytoGroupStatus set to rowStatus.destroy. In case of an error the function returns nil plus an errormessage.

varlist, err = snmp.createview(SESSION, VIEWNAME, SUBTREE, MASK [,FLAG])

The function snmp.createview creates a new entry in the SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable. The parameter VIEWNAME is the name of the view. SUBTREE designates the root OID of the subtree that is added to the view. MASK is a  bitmask that indicates which subidentifiers of the given tree are significant. The optional parameter FLAG is string that defines whether the SUBTREE is included (FLAG = "inc(lude)") or excluded (FLAG="exc(lude)") from the view. The default is "include".

Upon success the function returns a varlist containing the new entry in SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable. 
The following objects of the entry are returned: vacmViewTreeFamilyStatus (createAndGo), vacmViewTreeFamilyMask (as given by MASK) and vacmViewTreeFamilyType ("include" or "exclude"). If an error occurs the function return nil plus an errormessage indicating the first varbind in the varlist that failed.

varbind, err = snmp.deleteview(SESSION, VIEWNAME, SUBTREE)

Deletes an entry in the view table SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable. If the entry could be successfully deleted, the function returns a varbind containing SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyStatus set to rowStatus.destroy. In case of an error the function returns nil plus an errormessage.

varlist, err = snmp.createaccess(SESSION, GROUPNAME, SECMODEL, SECLEVEL, CONTEXTMATCH, READVIEW, WRITEVIEW, NOTIFYVIEW [,CONTEXTPREFIX]) 

The function snmp.createaccess creates an entry int the SNMPv3 access table SNMP-VIEW-BASED-ACM-MIB::vacmAccessTable of the remote element reachable via the given SESSION. The access writes are those described by  the given GROUPNAME, SECMODEL and SECLEVEL. The parameter CONTEXTMATCH defines how the optionally given CONTEXTPREFIX is interpreted: either "exact" or "prefix". The CONTEXTPREFIX is either an empty string, a single context or a collection of contexts. 

The MIB scope is defined differently for read, write and notification access using the view names READVIEW, WRITEVIEW and NOTIFYVIEW.

Upon success the function returns a varlist containing the new entry in SNMP-VIEW-BASE-ACM-MIB::vacmAccessTable. If an error occurs the function returns nil plus an errormesage indicating the first object with a failure in the varlist.

Example:

local snmp = require "snmp"
local mib = snmp.mib

-- We will use this frequently - so let's have a local ref
local check = snmp.check

--
-- Open a working session
--
local sess, err = check(snmp.open{
peer = "localhost",
version = snmp.SNMPv3,
user = "leuwer",
password = "leuwer2006",
})
--
-- Create a user to give access to.
--
local vl = check(sess:createuser("olivia", "ronja"))

--
-- The new user typically needs a new password.
--
vl = check(sess:newpassword("ronja2006", "gonzo2006", "a", "olivia"))

--
-- Create security name to group mapping.
--
vl, err = sess:createsectogroup("usm", "olivia", "rwgroup")
if err then
--
-- An error occurred: cleanup and delete the above new user here.
--
vl = check(sess:deleteuser("olivia"))
sess:close()
os.exit(1)
end

--
-- Create a new view 'interfaces'
--
vl = check(sess:createview("interfaces", mib.oid("ifTable"), "80", "include"))

--
-- Create a new access entry for the new group.
--
vl = check(sess:createaccess("rwgroup", "usm", "authNoPriv", "exact",
"interfaces", "interfaces", "_none_"))

--
-- Let's have a look at all this stuff
--
table.foreach(sess:walk("vacmAccessTable"), print)

-- Finished: now we could test gos and nogos in the access.
-- Cleanup all newly created stuff.
--
vl = check(sess:deleteaccess("rwgroup", "usm", "authNoPriv"))
vl = check(sess:deleteview("interfaces", mib.oid("ifTable")))
vl = check(sess:deletesectogroup("usm", "olivia"))
vl = check(sess:deleteuser("olivia"))
sess:close()

varbind, err = snmp.deleteaccess(SESSION, GROUPNAME, SECMODEL, SECLEVEL [,CONTEXTPREFIX])

Deletes an entry in the SNMP-VIEW-BASED-ACM-MIB::vacmAccessTable. If the entry could be successfully deleted the function returns a varbind containing the table entry's row status set to destroy. In case of an error the function returns nil plus an errormessage.

Data Conversion

varbind = snmp.newvar(NAME, VALUE [,TYPE] [,SESSION])

The function snmp.newvar creates a new varbind instance. NAME designates the SNMP node via a symbolic name or via an objects identifier in dotted notation. The varbind receives VALUE as its value. A TYPE can be optionally defined. If omitted, LuaSNMP determines the object type using the NAME and the global MIB tree.

The optional parameter SESSION is stored in a hidden field and is currently not used. 

LuaSNMP sets appropriate metamethods for the varbind table for printing, concatenation and arithmetic (see varbinds).

A typical usage of this function is the preparation of variable bindings for set-request operations.

result = snmp.sprintvar(VAR)
result = snmp.sprintvar2(VAR)
result = SESSION.sprintvar(VAR)

Converts the given varbind VAR into a printable string. The function can be called either in snmp namespace or as a method of a valid SNMP session. The second method allows to override the function by specifiying the sprintvar configuration parameter during session creation.

snmp.sprintvar2 is just another variant to convert a varbind to a printable string.

Note, that you can also apply the standard Lua SNMP tostring function to varbinds returned by LuaSNMP. See Varbinds for details.

The following example illustrates the usage of the various conversion functions:

local snmp = require "snmp"

hub1 = assert(snmp.open{peer = "goofy"})

vb = assert(hub1:get("sysContact.0"))
print("OUTPUT of 'sprintvar':")
print(vb)
print(snmp.sprintvar(vb))
print(hub1.sprintvar(vb))
print(snmp.sprintvar2(vb))
print()
print("OUTPUT of 'sprintval':")
print(snmp.sprintval(vb))
print(snmp.sprintval2(vb))
print()
print("OUTPUT of 'sprinttype':")
print(snmp.sprinttype(vb))
print(snmp.mib.typename(vb))

The above script generates the following output. Don't be confused about the relation Integer32 and STRING. This is caused by official SNMP typecodes and their translation into typenames.

luasnmp$ lua doc/examples/sprint.lua
OUTPUT of 'sprintvar':
SNMPv2-MIB::sysContact.0 = STRING: root
SNMPv2-MIB::sysContact.0 = STRING: root
SNMPv2-MIB::sysContact.0 = STRING: root
sysContact.0 (Integer32) = STRING: root

OUTPUT of 'sprintval':
STRING: root
root

OUTPUT of 'sprinttype':
Integer32
Integer32

result = snmp.sprintval(VAR)
result = snmp.sprintval2(VAR)
result = SESSION.sprintval(VAR)

Converts the value of a given VAR into a printable string. See snmp.sprintvar for details and an example.

result = snmp.sprinttype(VAR)

Converts the type  of a given VAR into a printable string. See snmp.sprintvar for details and an example.

str = snmp.sprintkey(KEY [,LEN])
str = snmp.key2octet(KEY [, LEN])
str = snmp.octetstring(KEY [, LEN])

Converts the string KEY containing any character code (even 0x00) into a OCTET STRING  representation of the form 01:02:0a:0b. This representation can be used to set a string value with embedded zeros in a varbind. The optional parameter LEN defines how many characters are to evaluated. If omitted the whole string is converted.

Examples:

luasnmp$ lua -l snmp -i
> key=string.char(1,2,3,4,5,10,11,12)
> =string.len(key)
8
> =snmp.octetstring(key)
01:02:03:04:05:0a:0b:0c
> =snmp.key2oid(key)
1.2.3.4.5.10.11.12
> =snmp.key2hex(key)
0x01020304050A0B0C
> return snmp.key2octet(key,6)
01:02:03:04:05:0a
> return snmp.key2octet(key,9)
01:02:03:04:05:0a:0b:0c

str = snmp.sprintkeyd(KEY, LEN)
str = snmp.key2oid(KEY, LEN)

Converts the string KEY containing any character code (even 0x00) into an OID representation 1.2.10.11. The optional parameter LEN defines how many characters are to evaluated. If omitted the whole string is converted.

See snmp.key2octet for examples.

str = snmp.sprintkeyx(KEY, LEN)
str = snmp.key2hex(KEY, LEN

Converts the string KEY containing any character code (even 0x00) into an hexadecimal readable representation 0x01020A0B. The optional parameter LEN defines how many characters are to evaluated. If omitted the whole string is converted.

See snmp.key2octet for examples.

result = snmp.uptimeV2S(TICKS)
result = snmp.uptimeS2V(TIME)

These functions convert SNMP timetick values into a formatted strings and vice versa. 

snmp.uptimeV2S receives a number containing TICKS  and returns a formatted string containing the time  human readable format.

snmp.uptimeS2V receives a formatted string TIME and returns a table with the following fields:
days, hours, minutes, seconds, deciseonds and ticks.

Here is an example:

local snmp = require "snmp"

hub1 = assert(snmp.open{peer = "goofy"})

vb = assert(hub1:get("sysUpTime.0"))
print(vb)

time = snmp.uptimeV2S(vb.value)
ticks = snmp.uptimeS2V(time)

print(time)
table.foreach(ticks, print)

Output:

luasnmp$ lua doc/examples/uptime.lua
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (383697) 1:03:56.97
0:1:3:56.97
minutes 3
deciseconds 97
ticks 383697
hours 1
days 0
seconds 56

Finalized Exceptions

LuaSNMP provides the Finalized Exceptions concept designed by Diego Nehab for luasocket. See here for details.

result = snmp.protect(FUNCTION)

Converts a function that throws exceptions  into a safe function. This function only catches exceptions thrown by a try function - either the predefined snmp.try or a  try function, that was newly created via snmp.newtry. It does not catch normal Lua errors.

FUNCTION is a function that calls try (or assert or error) to throw exceptions. Function snmp.protect returns  an equivalent function that instead of throwing exceptions, returns nil followed by an error message.

Note: Beware that if your function performs some illegal operation that raises an error, the protected function will catch the error and return it as a string. This is because the try function uses errors as the mechanism to throw exceptions.

result = snmp.newtry(FINALIZER)

Creates and returns a clean try function that allows for cleanup before the exception is raised. 

FINALIZER is a function that will be called before try throws the exception. It will be called in protected mode.

The function snmp.newtry returns your customized try function.

Example:

local snmp = require "snmp"
local mib = snmp.mib

--
-- We need this reference here for the finalizer
--
local sess

--
-- Finalize function
--
local function finalize()
io.write("Cleanup handler: ")
if sess then
if rawget(sess, "internal") then
print("closing session.")
sess:close()
else
print("session already closed.")
end
else
print("nothing to do.")
end
end

local function pdoit()

--
-- Acquire a new exception handler
--
local try = snmp.newtry(finalize)

--
-- Do some work: no failure here
--
sess = try(snmp.open{peer = "goofy"})

--
-- Do some work: no failure here
--
local vl = try(sess:get{"sysContact.0","sysDescr.0"})
table.foreach(vl, print)

--
-- Do some work: no failure here
--
local vl = try(snmp.walk(sess, "ifDescr"))
table.foreach(vl, print)

--
-- Do some work: no failure here
--
local t = try(sess.ifDescr)
for k,v in snmp.spairs(t) do print(k,v) end

--
-- Close the session orderly. Note this will delete the
-- internal session. Following usage must fail.
--
try(sess:close())

--
-- Try to re-use the old session which should fail
--
local vl = try(snmp.walk(sess, "ifDescr"))
table.foreach(vl, print)

return "ok"
end

--
-- Execute the protected function 'pdoit'
--
local rv, err = snmp.protect(pdoit)()

--
-- Evaluate the result of error capturing
--
if not rv then
print("doit error: '"..(err or "unknown").."'")
else
print("doit o.k.")
end

result = snmp.try(RET1 [, RET2, ..., RETn])

Throws an exception in case of an error in RET1. The exception can only be caught by the function snmp.protect. If used outside a protected function, the Lua script is abortet WITHOUT error message or stack backtrace. 

The function receives an arbitrary number of arguments RET1 to RETn, which are typically the returns values of a function call nested with snmp.try. The function returns RET1 to RETn if RET1 was not nil. Otherwise, it calls error passing RET2 as argument.

Miscellaneous Functions

result = snmp.check(EXPRESSION, MESSAGE])

The function snmp.check performs a check on the result of LuaSNMP functions. It works similar to the standard Lua function assert. However, instead of just checking EXPRESSION, snmp.check also checks whether MESSAGE is nil. It returns the given EXPRESSION only if the following two conditions are met: EXPRESSION == true and MESSAGE == nil.

The function is provided for convenience reasons, because there are conditions, where LuaSNMP will return both, a valid result AND an error message. For these cases Lua's standard assert function is not suited to capture errors by simply writing result = assert(FUNCTION).

Using assert:

-- NOTE: DOES NOT WORK PROPERLY IN ALL CASES
vbind, err = assert(session:get(OBJECT))

-- NOTE: THIS WORKS
vbind, err = session:get(OBJECT)
assert(vbind and not err, err)

Using check:

-- Do this once in the script
local check = snmp.check
...
-- NOTE: WORKS
vbind, err = check(session:get(OBJECT))
vbind, err = session:get(OBJECT)
check(vbind, err)

-- NOTE: DOES NOT WORK, because MESSAGE will be always non-nil
vbind, err = session:get(OBJECT)
check(vbind, "Error occured here")

Note, that you MUST NOT specify your own error message to snmp.check, because this will always lead to an error condition (see example above).

keys = snmp.getkeys(TABLE)

Retrieves a table with keys from a table, that has been created via SNMP Object Access. The keys are lexicographically sorted.

Example:

local snmp = require "snmp"

-- Open the session
local hub, err = assert(snmp.open{peer="localhost"})

-- Get a table or part of a table
local ifDescr = hub.ifDescr

local keys = snmp.getkeys(ifDescr)

-- Print using standard iterator
print("Using standard iterator:")
for k,v in pairs(ifDescr) do
print(string.format("%s = %s", k, v))
end

-- Print using sorting iterator
print("Using sorting iterator:")
for i,key in ipairs(keys) do
print(string.format("%s = %s", key, ifDescr[key]))
end

See snmp.spairs  for an output of this script.

key, value = snmp.spairs(TABLE)

The iterator snmp.spairs works similar to the standard Lua operator pairs. However, instead of returning the elements of a table with arbitrary keys in arbitrary order  (e.g. table as created via SNMP Object Access), it returns the elements in a lexicographically sorted order of their keys.

local snmp = require "snmp"

-- Let's have a local reference to this function
local spairs = snmp.spairs

-- Open the session
local hub, err = assert(snmp.open{peer="localhost"})

-- Get a table or part of a table
local ifDescr = hub.ifDescr

-- Print using standard iterator
print("Using standard iterator:")
for k,v in pairs(ifDescr) do
print(string.format("%s = %s", k, v))
end

-- Print using sorting iterator
print("Using sorting iterator:")
for k,v in spairs(ifDescr) do
print(string.format("%s = %s", k, v))
end

The script above produces the following output:

Using standard iterator:
ifDescr.3 = eth1
ifDescr.2 = eth0
ifDescr.4 = eth2
ifDescr.5 = sit0
ifDescr.1 = lo
Using sorting iterator:
ifDescr.1 = lo
ifDescr.2 = eth0
ifDescr.3 = eth1
ifDescr.4 = eth2
ifDescr.5 = sit0

oid = snmp.instance(ARG1, ARG2, ..., ARGN)
oid = mib.instance(ARG1, ARG2, ..., ARGN)

Creates an oid instance substring from arguments ARG1 to ARGN. Each argument is converted into OID syntax. Single arguments are separated by a period '.'. Lua types are processed in the following way:

Example:

luasnmp$ lua -l snmp -i
> =snmp.instance(99,"hello",true,{99,"hello",true})
.99.5.104.101.108.108.111.1.99.5.104.101.108.108.111.1
> =snmp.instance{1,2,3,4,5}
.1.2.3.4.5

oid = snmp.stringoid(STR)

Converts a string into an OID.  The function works similar as snmp.instance with a string as argument. The only differences is that snmp.stringoid produces an absolute OID  (without a leading '.'), while snmp.instance always prepends a leading dot. 

SNMP Object Access

LuaSNMP provides a simplified access method to SNMP instances, where the object instances appear as members of an SNMP session. The method usese the Lua metamethods __index and __newindex attached to a session. Once a session has been successfully created, each read access to a session member is automatically translated to a read accesses to SNMP object instances with the given name. Also, any attempt to assign a value to a member is  translated to a write request to the given SNMP instances. 

The above works for single SNMP object instances and also for tables containing multiple object instances. Tables are always returned as simple Lua tables, where the key always represents a valid object identifier in symbolic notation and index instances appended. The values behave like values of varbinds. A returned table can be handled in two different ways. You can assign new values to table entries and then assign assign the table as value to the session member or you can use the indices of the table as single member names for assignment.

here are a few aspects to consider if you want to use this simplified way of SNMP programming.

Example script:

local snmp = require "snmp"

hub1 = assert(snmp.open{peer = "goofy", community = "private"})

-- Sets an re-stores sysContact.0
local oldval = hub1.sysContact_0
print("sysContact.0 = " .. hub1.sysContact_0)

hub1.sysContact_0 = "admin"
print("sysContact.0 = " .. hub1["sysContact.0"])

hub1["sysContact.0"] = oldval
print("sysContact.0 = " .. hub1.sysContact_0)

-- Shows the list of interface names
itab = hub1.ifName
table.foreach(itab, print)

-- Counts the number of objects in mib-2.
local mib2 = hub1["mib-2"]
sum = 0
for _,v in pairs(mib2) do
sum = sum + 1
end
print("Number of entries in 'mib-2': " .. sum)

The above script produces the following output:

luasnmp$ lua doc/examples/direct.lua
sysContact.0 = root
sysContact.0 = admin
sysContact.0 = root
ifName.1 lo
ifName.2 eth0
ifName.5 sit0
ifName.3 eth1
ifName.4 eth2
Number of entries in 'mib-2': 2389