Merge pull request #7 from julthomas/dev/jth/no-des
[luasnmp.git] / snmp.lua
blob6c9fcf0a379a2c99b98815a0428353dbe39e8ec3
1 ------------------------------------------------------------------------------
2 --
3 -- snmp.lua : SNMP primitives
4 --
5 ------------------------------------------------------------------------------
7 local snmp = require "snmp.core"
8 local mib = snmp.mib
10 -- Lua version compatibility
11 local unpack = unpack or table.unpack
13 if string.find(_VERSION, "5.1") then
14 module("snmp", package.seeall)
15 else
16 _ENV = setmetatable(snmp, {
17 __index = _G
19 end
20 ------------------------------------------------------------------------------
21 -- Default Exception Handler
22 ------------------------------------------------------------------------------
23 try = newtry()
25 ------------------------------------------------------------------------------
26 -- MIB definitions
27 ------------------------------------------------------------------------------
28 mib.NOACCESS = 0
29 mib.READONLY = 1
30 mib.READWRITE = 2
31 mib.WRITEONLY = 3
32 mib.READCREATE = 4
34 ------------------------------------------------------------------------------
35 -- Init Mibs and SNMP client
36 ------------------------------------------------------------------------------
37 if (LUA_SNMP_MIBS == nil or LUA_SNMP_MIBS) then
38 mib.init()
39 end
41 ------------------------------------------------------------------------------
42 -- Reverse
43 ------------------------------------------------------------------------------
44 function mib.renums(oid)
45 local t, err = mib.enums(oid)
46 if not t then return nil, err end
47 local rv = {}
48 for k, v in ipairs(t) do
49 rv[v] = k
50 end
51 return rv
52 end
54 ------------------------------------------------------------------------------
55 -- Assemble list of net-snmp config files
56 ------------------------------------------------------------------------------
57 local function configfiles()
58 local dirlist = {
59 "/etc/snmp/",
60 "/usr/lib/snmp/",
61 "/usr/share/snmp/",
62 os.getenv("HOME").."/.snmp/"
64 local filelist = {
65 "snmp.conf",
66 "snmp.local.conf",
67 -- "snmpapp.conf"
69 local t = {}
70 local confpath = os.getenv("SNMPCONFPATH")
71 if confpath then
72 for dir in string.gmatch(confpath, "[^:]+") do
73 table.insert(t, dir .. "/snmp.conf")
74 table.insert(t, dir .. "/snmp.local.conf")
75 table.insert(t, dir .. "/snmpapp.conf")
76 end
77 else
78 for _, dir in ipairs(dirlist) do
79 for _, file in ipairs(filelist) do
80 table.insert(t, dir .. file)
81 end
82 end
83 end
84 return t
85 end
88 ------------------------------------------------------------------------------
89 -- Gather SNMP configuration
90 ------------------------------------------------------------------------------
91 local function read_config()
92 local config = {}
93 for _, fname in ipairs(configfiles()) do
94 local f = io.open(fname, "r")
95 if f then
96 for line in f:lines() do
97 tok, val = string.gsub(line, "^%s*(#*)%s*(%w+)%s+([^%s#]*)",
98 function(comment1, tok, val)
99 if comment1 ~= "#" then
100 -- Uncomment to see which token read from which file.
101 -- print(string.format("config-read: %s = %s from %s",
102 -- tok, val, fname))
103 config[tok] = val
105 end)
107 f:close()
110 return config
113 config = read_config()
114 local tokens = {
115 "pathStrap",
116 "trapdPort"
119 ------------------------------------------------------------------------------
120 -- A metatable for variable bindings.
121 ------------------------------------------------------------------------------
122 __vbindmetatable = {
123 __tostring = sprint_variable,
124 __eq = function(vb1, vb2) return vb1.value == vb2.value end,
125 __le = function(vb1, vb2) return vb1.value <= vb2.value end,
126 __lt = function(vb1, vb2) return vb1.value < vb2.value end,
127 __concat = function(vb1, vb2)
128 local vb = {}
129 if vb1.oid and vb2.oid then return {vb1, vb2} end
130 if not vb1.oid then
131 for _, v in ipairs(vb1) do table.insert(vb, v) end
132 if vb2.oid then
133 table.insert(vb, vb2)
134 else
135 for _, v in ipairs(vb2) do table.insert(vb, v) end
137 else
138 table.insert(vb, vb1)
139 for _, v in ipairs(vb2) do table.insert(vb, v) end
141 return vb
145 ------------------------------------------------------------------------------
146 -- Init general things and config tokens
147 ------------------------------------------------------------------------------
148 assert(not init(tokens, function(token, line)
149 end))
151 ------------------------------------------------------------------------------
152 -- Init Traps handler.
153 -- 1. LUASNMP_STRAPS is set: use straps
154 -- 2. LUASNMP_TRAPDPORT is set: use LUASNMP_TRAPDPORT
155 -- 3. trapdPort in snmp.conf is set: use configuration value
156 -- 4. Use port 6000
157 ------------------------------------------------------------------------------
158 local trapdport = tonumber(os.getenv("LUASNMP_TRAPDPORT") or
159 config.trapdPort) or 6000
160 if trapdport then
161 inittrap(trapdport)
165 ------------------------------------------------------------------------------
166 -- Session configuration parameters
167 ------------------------------------------------------------------------------
168 SNMPv1 = 0
169 SNMPv2C = 1
170 SNMPv2c = 1
171 SNMPv2u = 2
172 SNMPv3 = 3
174 local NOAUTH = 1
175 local AUTHNOPRIV = 2
176 local AUTHPRIV = 3
177 local seclevels = {
178 ["noAuthNoPriv"] = NOAUTH,
179 ["authNoPriv"] = AUTHNOPRIV,
180 ["authPriv"] = AUTHPRIV
182 local secmodels = {
183 ["SNMPv1"] = 1,
184 ["SNMPv2c"] = 2,
185 ["USM"] = 3
187 ------------------------------------------------------------------------------
188 -- SNMPv3 transform OIDs
189 -- Giving them here explicitly avoids the necessity to load the MIBs.
190 ------------------------------------------------------------------------------
191 usmProtocol = {
192 NoAuth = "1.3.6.1.6.3.10.1.1.1",
193 HMACMD5Auth = "1.3.6.1.6.3.10.1.1.2",
194 HMACSHA1Auth = "1.3.6.1.6.3.10.1.1.3",
195 NoPriv = "1.3.6.1.6.3.10.1.2.1",
196 DESPriv = "1.3.6.1.6.3.10.1.2.2",
197 AESPriv = "1.3.6.1.6.3.10.1.2.4"
200 keyOid = {
201 auth = "1.3.6.1.6.3.15.1.2.2.1.6",
202 ownAuth = "1.3.6.1.6.3.15.1.2.2.1.7",
203 priv = "1.3.6.1.6.3.15.1.2.2.1.9",
204 ownPriv = "1.3.6.1.6.3.15.1.2.2.1.10"
207 usmOid = {
208 userStatus = "1.3.6.1.6.3.15.1.2.2.1.13",
209 userSecurityName = "1.3.6.1.6.3.15.1.2.2.1.3",
210 userCloneFrom = "1.3.6.1.6.3.15.1.2.2.1.4"
213 vacmOid = {
214 groupName = "1.3.6.1.6.3.16.1.2.1.3",
215 sec2GroupStorageType = "1.3.6.1.6.3.16.1.2.1.4",
216 sec2GroupStatus = "1.3.6.1.6.3.16.1.2.1.5",
217 accessContextMatch = "1.3.6.1.6.3.16.1.4.1.4",
218 accessReadViewName = "1.3.6.1.6.3.16.1.4.1.5",
219 accessWriteViewName = "1.3.6.1.6.3.16.1.4.1.6",
220 accessNotifyViewName = "1.3.6.1.6.3.16.1.4.1.7",
221 accessStorageType = "1.3.6.1.6.3.16.1.4.1.8",
222 accessStatus = "1.3.6.1.6.3.16.1.4.1.9",
223 viewTreeFamilyMask = "1.3.6.1.6.3.16.1.5.2.1.3",
224 viewTreeFamilyType = "1.3.6.1.6.3.16.1.5.2.1.4",
225 viewTreeFamilyStorageType = "1.3.6.1.6.3.16.1.5.2.1.5",
226 viewTreeFamilyStatus = "1.3.6.1.6.3.16.1.5.2.1.6"
228 rowStatus = {
229 active = 1,
230 notInService = 2,
231 notReady = 3,
232 createAndGo = 4,
233 createAndWait = 5,
234 destroy = 6,
236 ------------------------------------------------------------------------------
237 -- Error Codes
238 ------------------------------------------------------------------------------
239 NOERROR = 0
240 TOOBIG = 1
241 NOSUCHNAME = 2
242 BADVALUE = 3
243 READONLY = 4
244 GENERR = 5
245 NOACCESS = 6
246 WRONGTYPE = 7
247 WRONGLENGTH = 8
248 WRONGENCODING = 9
249 WRONGVALUE = 10
250 NOCREATION = 11
251 INCONSISTENTVALUE = 12
252 RESOURCEUNAVAILABLE = 13
253 COMMITFAILED = 14
254 UNDOFAILED = 15
255 AUTHORIZATIONERROR = 16
256 NOTWRITABLE = 17
257 INCONSISTENTNAME = 18
259 BADVERSION = 101
260 BADCOMMUNITY = 102
261 BADTIME = 103
262 BADRETRIES = 104
263 BADPEER = 105
264 BADPORT = 106
265 BADCALLBACK = 107
266 BADTRAP = 108
267 BADINFO = 109
268 INVINFO = 110
269 BADPRINTVAR = 111
270 BADPRINTVAL = 112
271 BADUSER = 113
272 BADSECLEVEL = 114
273 BADPASSPHRASE = 115
274 BADPRIVPASSPHRASE = 116
275 BADENGBOOTS = 117
276 BADENGTIME = 118
277 BADINCL = 119
278 BADSESSION = 120
279 BADTYPE = 121
280 BADNAME = NOSUCHNAME
281 BADNR = 123
282 BADMR = 124
283 INVINFOREQ = 125
284 BADTRAPOID = 126
285 TIMEOUT = 190
286 INTERR = 191
287 BADARG = 192
288 BADOID = 193
289 BADENGPROBE = 194
290 BADSECMODEL = 195
291 BADSECNAME = 196
292 BADGROUPNAME = 197
294 ------------------------------------------------------------------------------
295 -- Error Messages
296 ------------------------------------------------------------------------------
297 errtb={}
298 errtb[0] = "snmp: no error"
299 errtb[1] = "snmp: too big"
300 errtb[2] = "snmp: no such name"
301 errtb[3] = "snmp: bad value"
302 errtb[4] = "snmp: read only"
303 errtb[5] = "snmp: generic error"
304 errtb[6] = "snmp: no access"
305 errtb[7] = "snmp: wrong type"
306 errtb[8] = "snmp: wrong length"
307 errtb[9] = "snmp: wrong encoding"
308 errtb[10] = "snmp: wrong value"
309 errtb[11] = "snmp: no creation"
310 errtb[12] = "snmp: inconsistent value"
311 errtb[13] = "snmp: resource unavailable"
312 errtb[14] = "snmp: commit failed"
313 errtb[15] = "snmp: undo failed"
314 errtb[16] = "snmp: authorization error"
315 errtb[17] = "snmp: not writable"
316 errtb[18] = "snmp: inconsistent name"
318 local cnf_err = "snmp: invalid session configuration "
319 errtb[101] = cnf_err.."(version)"
320 errtb[102] = cnf_err.."(community)"
321 errtb[103] = cnf_err.."(timeout)"
322 errtb[104] = cnf_err.."(retries)"
323 errtb[105] = cnf_err.."(peer address)"
324 errtb[106] = cnf_err.."(port)"
325 errtb[107] = cnf_err.."(callback)"
326 errtb[108] = cnf_err.."(trap)"
327 errtb[109] = cnf_err.."(inform)"
328 local opt_err = "snmp: invalid configuration for SNMPv1 session "
329 errtb[110] = opt_err.."(inform)"
330 errtb[111] = cnf_err.."(sprintvar)"
331 errtb[112] = cnf_err.."(sprintval)"
332 errtb[113] = cnf_err.."(user)"
333 errtb[114] = cnf_err.."(securityLevel)"
334 errtb[115] = cnf_err.."(authPassphrase, password)"
335 errtb[116] = cnf_err.."(privPassphrase, password)"
336 errtb[117] = cnf_err.."(engineBoots)"
337 errtb[118] = cnf_err.."(engineTime)"
338 errtb[119] = opt_err.."(includeroot)"
340 errtb[120] = "snmp: invalid session"
341 errtb[121] = "snmp: invalid variable type"
342 errtb[122] = "snmp: invalid variable name"
344 errtb[123] = "snmp: invalid argument (non-repeaters)"
345 errtb[124] = "snmp: invalid argument (max-repetitions)"
347 errtb[125] = "snmp: invalid operation for SNMPv1 session (inform)"
348 errtb[126] = "snmp: invalid argument (trap OID)"
350 errtb[190] = "snmp: no response (timeout)"
351 errtb[191] = "snmp: internal failure"
352 errtb[192] = "snmp: bad argument"
353 errtb[193] = "snmp: oid not increasing"
354 errtb[194] = "snmp: engineID probe failed"
355 errtb[195] = "snmp: bad security model"
356 errtb[196] = "snmp: bad security name"
357 errtb[197] = "snmp: bad group name"
359 ------------------------------------------------------------------------------
360 -- Returns nil + error message in case of errors.
361 -- @param errnum number - Error number.
362 -- @param message string - function's message to append.
363 -- @return nil, "errormsg (MESSAGE)"
364 ------------------------------------------------------------------------------
365 local function FAIL(err, message)
366 local rmsg
367 if type(err) == "number" then
368 rmsg = errtb[err]
369 elseif type(err) == "string" then
370 rmsg = err
372 if message then
373 rmsg = rmsg .. " (" .. message ..")"
375 return nil, rmsg
378 ------------------------------------------------------------------------------
379 -- Request Types
380 ------------------------------------------------------------------------------
381 GET_REQ = 1
382 GETNEXT_REQ = 2
383 SET_REQ = 3
384 BULK_REQ = 5
385 INFO_REQ = 6
387 ------------------------------------------------------------------------------
388 -- Mib type codes
389 ------------------------------------------------------------------------------
390 NOSUCHOBJECT = 128
391 NOSUCHINSTANCE = 129
392 ENDOFMIBVIEW = 130
394 TYPE_OTHER = 0
395 TYPE_OBJID = 1
396 TYPE_OCTETSTR = 2
397 TYPE_INTEGER = 3
398 TYPE_NETADDR = 4
399 TYPE_IPADDR = 5
400 TYPE_COUNTER = 6
401 TYPE_GAUGE = 7
402 TYPE_TIMETICKS = 8
403 TYPE_OPAQUE = 9
404 TYPE_NULL = 10
405 TYPE_COUNTER64 = 11
406 TYPE_BITSTRING = 12
407 TYPE_NSAPADDRESS = 13
408 TYPE_UINTEGER = 14
409 TYPE_UNSIGNED32 = 15
410 TYPE_INTEGER32 = 16
412 TYPE_SIMPLE_LAST = 16
414 TYPE_TRAPTYPE = 20
415 TYPE_NOTIFTYPE = 21
416 TYPE_OBJGROUP = 22
417 TYPE_NOTIFGROUP = 23
418 TYPE_MODID = 24
419 TYPE_AGENTCAP = 25
420 TYPE_MODCOMP = 26
422 TYPE_FLOAT = 120
423 TPYE_DOUBLE = 121
424 TYPE_INTEGER64 = 122
425 TYPE_UNSIGNED64 = 123
427 ------------------------------------------------------------------------------
428 -- Mib type names
429 ------------------------------------------------------------------------------
430 typetb = {
431 "OBJECT IDENTIFIER",
432 "OCTET STRING",
433 "INTEGER",
434 "NetworkAddress",
435 "IpAddress",
436 "Counter",
437 "Gauge32",
438 "TimeTicks",
439 "Opaque",
440 "NULL",
441 "Counter64",
442 "BIT STRING",
443 "NsapAddress",
444 "UInteger",
445 "UInteger32",
446 "Integer32",
450 "TRAP-TYPE",
451 "NOTIFICATION-TYPE",
452 "OBJECT-GROUP",
453 "NOTIFICATION-GROUP",
454 "MODULE-IDENTITY",
455 "AGENT-CAPABILITIES",
456 "MODULE-COMPLIANCE"
458 typetb[0] = "OTHER"
459 typetb[120] = "Opaque: Float"
460 typetb[121] = "Opaque: Double"
461 typetb[122] = "Opaque: Integer64"
462 typetb[123] = "Opaque: Unsigned64"
464 typetb[128] = "NO SUCH OBJECT"
465 typetb[129] = "NO SUCH INSTANCE"
466 typetb[130] = "END OF MIB VIEW"
468 ------------------------------------------------------------------------------
470 ------------------------------------------------------------------------------
472 ------------------------------------------------------------------------------
473 -- Return comprimated varlist result.
474 -- @param vl table - Varlist.
475 -- @param err string - error or nil.
476 -- @param errindex number - index of failure varbind in varlist.
477 -- @return varlist or nil + errormessage on failure.
478 ------------------------------------------------------------------------------
479 local function retvarlist(vl, err, errindex)
480 if err then
481 if errindex then
482 return FAIL(err, "in index " .. tostring(errindex))
483 else
484 return FAIL(err)
486 else
487 return vl
492 ------------------------------------------------------------------------------
493 -- Print an object value
494 -- @param vb Variable binding or value.
495 -- @return formatted string for printing.
496 ------------------------------------------------------------------------------
497 function sprintval2(vb)
498 if type(vb) ~= "table" then
499 if vb then
500 return tostring(vb)
501 else
502 return "<NULL>"
505 local value = vb.value
506 if not value then
507 return "<NULL>"
509 local type = vb.type
510 if type == TYPE_TIMETICKS then
511 local days,hours,minutes,seconds,deci,ticks
512 if (value.days) then days = value.days else days = 0 end
513 if (value.hours) then hours = value.hours else hours = 0 end
514 if (value.minutes) then minutes = value.minutes else minutes = 0 end
515 if (value.seconds) then seconds = value.seconds else seconds = 0 end
516 if (value.deciseconds) then deci= value.deciseconds else deci= 0 end
517 if (value.ticks) then ticks= value.ticks else ticks= 0 end
518 return string.format("%dd %d:%d:%d.%d (%d)",days,hours,minutes,seconds,deci,ticks)
519 elseif (type == TYPE_INTEGER) or (type == TYPE_UINTEGER) then
520 local enums = mib.enums(vb.oid)
521 if enums and enums[value] then
522 return enums[value].."("..value..")"
524 elseif (type == TYPE_OBJID) then
525 local name = mib.name(value)
526 if name ~= value then
527 return name.." ("..value..")"
528 else
529 return value
532 return tostring(value)
535 ------------------------------------------------------------------------------
536 -- Print an object variable.
537 -- @param vb table - Variable binding.
538 -- @return Formatted string for printing.
539 ------------------------------------------------------------------------------
540 function sprintvar2(vb)
541 if type(vb) ~= "table" then
542 return "<Invalid varbind>"
544 local name = mib.name(vb.oid) ---> aceitar ja' receber nome pronto?
545 if not name then
546 name = ""
548 return string.format("%s (%s) = %s",name,sprint_type(vb.type),sprintval2(vb))
551 ------------------------------------------------------------------------------
552 -- Print an object type.
553 -- @param t Variable binding or type code.
554 -- @return Formatted string for printing.
555 ------------------------------------------------------------------------------
556 function sprint_type(t)
557 local tCode
558 if type(t) == "table" then
559 tCode = tonumber(t.type or mib.type(t.oid))
560 elseif type(t) == "number" then
561 tCode = tonumber(t)
562 elseif type(t) == "string" then
563 tCode = mib.type(t)
565 if tCode and typetb[tCode] then
566 return typetb[tCode]
567 else
568 return "<Invalid type>"
572 -- For convenience.
573 mib.typename = sprint_type
575 ------------------------------------------------------------------------------
576 -- Print an error.
577 -- @param err Error code.
578 -- @return Formatted string for printing.
579 ------------------------------------------------------------------------------
580 function sprint_error(err)
581 local str = errtb[err]
582 if str then
583 return str
584 else
585 return "UNKNOWN ERROR"
589 ------------------------------------------------------------------------------
590 -- Check an OID.
591 -- Does not check whether OID is in the tree.
592 -- @param oid string - OID string representation.
593 -- @return OID string, if o.k. Nil otherwise.
594 ------------------------------------------------------------------------------
595 local function isoid(oid)
596 if type(oid) ~= "string" then return FAIL(BADARG) end
597 if string.find(string.gsub(oid,"%.%d%d*",""),"^%d%d*$") then
598 return oid
599 else
600 return FAIL("snmp: not an OID")
604 ------------------------------------------------------------------------------
605 -- Eval length of an OID.
606 -- Does not check whether OID is in the tree.
607 -- @param oid string - OID string representation.
608 -- @return OID string, if o.k. Nil otherwise.
609 ------------------------------------------------------------------------------
610 local function oidlen(oid)
611 local oid, err = isoid(oid)
612 if not oid then return FAIL("snmp: not an OID") end
613 local n = 0
614 string.gsub(oid, "%.", function(v)
615 n = n + 1
616 end)
617 return n + 1
620 ------------------------------------------------------------------------------
621 -- Eval a base OID of certain length.
622 -- Does not check whether OID is in the tree.
623 -- @param oid string - OID string representation.
624 -- @param len number - Length of base OID.
625 -- @return Base OID string, if o.k. nil otherwise.
626 ------------------------------------------------------------------------------
627 local function oidbase(oid, len)
628 local err
629 local len = len or oidlen(oid)
630 oid, err = isoid(oid)
631 if not oid then return nil, err end
632 local rv = ""
633 for d, p in string.gmatch(oid,"(%d+)(%.*)") do
634 rv = rv .. d
635 len = len - 1
636 if len == 0 then
637 return rv
638 else
639 rv = rv .. (p or "")
642 print("###", oid, rv)
643 return rv
646 ------------------------------------------------------------------------------
647 -- Translate an OID into a table.
648 -- Does not check whether OID is in the tree.
649 -- @param oid string - OID string representation or name.
650 -- @return Table presentation of an OID: {1,2,17,2,3}.
651 ------------------------------------------------------------------------------
652 local function oidtotable(oid)
653 local err
654 oid, err = isoid(oid)
655 if not oid then return nil, err end
656 local t = {}
657 string.gsub(oid, "(%d+)%.*", function(d) table.insert(t, tonumber(d)) end)
658 return t, #t
661 ------------------------------------------------------------------------------
662 -- Compare two OIDs.
663 -- Does not check whether OID is in the tree.
664 -- @param oid1 string - OID string 1 or name 1.
665 -- @param oid2 string - OID string 2 or name 2.
666 -- @return 1 if oid1 > oid2, 0 if oid1 == oid2, -1 if oid1 < oid2.
667 ------------------------------------------------------------------------------
668 local function oidcompare(oid1, oid2)
669 if not isoid(oid1) then return FAIL(BADARG, "oid1") end
670 if not isoid(oid2) then return FAIL(BADARG, "oid2") end
671 local t1,n1 = oidtotable(oid1)
672 local t2,n2 = oidtotable(oid2)
674 local n = n1
675 if n2 < n1 then
676 n = n2
678 for i = 1,n do
679 if t1[i] > t2[i] then
680 return 1
681 elseif t1[i] < t2[i] then
682 return -1
685 if n1 < n2 then return -1 end
686 if n2 < n1 then return 1 end
687 return 0
690 ------------------------------------------------------------------------------
691 -- Returns the index of a varbind based on a superordinated OID.
692 -- @param vb table - Variable name (text or OID).
693 -- @param tname string - Super-ordinated OID
694 -- @return Index as string.
695 ------------------------------------------------------------------------------
696 local function oidindex(oid, base)
697 if not isoid(oid) then return FAIL(BADARG, "oid") end
698 if not isoid(base) then return FAIL(BADARG, "base") end
699 local s, n = string.gsub(oid, "^"..base.."%.(.+)","%1")
700 if n > 0 then return s else return "" end
703 ------------------------------------------------------------------------------
704 -- Load a MIB file.
705 -- Search in all directories given by MIBDIRS.
706 -- @param fname string File name.
707 ------------------------------------------------------------------------------
708 local function load(fname)
709 if string.sub(fname, 1, 1) == "/" then
710 local tree, err = mib._load(fname)
711 if tree then return tree end
712 else
713 local configdirs = string.gsub(config.mibdirs or "", "(%+*)(.+)", "%2")
714 local mibdirs = ".:" .. (os.getenv("LUASNMP_MIBDIRS") or os.getenv("MIBDIRS") or configdirs)
715 for dir in string.gmatch(mibdirs, "[^:;]+") do
716 for _,ext in ipairs{".txt","",".mib"} do
717 local fn = dir.."/"..fname..ext
718 local f = io.open(fn,"r")
719 if f then
720 f:close()
721 local tree, err = mib._load(fn)
722 if tree then return tree end
727 return nil, "mib: cannot add mib"
730 mib.isoid = isoid
731 mib.oidlen = oidlen
732 mib.oidbase = oidbase
733 mib.oidtotable = oidtotable
734 mib.oidcompare = oidcompare
735 mib.oidindex = oidindex
736 mib.load = load
738 -- Some other names for sprints.
739 sprintval = sprint_value
740 sprinttype = sprint_type
741 sprintvar = sprint_variable
742 sprinterr = sprint_error
743 ---------------------------------------------------------------------------
744 -- Walk the MIB tree
745 -- @param sess table - Session.
746 -- @param var table or string - Root object.
747 -- @return Varbind list with values.
748 ---------------------------------------------------------------------------
749 function walk(sess, var)
750 local oid
751 local running = true
752 local t = {}
753 local root, vb, err
754 -- if not sess then return nil, errtb[BADSESSION] end
755 if not sess then return FAIL(BADSESSION) end
756 local var = var or "mib-2"
757 if type(var) == "table" then
758 oid = mib.oid(var.oid)
759 elseif type(var) == "string" then
760 oid = mib.oid(var)
761 else
762 return FAIL(BADARG, "var")
764 if not oid then return nil, errtb[BADOID] end
765 root = {oid = oid}
766 if sess.includeroot then
767 vb, err = sess:get(root)
768 if err then return nil, err end
769 table.insert(t,vb)
771 local rootlen = oidlen(root.oid)
772 local vb = root
773 local rootoid = root.oid
774 local last = rootoid
775 while running do
776 vb, err = sess:getnext(vb)
777 if not vb then return nil, err end
778 if err then
779 if err == NOSUCHNAME or string.find(err, "no such name") then
780 running = false
781 return t, nil
782 else
783 return t, err
786 local oid = vb.oid
787 if oidlen(oid) < rootlen or
788 vb.type == ENDOFMIBVIEW or
789 not string.find(oid, rootoid) then
790 running = false
791 else
792 if oidcompare(last, oid) >= 0 then
793 return nil, errtb[BADOID]
795 last = oid
796 table.insert(t, vb)
799 if not err and #t == 0 then
800 vb, err = sess:get(root)
801 if err then return t, err end
802 table.insert(t, vb)
804 return t
807 ---------------------------------------------------------------------------
808 -- Retrieve sorted list of keys of a table
809 ---------------------------------------------------------------------------
810 function getkeys(t)
811 local rv = {}
812 for k,v in pairs(t) do
813 table.insert(rv, k)
815 table.sort(rv)
816 return rv
819 ---------------------------------------------------------------------------
820 -- Sorted iteration over a list of keys in table.
821 ---------------------------------------------------------------------------
822 assert(not _G.spairs, "Symbol 'spairs' already defined")
823 function _G.spairs(t)
824 local keys = getkeys(t)
825 local i = 0
826 table.sort(keys)
827 return function()
828 i = i + 1
829 return keys[i], t[keys[i]]
832 ---------------------------------------------------------------------------
833 -- Convert formatted time value to varbind.
834 -- @param s string - Formatted string.
835 -- @return Varbind.
836 ---------------------------------------------------------------------------
837 function uptimeS2V(s)
838 local ticks = nil
839 if type(s) ~= "string" then return FAIL(BADARG) end
840 string.gsub(s, "(%d+):(%d+):(%d+):(%d+)%.(%d+)$",
841 function(d, h, m, s, ds)
842 ticks = {
843 days = tonumber(d) or 0,
844 hours = tonumber(h) or 0,
845 minutes = tonumber(m) or 0,
846 seconds = tonumber(s) or 0,
847 deciseconds = tonumber(ds) or 0
849 ticks.ticks = ticks.deciseconds +
850 ticks.seconds * 100 +
851 ticks.minutes * 60 * 100 +
852 ticks.hours * 60 * 60 * 100 +
853 ticks.days * 24 * 60 * 60 * 100
854 end)
855 return ticks
858 ---------------------------------------------------------------------------
859 -- Convert uptime varbind to string.
860 -- @param vb table - Varbinding containing sysUpTime.
861 -- @return Formatted string.
862 ---------------------------------------------------------------------------
863 function uptimeV2S(vb)
864 if type(vb) ~= "table" then return FAIL(BADARG) end
865 return string.format("%d:%d:%d:%d.%d",
866 vb.days, vb.hours, vb.minutes,
867 vb.seconds, vb.deciseconds)
870 ---------------------------------------------------------------------------
871 -- A generic trap handler.
872 -- Only activated if a user supplied trap callback function has
873 -- been provided. It also calls the user callback function.
874 -- NOTE: The scanner of the trap message requires the following log formats
875 -- settings in snmptrapfmt.conf to work properly:
876 -- SUBST=\#\ \
877 -- NODEFMT=ip
878 -- VARFMT="#[%s] %n (%t) : %v"
879 -- LOGFMT="$x#$A#$e#$G#$S#$T$*"
880 -- @param session table - Session that captures the trap.
881 -- @param msg string - Message from snmptrapd.
882 -- See snmptrapd.conf manual page for content.
883 -- @return none.
884 ---------------------------------------------------------------------------
885 function __trap(session, msg)
886 local host, src, vbs, sip, dip, sport, dport, uptimeName, uptimeVal, vbs, ip, port
887 -- debugging: uncomment if desired
888 --print(string.format(" session.name=%s", session.name))
889 --print(string.format(" generic_trap(): msg = %q", msg))
890 --print(string.format(" netsnmp version: %s", snmp.getversion()))
891 --print(string.format(" snmp._SYSTEM: %s", snmp._SYSTEM))
893 -- The message may ne different for Cygwin and Linux.
894 -- We keep the differentiation even if not really required.
895 if snmp._SYSTEM == "Cygwin" and snmp.getversion() > "5.3" then
896 string.gsub(msg,
897 "^%s*([%w%.]+)%s+(%w+):%s*%[[%d%.]+%]%-%>%[([%d%.]+)%]:(%d+)%s+([^%s]+)%s+([^%s]+)%s+(.*)",
898 function(...)
899 host, proto, ip, port, uptimeName, uptimeVal, vbs = select(1, ...)
900 end)
901 -- debugging:
902 -- print(string.format(" host=%q, proto=%q, ip=%q, port=%q, uptimeName=%q, uptimeVal=%q, vbs=%q",
903 -- host, proto, ip, port, uptimeName, uptimeVal, vbs))
904 elseif snmp.getversion() > "5.5" then
905 string.gsub(msg,
906 -- Example msg:
907 -- " localhost UDP: [127.0.0.1]->[127.0.0.1]:-6577 \
908 -- iso.3.6.1.2.1.1.3.0 0:2:11:01.53 iso.3.6.1.6.3.1.1.4.1.0 ccitt.0 iso.3.6.1.2.1.1.5.0 \"hello\""
909 "^%s*([%w%.]+)%s+(%w+):%s*%[([%d%.]+)%]:([%d]+)%-%>%[([%d%.]+)%]:([%-%d]+)%s+([^%s]+)%s+([^%s]+)%s+(.*)",
910 function(...)
911 host, proto, sip, sport, dip, dport, uptimeName, uptimeVal, vbs = select(1, ...)
912 ip = sip
913 port = sport
914 -- debugging:
915 -- print("dissected msg:", host, proto, sip, sport, dip, dport, uptimeName, uptimeVal, vbs)
916 end)
917 -- debugging: uncomment if desired
918 -- print(string.format(" host=%q, proto=%q, ip=%q, port=%q, uptimeName=%q, uptimeVal=%q, vbs=%q",
919 -- host, proto, ip, port, uptimeName, uptimeVal, vbs))
920 else
921 string.gsub(msg, "([%w%.]+)%s+([%d%.]+)%s+([^%s]+)%s+([^%s]+)%s+(.*)",
922 function(...)
923 local arg = {select(1, ...)}
924 host = arg[1]
925 src = arg[2]
926 uptimeName = arg[3]
927 uptimeVal = uptimeS2V(arg[4])
928 vbs = arg[5]
929 end)
931 if dip == session.peer_ip or true then
932 -- Convert variable bindings
933 local vlist
934 if string.find(snmp.getversion(), "5.4") then
935 vlist = {{oid = uptimeName, type = mib.type("sysUpTime"), value = uptimeVal}}
936 elseif string.find(snmp.getversion(), "5.3") then
937 -- Note: we don't get a reasonable type value for sysUpTimeInstance in net-snmp 5.3
938 vlist = {{oid=uptimeName, type=mib.type("sysUpTime"), value = uptimeVal}}
939 else
940 vlist = {{oid=uptimeName, type=mib.type(uptimeName), value = uptimeVal}}
942 string.gsub(vbs, "([^%s]+)%s+([^%s]+)%s*",
943 function(name, val)
944 local oid = name
945 local typ = mib.type(name)
946 local value = mib.oid(val) or uptimeS2V(val) or val
947 table.insert(vlist, {oid=oid, type=typ, value=value})
948 end)
949 -- debugging:
950 -- table.foreach(vlist, function(k,v) table.foreach(v, print) end)
951 session.usertrap(vlist, ip, port, host, session)
956 ---------------------------------------------------------------------------
957 -- Create a new variable binding.
958 -- @param name string - Name or OID of variable.
959 -- @param value any - Value of variable.
960 -- @param type number (opt) - Type of the object.
961 -- @return Variable binding with metamethods set.
962 ---------------------------------------------------------------------------
963 function newvar(name, value, typ, session)
964 local oid = mib.oid(name) or name
965 local vb = {oid = oid, type = typ or mib.type(oid), value = value}
966 if session then vb[".session"] = session end
967 setmetatable(vb, __vbindmetatable)
968 return vb
971 ---------------------------------------------------------------------------
972 -- Print a hexstring (key) in the form 0x1234etc.
973 -- @param key string - Hexstring (may contain embedded zeros).
974 -- @param len number - Length of the (sub)string to evaluate.
975 -- @return String containing a readable presentation of the hexstring.
976 ---------------------------------------------------------------------------
977 function sprintkeyx(key, len)
978 local s = "0x"
979 local slen = string.len(key)
980 local len = len or slen
981 if len > slen then len = slen end
982 for i = 1, len do
983 s = s..string.format("%02X", string.byte(key, i))
985 return s
988 ---------------------------------------------------------------------------
989 -- Print a hexstring (key) in the OID form 1.2.3.4.
990 -- @param key string - Hexstring (may contain embedded zeros).
991 -- @param len number - Length of the (sub)string to evaluate.
992 -- @return String containing an OID presentation of the input string.
993 ---------------------------------------------------------------------------
994 function sprintkeyd(key, len)
995 local s = ""
996 local slen = string.len(key)
997 local len = len or slen
998 if len > slen then len = slen end
999 for i = 1, len do
1000 s = s..string.format("%d", string.byte(key,i))
1001 if i < len then s = s .. "." end
1003 return s
1006 ---------------------------------------------------------------------------
1007 -- Print a hexstring (key) in the form 01:02:03:etc.
1008 -- @param key string - Hexstring (may contain embedded zeros).
1009 -- @param len number - Length of the (sub)string to evaluate.
1010 -- @return String containing a presentation of the hexstring that can
1011 -- be evaluated by snmp.set(SESSION, VAR)
1012 ---------------------------------------------------------------------------
1013 function sprintkey(key, len)
1014 local slen = string.len(key)
1015 local len = len or slen
1016 local s = ""
1017 if len > slen then len = slen end
1018 for i = 1, len do
1019 s = s..string.format("%02x", string.byte(key, i))
1020 if i < len then s = s .. ":" end
1022 return s
1026 -- Shortcuts - probably more SNMP like names.
1028 key2oid = sprintkeyd
1029 key2hex = sprintkeyx
1030 key2octet = sprintkey
1031 octetstring = sprintkey
1032 hexstring = sprintkeyx
1034 ---------------------------------------------------------------------------
1035 -- Print a string as OID. The length is prepended to the OID string
1036 -- representing the actual string data.
1037 -- @param str string - String to convert.
1038 -- @return String containint an OID in the from LENGTH.CHAR.CHAR.etc.
1039 ---------------------------------------------------------------------------
1040 function stringoid(str)
1041 local len = string.len(str)
1042 if len == 0 then
1043 return "0"
1044 else
1045 return string.format("%d.%s", string.len(str), key2oid(str))
1049 mib.stringoid = stringoid
1050 ---------------------------------------------------------------------------
1051 -- Create an index in OID from from the given parameters.
1052 -- @param arg table - Table containing a variable number of parameters
1053 -- @return String containing the index as OID.
1054 ---------------------------------------------------------------------------
1055 function instance(...)
1056 local oid = ""
1057 arg = {select(1, ...)}
1058 for k,v in ipairs(arg) do
1059 if type(v) == "number" then
1060 oid = oid .. string.format(".%d", v)
1061 elseif type(v) == "string" then
1062 if isoid(v) then
1063 oid = oid .. string.format(".%d.%s",oidlen(v), v)
1064 else
1065 oid = oid .. string.format(".%s", stringoid(v))
1067 elseif type(v) == "boolean" then
1068 if v == true then oid = oid .. ".1" else
1069 oid = oid .. ".0" end
1070 elseif type(v) == "table" then
1071 oid = oid .. instance(unpack(v))
1072 else
1073 return FAIL(BADARG, "arg["..k.."]")
1076 return oid
1079 mkindex = instance
1080 mib.instance = instance
1082 ------------------------------------------------------------------------------
1083 -- Change user's password.
1084 -- @param session table - Active session.
1085 -- @param oldpw string - Old password.
1086 -- @param newpw string - New password.
1087 -- @param flag string (opt) - Indicates what pass phrase to
1088 -- change: "a" = auth, "ap" = auth + priv, "p" = priv.
1089 -- @param user (opt) string - What user to change.
1090 -- @param engineID (opt) hexstring - Context engine ID to use.
1091 -- @return varlist on success, nil + error message of failure.
1092 ------------------------------------------------------------------------------
1093 function newpassword(session, oldpw, newpw, flag, user, engineID)
1094 local authKeyChange, privKeyChange
1096 local doauth, dopriv = false, false
1097 -- Be sure we run a version 3 session
1098 -- Do we really need this? Don't think so (leu)
1099 if session.version ~= SNMPv3 then return FAIL(BADSESSION) end
1101 -- What to change: auth=a, priv=p or both=ap
1102 flag = flag or "a"
1103 if string.find(flag,"a") then doauth = true end
1104 if string.find(flag,"p") then dopriv = true end
1106 -- What user to change
1107 if not user then
1108 -- session user
1109 authKeyChange = keyOid.ownAuth
1110 privKeyChange = keyOid.ownPriv
1111 user = session.user
1112 else
1113 -- defined user
1114 if type(user) ~= "string" then
1115 return nil, errtb[BADARG] .. " (user)"
1117 authKeyChange = keyOid.auth
1118 privKeyChange = keyOid.priv
1120 -- Engine ID to use
1121 if not engineID then
1122 local t = session:details()
1123 if t.contextEngineIDLen == 0 then
1124 -- we need an engineID and don't have one: probe with empty get-request
1125 local vb, err = session:get(nil)
1126 if not vb or err then return nil, errtb[BADENGPROBE] end
1128 t = session:details()
1129 engineID = t.contextEngineID
1131 -- check password lengths
1132 if type(oldpw) ~= "string" and string.len(oldpw) < 8 then
1133 return FAIL(BADARG, "oldpw")
1135 if type(newpw) ~= "string" and string.len(oldpw) < 8 then
1136 return FAIL(BADARG, "newpw")
1139 -- create old and new keys
1140 local authProto
1141 if session.authType == "MD5" then
1142 authProto = usmProtocol.HMACMD5Auth
1143 else
1144 authProto = usmProtocol.HMACSHA1Auth
1146 local oldKu, oldKuLen = createkey(session, oldpw, authProto)
1147 if not oldKu then return nil, oldKuLen end
1148 local newKu, newKuLen = createkey(session, newpw, authProto)
1149 if not newKu then return nil, newKuLen end
1151 -- create old and new localized keys
1152 local oldKul, oldKulLen = createlocalkey(session, oldKu, authProto, engineID)
1153 if not oldKul then return nil, oldKulLen end
1154 local newKul, newKulLen = createlocalkey(session, newKu, authProto, engineID)
1155 if not newKul then return nil, newKulLen end
1157 -- transformations
1158 if dopriv then
1159 if session.privType == "DES" or session.privType == "AES" then
1160 oldKulPrivLen, newKulPrivLen = 16, 16
1162 oldKulPriv = oldKul
1163 newKulPriv = newKul
1166 -- create keychange string
1167 local keychg, keychgpriv, keychglen, keychgprivlen
1168 if doauth then
1169 keychg, keychglen = keychange(session, oldKul, newKul, authProto)
1170 if not keychg then return FAIL("snmp: cannot create keychange") end
1173 if dopriv then
1174 keychgpriv, keychgprivlen = keychange(session, oldKulPriv, newKulPriv, authProto)
1175 if not keychgpriv then return FAIL("snmp: cannot create keychange priv") end
1178 -- setup varbind list
1179 local vl = {}
1180 if doauth then
1181 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1182 authKeyChange,
1183 string.len(engineID), key2oid(engineID),
1184 string.len(user), key2oid(user)),
1185 key2octet(keychg, keychglen),
1186 snmp.TYPE_OCTETSTR)
1187 table.insert(vl, vb)
1189 if dopriv then
1190 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1191 privKeyChange,
1192 string.len(engineID), key2oid(engineID),
1193 string.len(user), key2oid(user)),
1194 key2octet(keychgpriv, keychgprivlen),
1195 snmp.TYPE_OCTETSTR)
1197 -- set request
1198 vl, err, errindex = session:set(vl)
1199 if err then
1200 if errindex then
1201 return FAIL(err,"index " .. tostring(errindex))
1202 else
1203 return FAIL(err)
1205 else
1206 return vl
1210 ------------------------------------------------------------------------------
1211 -- Create user and optionally take authentication parameters
1212 -- from another user.
1213 -- @param session table - Session for performing the operation.
1214 -- @param user string - Name of user to create.
1215 -- @param clonefrom string (opt) - Name of user to clone.
1216 -- @return Varlist or nil + errormessage
1217 ------------------------------------------------------------------------------
1218 function createuser(session, user, clonefrom, engineID)
1219 local err
1221 if type(user) ~= "string" then return FAIL(BADARG, "user") end
1223 -- Engine ID to use
1224 if not engineID then
1225 local t = session:details()
1226 if t.contextEngineIDLen == 0 then
1227 -- we need an engineID and don't have one: probe with empty get-request
1228 local vb, err = session:get(nil)
1229 if not vb then return FAIL(BADENGPROBE) end
1231 t = session:details()
1232 engineID = t.contextEngineID
1235 -- Prepare and execute the request
1236 if clonefrom then
1237 -- create clonefrom
1238 if type(clonefrom) ~= "string" then
1239 return FAIL(BADARG, "clonefrom")
1241 local vl = {}
1242 -- oid is the new user
1243 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1244 usmOid.userStatus,
1245 string.len(engineID), key2oid(engineID),
1246 string.len(user), key2oid(user)),
1247 -- value is the row status
1248 rowStatus.createAndGo)
1249 table.insert(vl, vb)
1250 -- oid is the new user
1251 vb = newvar(string.format("%s.%d.%s.%d.%s",
1252 usmOid.userCloneFrom,
1253 string.len(engineID), key2oid(engineID),
1254 string.len(user), key2oid(user)),
1255 -- value is the entry of clonefrom user
1256 string.format("%s.%d.%s.%d.%s",
1257 usmOid.userSecurityName,
1258 string.len(engineID), key2oid(engineID),
1259 string.len(clonefrom), key2oid(clonefrom)))
1260 table.insert(vl, vb)
1262 local vl, err, errindex = session:set(vl)
1263 if err then
1264 if errindex then
1265 return FAIL(err, "index " .. tostring(errindex))
1266 else
1267 return FAIL(err)
1269 else
1270 return vl
1272 else
1273 -- create
1274 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1275 usmOid.userStatus,
1276 string.len(engineID), key2oid(engineID),
1277 string.len(user), key2oid(user)),
1278 rowStatus.createAndWait)
1279 vb, err = session:set(vb)
1280 return vb, err
1284 ------------------------------------------------------------------------------
1285 -- Active a created user and clone authentication from another
1286 -- user.
1287 -- @param session table - Session for performing the operation.
1288 -- @param user string - Name of user to create.
1289 -- @param clonefrom string - Name of user to clone.
1290 -- @return Varlist or nil + errormessage
1291 ------------------------------------------------------------------------------
1292 function clonefromuser(session, user, clonefrom, engineID)
1293 local err
1295 -- sanity checks
1296 if type(clonefrom) ~= "string" then return FAIL(BADARG, "clonefrom") end
1297 if type(user) ~= "string" then return FAIL(BADARG, "user") end
1299 -- Engine ID to use
1300 if not engineID then
1301 local t = session:details()
1302 if t.contextEngineIDLen == 0 then
1303 -- we need an engineID and don't have one: probe with empty get-request
1304 local vb, err = session:get(nil)
1305 if not vb then return FAIL(BADENGPROBE) end
1307 t = session:details()
1308 engineID = t.contextEngineID
1311 local vl = {}
1312 -- Set user row active
1313 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1314 usmOid.userStatus,
1315 string.len(engineID), key2oid(engineID),
1316 string.len(user), key2oid(user)),
1317 rowStatus.active)
1318 table.insert(vl, vb)
1320 -- oid is the new user
1321 vb = newvar(string.format("%s.%d.%s.%d.%s",
1322 usmOid.userCloneFrom,
1323 string.len(engineID), key2oid(engineID),
1324 string.len(user), key2oid(user)),
1325 -- value is the entry of clonefrom user
1326 string.format("%s.%d.%s.%d.%s",
1327 usmOid.userSecurityName,
1328 string.len(engineID), key2oid(engineID),
1329 string.len(clonefrom), key2oid(clonefrom)))
1330 table.insert(vl, vb)
1332 -- set request
1333 local vl, err, errindex = session:set(vl)
1334 if err then
1335 if errindex then
1336 return FAIL(err,"index " .. tostring(errindex))
1337 else
1338 return FAIL(err)
1340 else
1341 return vl
1345 ------------------------------------------------------------------------------
1346 -- Delete a user.
1347 -- @param session table - Session for performing the operation.
1348 -- @param user string - Name of user to delete.
1349 -- @return varbind or nil + errormessage
1350 ------------------------------------------------------------------------------
1351 function deleteuser(session, user, engineID)
1353 -- Sanity check
1354 if type(user) ~= "string" then return FAIL(BADARG, "user") end
1356 -- Engine ID to use
1357 if not engineID then
1358 local t = session:details()
1359 if t.contextEngineIDLen == 0 then
1360 -- we need an engineID and don't have one: probe with empty get-request
1361 local vb, err = session:get(nil)
1362 if not vb then return FAIL(BADENGPROBE) end
1364 t = session:details()
1365 engineID = t.contextEngineID
1368 -- delete
1369 local vb = newvar(string.format("%s.%d.%s.%d.%s",
1370 usmOid.userStatus,
1371 string.len(engineID), key2oid(engineID),
1372 string.len(user), key2oid(user)),
1373 rowStatus.destroy)
1374 vb, err = session:set(vb)
1375 return vb, err
1378 ------------------------------------------------------------------------------
1379 -- Create security name to group mapping.
1380 -- @param session table - Valid session.
1381 -- @param secmodel string - Security model 'SNMPv1', 'SNMPv2', 'USM'
1382 -- @param secname string - Security name.
1383 -- @param groupname string - Group name.
1384 -- @return varlist or nil + erromessage on failure
1385 ------------------------------------------------------------------------------
1386 function createsectogroup(session, secmodel, secname, groupname)
1387 -- Sanity checks
1388 if type(secmodel) ~= "string" then return FAIL(BADARG, "secmodel") end
1389 secmodel = secmodels[string.upper(secmodel)]
1390 if type(secname) ~= "string" then return FAIL(BADARG, "secname") end
1391 if type(groupname) ~= "string" then return FAIL(BADARG, "groupname") end
1393 local vl =
1394 newvar(vacmOid.sec2GroupStatus..instance(secmodel, secname),
1395 rowStatus.createAndGo) ..
1396 newvar(vacmOid.groupName..instance(secmodel, secname),
1397 groupname)
1398 -- set request
1399 local vl, err, errindex = session:set(vl)
1400 return retvarlist(vl, err, errindex)
1404 ------------------------------------------------------------------------------
1405 -- Delete security name to group mapping.
1406 -- @param session table - Valid session.
1407 -- @param secmodel string - Security model 'SNMPv1', 'SNMPv2', 'USM'
1408 -- @param secname string - Security name.
1409 -- @return varlist or nil + erromessage on failure
1410 ------------------------------------------------------------------------------
1411 function deletesectogroup(session, secmodel, secname)
1412 local err
1413 -- Sanity checks
1414 if type(secmodel) ~= "string" then FAIL(BADARG, "secmodel") end
1415 secmodel = secmodels[string.upper(secmodel)]
1417 if type(secname) ~= "string" then return FAIL(BADARG, "secname") end
1418 local vb = newvar(vacmOid.sec2GroupStatus..instance(secmodel, secname),
1419 rowStatus.destroy)
1421 -- set request
1422 vb, err = session:set(vb)
1423 return vb, err
1426 ------------------------------------------------------------------------------
1427 -- Create a view for a subtree
1428 -- @param session table - Valid session.
1429 -- @param viewname string - Name for the created view.
1430 -- @param subtree oid - OID of view's subtree.
1431 -- @param mask string - Bitmask.
1432 -- @param flag string - "exc[lude]" or "inc[lude]" (default).
1433 -- @return varlist or nil + erromessage on failure
1434 ------------------------------------------------------------------------------
1435 function createview(session, viewname, subtree, mask, flag)
1436 local err, included
1437 if type(viewname) ~= "string" then return
1438 nil, errtb[BADARG].." (viewname)"
1440 if not isoid(subtree) then return FAIL(BADARG, "subtree") end
1441 if type(mask) ~= "string" then return FAIL(BADARG, "mask") end
1442 local viewmask = ""
1443 string.gsub(mask, "([0-9A-Fa-f])([0-9A-Fa-f]*)",
1444 function(d1, d2)
1445 if d2 == "" then d2 = "0" end
1446 if string.len(d2) ~= 1 then
1447 return nil, errtb[BADARG] .. " (mask)" end
1448 viewmask = viewmask .. string.char(tonumber(d1,16)*16+tonumber(d2,16))
1449 end)
1450 if not flag then flag = "include" end
1451 flag = string.lower(flag)
1452 if string.sub(flag, 1,3) == "inc" then
1453 included = 1
1454 elseif string.sub(flag, 1, 3) == "exc" then
1455 included = 2
1457 local vl =
1458 newvar(vacmOid.viewTreeFamilyStatus .. instance(viewname, subtree),
1459 rowStatus.createAndGo) ..
1460 newvar(vacmOid.viewTreeFamilyMask .. instance(viewname, subtree),
1461 key2octet(viewmask)) ..
1462 newvar(vacmOid.viewTreeFamilyType .. instance(viewname, subtree),
1463 included)
1465 -- set request
1466 local vl, err, errindex = session:set(vl)
1467 return retvarlist(vl, err, errindex)
1470 ------------------------------------------------------------
1471 -- Delete a view.
1472 -- @param session table - Valid session.
1473 -- @param viewname string - Name for the created view.
1474 -- @param subtree oid - OID of view's subtree.
1475 -- @return varbind or nil + erromessage on failure
1476 ------------------------------------------------------------
1477 function deleteview(session, viewname, subtree)
1479 if type(viewname) ~= "string" then return FAIL(BADARG, "viewname") end
1480 if not isoid(subtree) then return FAIL(BADARG, "subtree") end
1481 local vb = newvar(vacmOid.viewTreeFamilyStatus .. instance(viewname, subtree),
1482 rowStatus.destroy)
1483 -- set request
1484 local vb, err = session:set(vb)
1485 return vb, err
1488 ------------------------------------------------------------------------------
1489 -- Create Access Entry.
1490 -- @param session table - Valid session.
1491 -- @param group string - Group name.
1492 -- @param secmodel string - Security model 'SNMPv1', 'SNMPv2', 'USM'
1493 -- @param seclevel string - Security level 'authNoPriv' etc.
1494 -- @param match string - View ma
1495 -- @param secname string - Security name.
1496 -- @return varlist or nil + erromessage on failure
1497 ------------------------------------------------------------------------------
1498 function createaccess(session, group, secmodel, seclevel, match,
1499 readview, writeview, notifyview, context)
1500 if type(secmodel) ~= "string" then return FAIL(BADARG, "secmodel") end
1501 secmodel = secmodels[string.upper(secmodel)]
1502 if type(seclevel) ~= "string" then return FAIL(BADARG, "secname") end
1503 if not seclevels[seclevel] then return FAIL(BADARG, "seclevel") end
1504 seclevel = seclevels[seclevel]
1505 if type(match) ~= "string" then return FAIL(BADARG, "match") end
1506 local nmatch
1507 if match == "exact" then
1508 nmatch = 1
1509 elseif match == "prefix" then
1510 nmatch = 2
1512 if not context then context = "" end
1513 local inst = instance(group, context, secmodel, seclevel)
1514 local vl =
1515 newvar(vacmOid.accessStatus .. inst, rowStatus.createAndGo) ..
1516 newvar(vacmOid.accessContextMatch .. inst, nmatch) ..
1517 newvar(vacmOid.accessReadViewName .. inst, key2octet(readview)) ..
1518 newvar(vacmOid.accessWriteViewName .. inst, key2octet(writeview)) ..
1519 newvar(vacmOid.accessNotifyViewName .. inst, key2octet(notifyview))
1521 -- set request
1522 local vl, err, errindex = session:set(vl)
1523 return retvarlist(vl, err, errindex)
1526 ------------------------------------------------------------------------------
1527 -- Delete security name to group mapping.
1528 -- @param session table - Valid session.
1529 -- @param secmodel string - Security model 'SNMPv1', 'SNMPv2', 'USM'
1530 -- @param secname string - Security name.
1531 -- @return varlist or nil + erromessage on failure
1532 ------------------------------------------------------------------------------
1533 function deleteaccess(session, group, secmodel, seclevel, context)
1534 if type(secmodel) ~= "string" then return FAIL(BADARG, "secmodel") end
1535 secmodel = secmodels[string.upper(secmodel)]
1536 if type(seclevel) ~= "string" or not seclevels[seclevel] then
1537 return FAIL(BADARG, "seclevel")
1539 seclevel = seclevels[seclevel]
1541 if not context then context = "" end
1543 local inst = instance(group, context, secmodel, seclevel)
1544 local vb = newvar(vacmOid.accessStatus .. inst, rowStatus.destroy)
1546 -- set request
1547 local vb, err = session:set(vb)
1548 return vb, err
1551 ------------------------------------------------------------------------------
1552 -- Clones a valid session.
1553 -- Note: We disable session cloning because it leads to mangling of NET-SNMPs
1554 -- user list and hence doesn't work reliable enough. (NET-SNMP 5.7)
1555 -- @param parent table - Parent session.
1556 -- @param config table - Configuration (overrides parent's configuration)
1557 -- @return New session if o.k. Nil + error message otherwise.
1558 ------------------------------------------------------------------------------
1559 function __clone(parent, config)
1560 local new = {parent=parent}
1561 config = config or {}
1562 -- sanity checks
1563 if config.version then
1564 if config.version ~= parent.version then return FAIL(BADVERSION) end
1566 for k, v in pairs(parent) do
1567 if parent.version ~= SNMPv3 then
1568 if (k ~= "inform") then
1569 new[k] = v
1571 else
1572 if config.password then
1573 if (k ~= "authPassphrase") and (k ~= "privPathphrase") then
1574 new[k] = v
1576 else
1577 new[k] = v
1581 for k, v in pairs(config or {}) do
1582 new[k] = config[k]
1584 return open(new)
1587 ------------------------------------------------------------------------------
1588 -- Table with session properties accessible using session.NAME using
1589 -- __index metamethod. Used to retrieve C internals provided by
1590 -- snmp.details() function.
1591 ------------------------------------------------------------------------------
1592 local sessionproperties = {
1593 contextEngineID = function(self) return details(self).contextEngineID end,
1594 contextEngineIDLen = function(self) return details(self).contextEngineIDLen end,
1595 securityEngineID = function(self) return details(self).securityEngineID end,
1596 securityEngineIDLen = function(self) return details(self).securityEngineIDLen end,
1597 engineBoots = function(self) return details(self).engineBoots end,
1598 engineTime = function(self) return details(self).engineTime end,
1599 isAuthoritative = function(self) return details(self).isAuthoritative end
1602 ---------------------------------------------------------------------------
1603 -- Create a new SNMP session.
1604 -- @param session table - Base parameters.
1605 -- @return Active session.
1606 ---------------------------------------------------------------------------
1607 function open (session)
1609 if not session then
1610 session = {}
1613 -- Be sure to have the correct parameter type
1614 if type(session)~="table" then
1615 return FAIL(BADSESSION)
1618 if not session.name then
1619 session.name = "nobody"
1622 -- SNMP Version: v1, v2c (default) or v3
1623 if not session.version then
1624 session.version=tonumber(config.defVersion) or SNMPv2C
1626 if (session.version ~= SNMPv1 and
1627 session.version ~= SNMPv2C and
1628 session.version ~= SNMPv3) then
1629 return FAIL(BADVERSION)
1632 -- Printing variables
1633 if session.sprintvar then
1634 if type(session.sprintvar ) ~= "function" then
1635 return FAIL(BADPRINTVAR)
1637 else
1638 session.sprintvar = sprint_variable
1641 -- Printing values
1642 if session.sprintval then
1643 if type(session.sprintval ) ~= "function" then
1644 return FAIL(BADPRINTVAL)
1646 else
1647 session.sprintval = sprint_value
1650 -- Version 2: community: any or public (default)
1651 if session.version == SNMPv2c then
1652 if not session.community then
1653 session.community = config.defCommunity or "public"
1655 if type(session.community)~="string" then
1656 return FAIL(BADCOMMUNITY)
1660 -- Version 3: ...
1661 if session.version == SNMPv3 then
1663 -- username: any
1664 if not session.user then
1665 session.user = config.defSecurityName
1667 if not session.user then
1668 return FAIL(BADUSER)
1671 -- security level: authPriv, authNoPriv (default) or noAuthNoPriv
1672 if not session.securityLevel then
1673 session.securityLevel = config.defSecurityLevel or "authNoPriv"
1675 if not seclevels[session.securityLevel] then
1676 return FAIL(BADSECLEVEL)
1677 else
1678 session._securityLevel = seclevels[session.securityLevel]
1681 -- authentication protocol: MD5 (default) or SHA
1682 if not session.authType then
1683 session.authType = config.defAuthType or "MD5"
1686 -- authentication passphrase: any
1687 if not session.password and not session.authPassphrase then
1688 session.authPassphrase = config.defPassphrase or config.defAuthPassphrase
1689 else
1690 session.authPassphrase = session.authPassphrase or session.password
1692 if not session.authPassphrase then
1693 return FAIL(BADPASSPHRASE)
1696 -- encryption protocol: DES (default) or AES
1697 if not session.privType then
1698 session.privpType = config.defPrivType or "DES"
1701 -- privacy passphrase: any
1702 if not session.password and not session.privPassphrase then
1703 session.privPassphrase = config.defPassphrase or config.defPrivPassphrase
1704 else
1705 session.privPassphrase = session.privPassphrase or session.password
1707 if not session.privPassphrase then
1708 return FAIL(BADPRIVPASSPHRASE)
1711 -- context:
1712 if not session.context then
1713 session.context = ""
1716 -- authentication engine ID:
1717 if not session.engineID then
1718 session.engineID = nil
1721 -- context engine ID:
1722 if not session.contextID then
1723 session.contextID = session.engineID
1726 -- engine boots: not supported yet
1727 if not session.engineBoots then
1728 session.engboots = nil
1729 else
1730 return FAIL(BADENGBOOTS)
1733 -- engine time: not supported yet
1734 if not session.engineTime then
1735 session.engtime = nil
1736 else
1737 return FAIL(BADENGTIME)
1741 -- timeout: any or 1s (default)
1742 if not session.timeout then
1743 session.timeout = 1
1744 else
1745 session.timeout = tonumber(session.timeout)
1746 if not session.timeout then
1747 return FAIL(BADTIME)
1748 elseif session.timeout < 0 then
1749 return FAIL(BADTIME)
1753 -- retries: any or 5 (default)
1754 if not session.retries then
1755 session.retries = 5
1756 else
1757 session.retries = tonumber(session.retries)
1758 if not session.retries then
1759 return FAIL(BADRETRIES)
1760 elseif session.retries < 0 then
1761 return FAIL(BADRETRIES)
1765 -- agent: any or 0.0.0.0 (default)
1766 if not session.peer then
1767 session.peer = "0.0.0.0"
1768 elseif type(session.peer)~="string" then
1769 return FAIL(BADPEER)
1772 -- port: any or 161 (default)
1773 if not session.port then
1774 session.port = config.defaultPort or 161
1775 else
1776 session.port = tonumber(session.port)
1777 if not session.port then
1778 return FAIL(BADPORT)
1779 elseif session.port < 0 then
1780 return FAIL(BADPORT)
1784 -- default callback:
1785 if session.callback then
1786 if type(session.callback) ~= "function" then
1787 return FAIL(BADCALLBACK)
1791 -- trap callback:
1792 if session.trap then
1793 if type(session.trap) ~= "function" then
1794 return FAIL(BADTRAP)
1796 session.usertrap = session.trap
1797 session.trap = __trap
1800 -- inform callback:
1801 if session.inform then
1802 if session.version == SNMPv1 then
1803 return FAIL(INVINFO)
1805 if type(session.inform) ~= "function" then
1806 return FAIL(BADINFO)
1810 -- walk control
1811 if session.includeroot then
1812 if session.version == SNMPv1 then
1813 return FAIL(INVINFO)
1815 if type(session.includeroot) ~= "boolean" then
1816 return FAIL(BADINCL)
1818 else
1819 session.includeroot = false
1823 -- This opens the net-snmp session
1825 local intsess, ip_err = _open(session)
1826 if not intsess then return nil, ip_err end
1828 -- Informationals
1829 session.verid = "LuaSNMP " .. snmp.version
1830 session.internal = intsess
1831 session.peer_ip = ip_err
1832 session.getversion = function(self)
1833 if not self then
1834 return FAIL(BADSESSION)
1836 if self.version == SNMPv1 then
1837 return "SNMPv1"
1838 elseif self.version == SNMPv2c then
1839 return "SNMPv2c"
1840 elseif self.version == SNMPv3 then
1841 return "SNMPv3"
1842 else
1843 return FAIL(BADSESSION)
1847 -- map snmp methods to the session
1848 session.wait = wait
1849 session.get = get
1850 session.getnext = getnext
1851 session.getbulk = getbulk
1852 session.asynch_get = asynch_get
1853 session.asynch_getnext = asynch_getnext
1854 session.asynch_getbulk = asynch_getbulk
1855 session.close = function(self)
1856 -- We need to remove the running user from library's userList.
1857 -- Otherwise a new session with new passowrd to the same machine (engineID) fails.
1858 if session.version == SNMPv3 then
1859 self:removeuser(rawget(self,"user"))
1861 return close(session)
1863 session.set = set
1864 session.asynch_set = asynch_set
1865 session.inform = inform
1866 session.asynch_inform = asynch_inform
1867 session.walk = walk
1868 session.newvar = function(session, name, value, type)
1869 return newvar(name, value, type, session)
1871 -- session.clone = clone
1872 session.newpassword = newpassword
1873 session.createuser = createuser
1874 session.deleteuser = deleteuser
1875 session.clonefromuser = clonefromuser
1876 session.details = details
1877 session.createsectogroup = createsectogroup
1878 session.deletesectogroup = deletesectogroup
1879 session.createview = createview
1880 session.deleteview = deleteview
1881 session.createaccess = createaccess
1882 session.deleteaccess = deleteaccess
1883 session.removeuser = removeuser
1885 -- A cache with weak values for SNMP object type cacheing.
1887 session.cache ={}
1888 setmetatable(session.cache, {__mode="v"})
1889 setmetatable(session, {
1891 -- Syntactic sugar: val = sess.OBJECT <=> val = sess:get(OBJECT)
1892 __index = function(self, key)
1893 if sessionproperties[key] then
1894 return sessionproperties[key](self)
1895 else
1896 local name = string.gsub(key, "_",".")
1897 local rv, err = self:walk(name)
1898 try(not err, err)
1899 if #rv == 1 then
1900 -- No successor and single: return scalar
1901 return rv[1].value
1902 else
1903 -- No successor and multiple: return list of values
1904 local t = {}
1905 for _,v in ipairs(rv) do
1906 t[mib.name(v.oid)] = v.value
1908 return t
1911 end,
1912 -- Syntactic sugar: sess.OBJECT = val <=> sess:set(OBJECT)
1913 __newindex = function(self, key, value)
1914 local name = string.gsub(key, "_",".")
1915 -- Value is a table. Set all values - recursive.
1916 if type(value) == "table" then
1917 for k,v in pairs(v) do
1918 self[k] = v
1920 else
1921 return try(self:set{oid=name, value=value})
1923 end,
1924 __newindex2 = function(self, key, value)
1925 local name = string.gsub(key, "_",".")
1926 -- Value is a table. Set all values - recursive.
1927 if type(value) == "table" then
1928 for k,v in pairs(v) do
1929 self[k] = v
1932 -- Look for an entry in the varbinding cache
1933 local entry = self.cache[name]
1934 if entry then
1935 -- found one: just enter the value
1936 entry.value=value
1937 return try(self:set(entry))
1938 else
1939 -- no entry found: construct a new varbinding and
1940 -- put it into the cache.
1941 local oid = mib.oid(name)
1942 local entry = {oid=oid, type=mib.type(oid), value=value}
1943 self.cache[name] = entry
1944 return try(self:set(entry))
1946 end,
1949 -- Return the session handle
1950 return session
1954 local _assert = _G.assert
1955 ---------------------------------------------------------------------------
1956 -- Check result of a function.
1957 -- The function performs an extended check on both exp == true and a nil
1958 -- error message instead of just check the expression exp as assert does.
1959 -- @param exp boolean - Expression to check.
1960 -- @param err string - Error message to evaluate.
1961 -- @return exp if (exp == true) and (err == nil), exit otherwise
1962 ---------------------------------------------------------------------------
1963 function check(exp, err, ...)
1964 if exp ~= nil and not err then
1965 return exp
1966 else
1967 _assert(exp, err or "unknown")
1971 return snmp