Skip to content

Commit

Permalink
Populate MIB data in ObjectIds that are created through Manager requests
Browse files Browse the repository at this point in the history
  • Loading branch information
hallidave committed Oct 23, 2011
1 parent cd80693 commit c367344
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 127 deletions.
206 changes: 108 additions & 98 deletions lib/snmp/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

module SNMP

class RequestTimeout < RuntimeError; end
class RequestTimeout < RuntimeError;
end

##
# Wrap socket so that it can be easily substituted for testing or for
Expand Down Expand Up @@ -55,7 +56,7 @@ def next
@lock.synchronize do
@request_id += 1
@request_id = 1 if @request_id == MAX_REQUEST_ID
return @request_id
return @request_id
end
end

Expand Down Expand Up @@ -117,19 +118,19 @@ def force_next(next_id)
class Manager

class Config < Options
option :host, :Host, 'localhost'
option :port, :Port, 161
option :trap_port, :TrapPort, 162
option :community, :Community, 'public'
option :write_community, :WriteCommunity, lambda { |c| c.community }
option :version, :Version, :SNMPv2c
option :timeout, :Timeout, 1
option :retries, :Retries, 5
option :transport, :Transport, UDPTransport
option :max_recv_bytes, :MaxReceiveBytes, 8000
option :mib_dir, :MibDir, MIB::DEFAULT_MIB_PATH
option :mib_modules, :MibModules, default_modules
option :use_IPv6, :use_IPv6, lambda { |c| ipv6_address?(c) }
option :host, :Host, 'localhost'
option :port, :Port, 161
option :trap_port, :TrapPort, 162
option :community, :Community, 'public'
option :write_community, :WriteCommunity, lambda { |c| c.community }
option :version, :Version, :SNMPv2c
option :timeout, :Timeout, 1
option :retries, :Retries, 5
option :transport, :Transport, UDPTransport
option :max_recv_bytes, :MaxReceiveBytes, 8000
option :mib_dir, :MibDir, MIB::DEFAULT_MIB_PATH
option :mib_modules, :MibModules, default_modules
option :use_IPv6, :use_IPv6, lambda { |c| ipv6_address?(c) }

def create_transport
transport.respond_to?(:new) ? transport.new(socket_address_family) : transport
Expand Down Expand Up @@ -274,10 +275,10 @@ def get_next(object_list)
def get_bulk(non_repeaters, max_repetitions, object_list)
varbind_list = @mib.varbind_list(object_list, :NullValue)
request = GetBulkRequest.new(
@@request_id.next,
varbind_list,
non_repeaters,
max_repetitions)
@@request_id.next,
varbind_list,
non_repeaters,
max_repetitions)
try_request(request)
end

Expand Down Expand Up @@ -417,7 +418,7 @@ def walk(object_list, index_column=0)
vb_list = @mib.varbind_list(object_list, :NullValue)
raise ArgumentError, "index_column is past end of varbind list" if index_column >= vb_list.length
is_single_vb = object_list.respond_to?(:to_str) ||
object_list.respond_to?(:to_varbind)
object_list.respond_to?(:to_varbind)
start_list = vb_list
start_oid = vb_list[index_column].name
last_oid = start_oid
Expand Down Expand Up @@ -455,12 +456,13 @@ def validate_row(vb_list, start_list, index_column)
if i != index_column
expected_oid = start_list[i].name + row_index
if vb_list[i].name != expected_oid
vb_list[i] = VarBind.new(expected_oid, NoSuchInstance)
vb_list[i] = VarBind.new(expected_oid, NoSuchInstance).with_mib(@mib)
end
end
end
vb_list
end

private :validate_row

##
Expand All @@ -473,50 +475,50 @@ def next_request_id=(request_id)

private

def warn(message)
trace = caller(2)
location = trace[0].sub(/:in.*/,'')
Kernel::warn "#{location}: warning: #{message}"
end
def warn(message)
trace = caller(2)
location = trace[0].sub(/:in.*/, '')
Kernel::warn "#{location}: warning: #{message}"
end

def load_modules(module_list, mib_dir)
module_list.each { |m| @mib.load_module(m, mib_dir) }
end
def load_modules(module_list, mib_dir)
module_list.each { |m| @mib.load_module(m, mib_dir) }
end

def try_request(request, community=@community, host=@host, port=@port)
(@retries + 1).times do |n|
send_request(request, community, host, port)
begin
Timeout.timeout(@timeout) do
return get_response(request)
end
rescue Timeout::Error
# no action - try again
rescue => e
warn e.to_s
def try_request(request, community=@community, host=@host, port=@port)
(@retries + 1).times do |n|
send_request(request, community, host, port)
begin
Timeout.timeout(@timeout) do
return get_response(request)
end
rescue Timeout::Error
# no action - try again
rescue => e
warn e.to_s
end
raise RequestTimeout, "host #{config[:host]} not responding", caller
end
raise RequestTimeout, "host #{config[:host]} not responding", caller
end

def send_request(request, community, host, port)
message = Message.new(@snmp_version, community, request)
@transport.send(message.encode, host, port)
end
def send_request(request, community, host, port)
message = Message.new(@snmp_version, community, request)
@transport.send(message.encode, host, port)
end

##
# Wait until response arrives. Ignore responses with mismatched IDs;
# these responses are typically from previous requests that timed out
# or almost timed out.
#
def get_response(request)
begin
data = @transport.recv(@max_bytes)
message = Message.decode(data)
response = message.pdu
end until request.request_id == response.request_id
response
end
##
# Wait until response arrives. Ignore responses with mismatched IDs;
# these responses are typically from previous requests that timed out
# or almost timed out.
#
def get_response(request)
begin
data = @transport.recv(@max_bytes)
message = Message.decode(data, @mib)
response = message.pdu
end until request.request_id == response.request_id
response
end
end

class UDPServerTransport
Expand Down Expand Up @@ -562,11 +564,13 @@ class Config < Options
option :community, :Community, 'public'
option :server_transport, :ServerTransport, UDPServerTransport
option :max_recv_bytes, :MaxReceiveBytes, 8000
option :mib_dir, :MibDir, MIB::DEFAULT_MIB_PATH
option :mib_modules, :MibModules, default_modules
option :use_IPv6, :use_IPv6, false

def create_transport
server_transport.respond_to?(:new) ?
server_transport.new(host, port, socket_address_family) : server_transport
server_transport.new(host, port, socket_address_family) : server_transport
end
end

Expand Down Expand Up @@ -596,6 +600,8 @@ def initialize(options={}, &block)
@transport = config.create_transport
@community = config.community
@max_bytes = config.max_recv_bytes
@mib = MIB.new
load_modules(config.mib_modules, config.mib_dir)
@config = config.applied_config

@handler_init = block
Expand Down Expand Up @@ -671,55 +677,59 @@ def exit

private

def process_traps(trap_listener)
@handler_init.call(trap_listener) if @handler_init
loop do
data, source_ip, source_port = @transport.recvfrom(@max_bytes)
begin
message = Message.decode(data)
if @community == message.community
trap = message.pdu
if trap.kind_of?(InformRequest)
@transport.send(message.response.encode, source_ip, source_port)
end
trap.source_ip = source_ip
select_handler(trap).call(trap)
def load_modules(module_list, mib_dir)
module_list.each { |m| @mib.load_module(m, mib_dir) }
end

def process_traps(trap_listener)
@handler_init.call(trap_listener) if @handler_init
loop do
data, source_ip, source_port = @transport.recvfrom(@max_bytes)
begin
message = Message.decode(data, @mib)
if @community == message.community
trap = message.pdu
if trap.kind_of?(InformRequest)
@transport.send(message.response.encode, source_ip, source_port)
end
rescue => e
puts "Error handling trap: #{e}"
puts e.backtrace.join("\n")
puts "Received data:"
p data
trap.source_ip = source_ip
select_handler(trap).call(trap)
end
rescue => e
puts "Error handling trap: #{e}"
puts e.backtrace.join("\n")
puts "Received data:"
p data
end
end
end

def select_handler(trap)
@lock.synchronize do
if trap.kind_of?(SNMPv2_Trap)
oid = trap.trap_oid
if @oid_handler[oid]
return @oid_handler[oid]
elsif @v2c_handler
return @v2c_handler
elsif @default_handler
return @default_handler
else
return NULL_HANDLER
end
elsif trap.kind_of?(SNMPv1_Trap)
if @v1_handler
return @v1_handler
elsif @default_handler
return @default_handler
else
return NULL_HANDLER
end
def select_handler(trap)
@lock.synchronize do
if trap.kind_of?(SNMPv2_Trap)
oid = trap.trap_oid
if @oid_handler[oid]
return @oid_handler[oid]
elsif @v2c_handler
return @v2c_handler
elsif @default_handler
return @default_handler
else
return NULL_HANDLER
end
elsif trap.kind_of?(SNMPv1_Trap)
if @v1_handler
return @v1_handler
elsif @default_handler
return @default_handler
else
return NULL_HANDLER
end
else
return NULL_HANDLER
end
end
end
end

end
2 changes: 1 addition & 1 deletion lib/snmp/mib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def name(oid)
end
index.unshift current_oid.slice!(-1)
end
oid.to_s
ObjectId.new(oid).to_s
end

def parse_oid(node_hash, name)
Expand Down
30 changes: 15 additions & 15 deletions lib/snmp/pdu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ class Message
attr_reader :pdu

class << self
def decode(data)
def decode(data, mib=nil)
message_data, remainder = decode_sequence(data)
assert_no_remainder(remainder)
version, remainder = decode_version(message_data)
community, remainder = decode_octet_string(remainder)
pdu, remainder = decode_pdu(version, remainder)
pdu, remainder = decode_pdu(version, remainder, mib)
assert_no_remainder(remainder)
Message.new(version, community, pdu)
end
Expand All @@ -54,29 +54,29 @@ def decode_version(data)
return version, remainder
end

def decode_pdu(version, data)
def decode_pdu(version, data, mib=nil)
pdu_tag, pdu_data, remainder = decode_tlv(data)
case pdu_tag
when GetRequest_PDU_TAG
pdu = PDU.decode(GetRequest, pdu_data)
pdu = PDU.decode(GetRequest, pdu_data, mib)
when GetNextRequest_PDU_TAG
pdu = PDU.decode(GetNextRequest, pdu_data)
pdu = PDU.decode(GetNextRequest, pdu_data, mib)
when Response_PDU_TAG
pdu = PDU.decode(Response, pdu_data)
pdu = PDU.decode(Response, pdu_data, mib)
when SetRequest_PDU_TAG
pdu = PDU.decode(SetRequest, pdu_data)
pdu = PDU.decode(SetRequest, pdu_data, mib)
when SNMPv1_Trap_PDU_TAG
raise InvalidPduTag, "SNMPv1-trap not valid for #{version.to_s}" if version != :SNMPv1
pdu = SNMPv1_Trap.decode(pdu_data)
pdu = SNMPv1_Trap.decode(pdu_data, mib)
when GetBulkRequest_PDU_TAG
raise InvalidPduTag, "get-bulk not valid for #{version.to_s}" if version != :SNMPv2c
pdu = PDU.decode(GetBulkRequest, pdu_data)
pdu = PDU.decode(GetBulkRequest, pdu_data, mib)
when InformRequest_PDU_TAG
raise InvalidPduTag, "inform not valid for #{version.to_s}" if version != :SNMPv2c
pdu = PDU.decode(InformRequest, pdu_data)
pdu = PDU.decode(InformRequest, pdu_data, mib)
when SNMPv2_Trap_PDU_TAG
raise InvalidPduTag, "SNMPv2c-trap not valid for #{version.to_s}" if version != :SNMPv2c
pdu = PDU.decode(SNMPv2_Trap, pdu_data)
pdu = PDU.decode(SNMPv2_Trap, pdu_data, mib)
else
raise UnsupportedPduTag, pdu_tag.to_s
end
Expand Down Expand Up @@ -119,11 +119,11 @@ class PDU

alias vb_list varbind_list

def self.decode(pdu_class, pdu_data)
def self.decode(pdu_class, pdu_data, mib=nil)
request_id, remainder = decode_integer(pdu_data)
error_status, remainder = decode_integer(remainder)
error_index, remainder = decode_integer(remainder)
varbind_list, remainder = VarBindList.decode(remainder)
varbind_list, remainder = VarBindList.decode(remainder, mib)
assert_no_remainder(remainder)
pdu_class.new(request_id, varbind_list, error_status, error_index)
end
Expand Down Expand Up @@ -310,7 +310,7 @@ class SNMPv1_Trap

alias :vb_list :varbind_list

def self.decode(pdu_data)
def self.decode(pdu_data, mib=nil)
oid_data, remainder = decode_object_id(pdu_data)
enterprise = ObjectId.new(oid_data)
ip_data, remainder = decode_ip_address(remainder)
Expand All @@ -319,7 +319,7 @@ def self.decode(pdu_data)
specific_trap, remainder = decode_integer(remainder)
time_data, remainder = decode_timeticks(remainder)
timestamp = TimeTicks.new(time_data)
varbind_list, remainder = VarBindList.decode(remainder)
varbind_list, remainder = VarBindList.decode(remainder, mib)
assert_no_remainder(remainder)
SNMPv1_Trap.new(enterprise, agent_addr, generic_trap, specific_trap,
timestamp, varbind_list)
Expand Down
Loading

0 comments on commit c367344

Please sign in to comment.