netsnmp 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +42 -0
- data/AUTHORS +1 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +201 -0
- data/README.md +162 -0
- data/Rakefile +26 -0
- data/lib/netsnmp.rb +16 -0
- data/lib/netsnmp/client.rb +131 -0
- data/lib/netsnmp/core.rb +12 -0
- data/lib/netsnmp/core/client.rb +15 -0
- data/lib/netsnmp/core/constants.rb +153 -0
- data/lib/netsnmp/core/inline.rb +20 -0
- data/lib/netsnmp/core/libc.rb +48 -0
- data/lib/netsnmp/core/libsnmp.rb +44 -0
- data/lib/netsnmp/core/structures.rb +167 -0
- data/lib/netsnmp/core/utilities.rb +13 -0
- data/lib/netsnmp/errors.rb +8 -0
- data/lib/netsnmp/handlers/celluloid.rb +27 -0
- data/lib/netsnmp/handlers/em.rb +56 -0
- data/lib/netsnmp/oid.rb +94 -0
- data/lib/netsnmp/pdu.rb +105 -0
- data/lib/netsnmp/session.rb +306 -0
- data/lib/netsnmp/varbind.rb +181 -0
- data/lib/netsnmp/version.rb +3 -0
- data/netsnmp.gemspec +36 -0
- data/spec/client_spec.rb +90 -0
- data/spec/core/libc_spec.rb +2 -0
- data/spec/core/libsnmp_spec.rb +32 -0
- data/spec/core/structures_spec.rb +54 -0
- data/spec/handlers/celluloid_spec.rb +29 -0
- data/spec/handlers/em_client_spec.rb +34 -0
- data/spec/oid_spec.rb +9 -0
- data/spec/pdu_spec.rb +29 -0
- data/spec/session_spec.rb +34 -0
- data/spec/spec_helper.rb +114 -0
- data/spec/support/Dockerfile +14 -0
- data/spec/support/celluloid.rb +22 -0
- data/spec/support/start_docker.sh +4 -0
- data/spec/support/stop_docker.sh +5 -0
- data/spec/varbind_spec.rb +54 -0
- metadata +187 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
module NETSNMP::Core
|
2
|
+
# Maps to the relevant netsnmp C library structs.
|
3
|
+
module Structures
|
4
|
+
extend FFI::Library
|
5
|
+
typedef :u_long, :oid
|
6
|
+
|
7
|
+
callback(:snmp_callback, [ :int, :pointer, :int, :pointer, :pointer ], :int)
|
8
|
+
callback(:netsnmp_callback, [ :int, :pointer, :int, :pointer, :pointer ], :int)
|
9
|
+
|
10
|
+
class SessionList < FFI::Struct
|
11
|
+
layout(
|
12
|
+
:next, :pointer,
|
13
|
+
:session, :pointer,
|
14
|
+
:transport, :pointer,
|
15
|
+
:internal, :pointer
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Transport < FFI::Struct
|
20
|
+
layout(
|
21
|
+
:domain, :pointer,
|
22
|
+
:domain_length, :int,
|
23
|
+
:local, :pointer,
|
24
|
+
:local_length, :int,
|
25
|
+
:remote, :pointer,
|
26
|
+
:remote_length, :int,
|
27
|
+
:sock, :int,
|
28
|
+
:flags, :u_int,
|
29
|
+
:data, :pointer,
|
30
|
+
:data_length, :int,
|
31
|
+
:msgMaxSize, :size_t,
|
32
|
+
:base_transport, :pointer
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Session < FFI::Struct
|
37
|
+
layout(
|
38
|
+
:version, :long,
|
39
|
+
:retries, :int,
|
40
|
+
:timeout, :long,
|
41
|
+
:flags, :u_long,
|
42
|
+
:subsession, :pointer,
|
43
|
+
:next, :pointer,
|
44
|
+
:peername, :pointer,
|
45
|
+
:remote_port, :u_short,
|
46
|
+
:localname, :pointer,
|
47
|
+
:local_port, :u_short,
|
48
|
+
:authenticator, callback([ :pointer, :pointer, :pointer, :uint ], :pointer),
|
49
|
+
:callback, :netsnmp_callback,
|
50
|
+
:callback_magic, :pointer,
|
51
|
+
:s_errno, :int,
|
52
|
+
:s_snmp_errno, :int,
|
53
|
+
:sessid, :long,
|
54
|
+
:community, :pointer,
|
55
|
+
:community_len, :size_t,
|
56
|
+
:rcvMsgMaxSize, :size_t,
|
57
|
+
:sndMsgMaxSize, :size_t,
|
58
|
+
:isAuthoritative, :u_char,
|
59
|
+
:contextEngineID, :pointer,
|
60
|
+
:contextEngineIDLen, :size_t,
|
61
|
+
:engineBoots, :u_int,
|
62
|
+
:engineTime, :u_int,
|
63
|
+
:contextName, :pointer,
|
64
|
+
:contextNameLen, :size_t,
|
65
|
+
:securityEngineID, :pointer,
|
66
|
+
:securityEngineIDLen, :size_t,
|
67
|
+
:securityName, :pointer,
|
68
|
+
:securityNameLen, :size_t,
|
69
|
+
:securityAuthProto, :pointer,
|
70
|
+
:securityAuthProtoLen, :size_t,
|
71
|
+
:securityAuthKey, [:u_char, 32],
|
72
|
+
:securityAuthKeyLen, :size_t,
|
73
|
+
:securityAuthLocalKey, :pointer,
|
74
|
+
:securityAuthLocalKeyLen, :size_t,
|
75
|
+
:securityPrivProto, :pointer,
|
76
|
+
:securityPrivProtoLen, :size_t,
|
77
|
+
:securityPrivKey, [:u_char, 32],
|
78
|
+
:securityPrivKeyLen, :size_t,
|
79
|
+
:securityPrivLocalKey, :pointer,
|
80
|
+
:securityPrivLocalKeyLen, :size_t,
|
81
|
+
:securityModel, :int,
|
82
|
+
:securityLevel, :int,
|
83
|
+
:paramName, :pointer,
|
84
|
+
:securityInfo, :pointer,
|
85
|
+
:myvoid, :pointer
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
class Vardata < FFI::Union
|
90
|
+
layout(
|
91
|
+
:integer, :pointer,
|
92
|
+
:string, :pointer,
|
93
|
+
:objid, :pointer,
|
94
|
+
:bitstring, :pointer,
|
95
|
+
:counter64, :pointer,
|
96
|
+
:float, :pointer,
|
97
|
+
:double, :pointer
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
class VariableList < FFI::Struct
|
102
|
+
layout(
|
103
|
+
:next_variable, :pointer,#VariableList.typed_pointer,
|
104
|
+
:name, :pointer,
|
105
|
+
:name_length, :size_t,
|
106
|
+
:type, :u_char,
|
107
|
+
:val, Vardata,
|
108
|
+
:val_len, :size_t,
|
109
|
+
:name_loc, [:oid, Constants::MAX_OID_LEN],
|
110
|
+
:buf, [:u_char, 40],
|
111
|
+
:data, :pointer,
|
112
|
+
:dataFreeHook, callback([ :pointer ], :void),
|
113
|
+
:index, :int
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
class PDU < FFI::Struct
|
119
|
+
layout(
|
120
|
+
:version, :long,
|
121
|
+
:command, :int,
|
122
|
+
:reqid, :long,
|
123
|
+
:msgid, :long,
|
124
|
+
:transid, :long,
|
125
|
+
:sessid, :long,
|
126
|
+
:errstat, :long,
|
127
|
+
:errindex, :long,
|
128
|
+
:time, :u_long,
|
129
|
+
:flags, :u_long,
|
130
|
+
:securityModel, :int,
|
131
|
+
:securityLevel, :int,
|
132
|
+
:msgParseModel, :int,
|
133
|
+
:transport_data, :pointer,
|
134
|
+
:transport_data_length, :int,
|
135
|
+
:tDomain, :pointer,
|
136
|
+
:tDomainLen, :size_t,
|
137
|
+
:variables, :pointer,
|
138
|
+
:community, :pointer,
|
139
|
+
:community_len, :size_t,
|
140
|
+
:enterprise, :pointer,
|
141
|
+
:enterprise_length, :size_t,
|
142
|
+
:trap_type, :long,
|
143
|
+
:specific_type, :long,
|
144
|
+
:agent_addr, [:uchar, 4],
|
145
|
+
:contextEngineID, :pointer,
|
146
|
+
:contextEngineIDLen, :size_t,
|
147
|
+
:contextName, :pointer,
|
148
|
+
:contextNameLen, :size_t,
|
149
|
+
:securityEngineID, :pointer,
|
150
|
+
:securityEngineIDLen, :size_t,
|
151
|
+
:securityName, :pointer,
|
152
|
+
:securityNameLen, :size_t,
|
153
|
+
:priority, :int,
|
154
|
+
:range_subid, :int,
|
155
|
+
:securityStateRef, :pointer
|
156
|
+
)
|
157
|
+
end
|
158
|
+
|
159
|
+
class Counter64 < FFI::Struct
|
160
|
+
layout(
|
161
|
+
:high, :u_long,
|
162
|
+
:low, :u_long
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module NETSNMP::Core
|
2
|
+
|
3
|
+
def self.version
|
4
|
+
LibSNMP.netsnmp_get_version
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
# Do not support versions lower than 5.5, as they're mostly buggy.
|
9
|
+
if version < "5.5"
|
10
|
+
raise LoadError, "The netsnmp version #{version} is incompatible with this version of ffi-netsnmp-core"
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'netsnmp'
|
2
|
+
module NETSNMP
|
3
|
+
module Celluloid
|
4
|
+
class Client < ::NETSNMP::Client
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@session = Celluloid::Session.new(*args)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class Session < ::NETSNMP::Session
|
14
|
+
def wait_readable
|
15
|
+
return super unless ::Celluloid::IO.evented?
|
16
|
+
::Celluloid::IO.wait_readable(transport)
|
17
|
+
[[transport]]
|
18
|
+
end
|
19
|
+
|
20
|
+
def wait_writable
|
21
|
+
return super unless ::Celluloid::IO.evented?
|
22
|
+
::Celluloid::IO.wait_writable(transport)
|
23
|
+
[[],[transport]]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'netsnmp'
|
2
|
+
module NETSNMP
|
3
|
+
module EM
|
4
|
+
class Client < ::NETSNMP::Client
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@session = EM::Session.new(*args)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class Session < ::NETSNMP::Session
|
14
|
+
module Watcher
|
15
|
+
def initialize(client, deferable)
|
16
|
+
@client = client
|
17
|
+
@deferable = deferable
|
18
|
+
@is_watching = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_readable
|
22
|
+
detach
|
23
|
+
begin
|
24
|
+
operation = nil
|
25
|
+
@client.__send__ :async_read
|
26
|
+
result = @client.__send__ :handle_response
|
27
|
+
rescue => e
|
28
|
+
@deferable.fail(e)
|
29
|
+
else
|
30
|
+
@deferable.succeed(result)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def watching?
|
35
|
+
@is_watching
|
36
|
+
end
|
37
|
+
|
38
|
+
def unbind
|
39
|
+
@is_watching = false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def send(pdu)
|
44
|
+
if ::EM.reactor_running?
|
45
|
+
write(pdu)
|
46
|
+
deferable = ::EM::DefaultDeferrable.new
|
47
|
+
watch = ::EM.watch(transport.fileno, Watcher, self, deferable)
|
48
|
+
watch.notify_readable = true
|
49
|
+
::EM::Synchrony.sync deferable
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/netsnmp/oid.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
# Abstracts the OID structure
|
3
|
+
#
|
4
|
+
class OID
|
5
|
+
Error = Class.new(Error)
|
6
|
+
OIDREGEX = /^[\d\.]*$/
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# @return [Integer] the default oid size in bytes
|
11
|
+
def default_size
|
12
|
+
@default_size ||= Core::Inline.oid_size
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [FFI::Pointer] pointer the pointer to the beginning ot the memory octet
|
16
|
+
# @param [Integer] length the length of the oid
|
17
|
+
# @return [String] the oid code (ex: "1.2.4.56.3.4.5"...)
|
18
|
+
#
|
19
|
+
def read_pointer(pointer, length)
|
20
|
+
pointer.__send__(:"read_array_of_uint#{default_size * 8}", length).join('.')
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see read_pointer
|
24
|
+
# @return [OID] an OID object initialized from a code read from memory
|
25
|
+
#
|
26
|
+
def from_pointer(pointer, length)
|
27
|
+
new(read_pointer(pointer, length))
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :code
|
33
|
+
|
34
|
+
# @param [String] code the oid code
|
35
|
+
#
|
36
|
+
def initialize(code)
|
37
|
+
@struct = FFI::MemoryPointer.new(OID::default_size * Core::Constants::MAX_OID_LEN)
|
38
|
+
@length_pointer = FFI::MemoryPointer.new(:size_t)
|
39
|
+
@length_pointer.write_int(Core::Constants::MAX_OID_LEN)
|
40
|
+
existing_oid = case code
|
41
|
+
when OIDREGEX then Core::LibSNMP.read_objid(code, @struct, @length_pointer)
|
42
|
+
else Core::LibSNMP.get_node(code, @struct, @length_pointer)
|
43
|
+
end
|
44
|
+
raise Error, "unsupported oid: #{code}" if existing_oid.zero?
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [String] the oid code
|
48
|
+
#
|
49
|
+
def code ; @code ||= OID.read_pointer(pointer, length) ; end
|
50
|
+
|
51
|
+
# @return [String] the pointer to the structure
|
52
|
+
#
|
53
|
+
def pointer ; @struct ; end
|
54
|
+
|
55
|
+
# @return [Integer] length of the oid
|
56
|
+
#
|
57
|
+
def length ; @length_pointer.read_int ; end
|
58
|
+
|
59
|
+
# @return [Integer] size of the oid
|
60
|
+
#
|
61
|
+
def size ; length * NETSNMP::OID.default_size ; end
|
62
|
+
|
63
|
+
def to_s ; code ; end
|
64
|
+
|
65
|
+
# @param [OID, String] child oid another oid
|
66
|
+
# @return [true, false] whether the given OID belongs to the sub-tree
|
67
|
+
#
|
68
|
+
def parent_of?(child_oid)
|
69
|
+
child_code = child_oid.is_a?(OID) ? child_oid.code : child_oid
|
70
|
+
child_code.start_with?(code)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# SNMP v3-relevant OIDs
|
75
|
+
|
76
|
+
class MD5OID < OID
|
77
|
+
def initialize ; super("1.3.6.1.6.3.10.1.1.2") ; end
|
78
|
+
end
|
79
|
+
class SHA1OID < OID
|
80
|
+
def initialize ; super("1.3.6.1.6.3.10.1.1.3") ; end
|
81
|
+
end
|
82
|
+
class NoAuthOID < OID
|
83
|
+
def initialize ; super("1.3.6.1.6.3.10.1.1.1") ; end
|
84
|
+
end
|
85
|
+
class AESOID < OID
|
86
|
+
def initialize ; super("1.3.6.1.6.3.10.1.2.4") ; end
|
87
|
+
end
|
88
|
+
class DESOID < OID
|
89
|
+
def initialize ; super("1.3.6.1.6.3.10.1.2.2") ; end
|
90
|
+
end
|
91
|
+
class NoPrivOID < OID
|
92
|
+
def initialize ; super("1.3.6.1.6.3.10.1.2.1") ; end
|
93
|
+
end
|
94
|
+
end
|
data/lib/netsnmp/pdu.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module NETSNMP
|
3
|
+
# Abstracts the PDU base structure into a ruby object. It gives access to its varbinds.
|
4
|
+
#
|
5
|
+
class PDU
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
Error = Class.new(Error)
|
9
|
+
class << self
|
10
|
+
# factory method that abstracts initialization of the pdu types that the library supports.
|
11
|
+
#
|
12
|
+
# @param [Symbol] type the type of pdu structure to build
|
13
|
+
# @return [RequestPDU] a fully-formed request pdu
|
14
|
+
#
|
15
|
+
def build(type, *args)
|
16
|
+
case type
|
17
|
+
when :get then RequestPDU.new(Core::Constants::SNMP_MSG_GET, *args)
|
18
|
+
when :getnext then RequestPDU.new(Core::Constants::SNMP_MSG_GETNEXT, *args)
|
19
|
+
when :getbulk then RequestPDU.new(Core::Constants::SNMP_MSG_GETBULK, *args)
|
20
|
+
when :set then RequestPDU.new(Core::Constants::SNMP_MSG_SET, *args)
|
21
|
+
when :response then ResponsePDU.new(Core::Constants::SNMP_MSG_RESPONSE, *args)
|
22
|
+
else raise Error, "#{type} is not supported as type"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :struct, :varbinds
|
28
|
+
|
29
|
+
def_delegators :@struct, :[], :[]=, :pointer
|
30
|
+
|
31
|
+
# @param [FFI::Pointer] the pointer to the initialized structure
|
32
|
+
#
|
33
|
+
def initialize(pointer)
|
34
|
+
@struct = Core::Structures::PDU.new(pointer)
|
35
|
+
@varbinds = []
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
# Abstracts the request PDU
|
42
|
+
# Main characteristic is that it has a write-only API, in that you can add varbinds to it.
|
43
|
+
#
|
44
|
+
class RequestPDU < PDU
|
45
|
+
def initialize(type)
|
46
|
+
pointer = Core::LibSNMP.snmp_pdu_create(type)
|
47
|
+
super(pointer)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Adds a request varbind to the pdu
|
51
|
+
#
|
52
|
+
# @param [OID] oid a valid oid
|
53
|
+
# @param [Hash] options additional request varbind options
|
54
|
+
# @option options [Object] :value the value for the oid
|
55
|
+
def add_varbind(oid, **options)
|
56
|
+
@varbinds << RequestVarbind.new(self, oid, options[:value], options)
|
57
|
+
end
|
58
|
+
alias_method :<<, :add_varbind
|
59
|
+
end
|
60
|
+
|
61
|
+
# Abstracts the response PDU
|
62
|
+
# Main characteristic is: it reads the values on initialization (because the response structure
|
63
|
+
# is at some point free'd). It is therefore a read-only entity
|
64
|
+
#
|
65
|
+
class ResponsePDU < PDU
|
66
|
+
|
67
|
+
# @param [FFI::Pointer] the pointer to the response pdu structure
|
68
|
+
#
|
69
|
+
# @note it loads the variable as well.
|
70
|
+
#
|
71
|
+
def initialize(pointer)
|
72
|
+
super
|
73
|
+
load_variables
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [String] the concatenation of the varbind values (usually, it's only one)
|
77
|
+
#
|
78
|
+
def value
|
79
|
+
case @varbinds.size
|
80
|
+
when 0 then nil
|
81
|
+
when 1 then @varbinds.first.value
|
82
|
+
else
|
83
|
+
# assume that they're all strings
|
84
|
+
@varbinds.map(&:value).join(' ')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# loads the C-level structure variables into ruby ResponseVarbind objects,
|
91
|
+
# and store them as state in {{@varbinds}}
|
92
|
+
def load_variables
|
93
|
+
variable = @struct[:variables]
|
94
|
+
unless variable.null?
|
95
|
+
@varbinds << ResponseVarbind.new(variable)
|
96
|
+
variable = Core::Structures::VariableList.new(variable)
|
97
|
+
while( !(variable = variable[:next_variable]).null? )
|
98
|
+
variable = Core::Structures::VariableList.new(variable)
|
99
|
+
@varbinds << ResponseVarbind.new(variable.pointer)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|