package Net::SNMPTrapd;
########################################################
#
# AUTHOR = Michael Vincent
# www.VinsWorld.com
#
########################################################
require 5.005;
use strict;
use Exporter;
use Convert::ASN1;
use Socket qw(inet_ntoa AF_INET IPPROTO_TCP);
my $AF_INET6 = eval { Socket::AF_INET6() };
my $NI_NUMERICHOST = eval { Socket::NI_NUMERICHOST() };
our $VERSION = '0.12';
our @ISA = qw(Exporter);
our @EXPORT = qw();
our %EXPORT_TAGS = (
'all' => [qw()]
);
our @EXPORT_OK = (@{$EXPORT_TAGS{'all'}});
my $HAVE_IO_Socket_IP = 0;
eval "use IO::Socket::IP -register";
if(!$@) {
$HAVE_IO_Socket_IP = 1;
push @ISA, "IO::Socket::IP"
} else {
require IO::Socket::INET;
push @ISA, "IO::Socket::INET";
}
########################################################
# Start Variables
########################################################
use constant SNMPTRAPD_DEFAULT_PORT => 162;
use constant SNMPTRAPD_RFC_SIZE => 484; # RFC limit
use constant SNMPTRAPD_REC_SIZE => 1472; # Recommended size
use constant SNMPTRAPD_MAX_SIZE => 65467; # Actual limit (65535 - IP/UDP)
our @TRAPTYPES = qw(COLDSTART WARMSTART LINKDOWN LINKUP AUTHFAIL EGPNEIGHBORLOSS ENTERPRISESPECIFIC);
our @PDUTYPES = qw(GetRequest GetNextRequest Response SetRequest Trap GetBulkRequest InformRequest SNMPv2-Trap Report);
our $LASTERROR;
my $asn = Convert::ASN1->new;
$asn->prepare("
PDU ::= SEQUENCE {
version INTEGER,
community STRING,
pdu_type PDUs
}
PDUs ::= CHOICE {
response Response_PDU,
trap Trap_PDU,
inform_request InformRequest_PDU,
snmpv2_trap SNMPv2_Trap_PDU
}
Response_PDU ::= [2] IMPLICIT PDUv2
Trap_PDU ::= [4] IMPLICIT PDUv1
InformRequest_PDU ::= [6] IMPLICIT PDUv2
SNMPv2_Trap_PDU ::= [7] IMPLICIT PDUv2
IPAddress ::= [APPLICATION 0] STRING
Counter32 ::= [APPLICATION 1] INTEGER
Guage32 ::= [APPLICATION 2] INTEGER
TimeTicks ::= [APPLICATION 3] INTEGER
Opaque ::= [APPLICATION 4] STRING
Counter64 ::= [APPLICATION 6] INTEGER
PDUv1 ::= SEQUENCE {
ent_oid OBJECT IDENTIFIER,
agent_addr IPAddress,
generic_trap INTEGER,
specific_trap INTEGER,
timeticks TimeTicks,
varbindlist VARBINDS
}
PDUv2 ::= SEQUENCE {
request_id INTEGER,
error_status INTEGER,
error_index INTEGER,
varbindlist VARBINDS
}
VARBINDS ::= SEQUENCE OF SEQUENCE {
oid OBJECT IDENTIFIER,
value CHOICE {
integer INTEGER,
string STRING,
oid OBJECT IDENTIFIER,
ipaddr IPAddress,
counter32 Counter32,
guage32 Guage32,
timeticks TimeTicks,
opaque Opaque,
counter64 Counter64,
null NULL
}
}
");
our $snmpasn = $asn->find('PDU');
########################################################
# End Variables
########################################################
########################################################
# Start Public Module
########################################################
sub new {
my $self = shift;
my $class = ref($self) || $self;
# Default parameters
my %params = (
'Proto' => 'udp',
'LocalPort' => SNMPTRAPD_DEFAULT_PORT,
'Timeout' => 10,
'Family' => AF_INET
);
if (@_ == 1) {
$LASTERROR = "Insufficient number of args - @_";
return(undef)
} else {
my %cfg = @_;
for (keys(%cfg)) {
if (/^-?localport$/i) {
$params{'LocalPort'} = $cfg{$_}
} elsif (/^-?localaddr$/i) {
$params{'LocalAddr'} = $cfg{$_}
} elsif (/^-?family$/i) {
if ($cfg{$_} =~ /^(?:(?:(:?ip)?v?(?:4|6))|${\AF_INET}|$AF_INET6)$/) {
if ($cfg{$_} =~ /^(?:(?:(:?ip)?v?4)|${\AF_INET})$/) {
$params{'Family'} = AF_INET
} else {
if (!$HAVE_IO_Socket_IP) {
$LASTERROR = "IO::Socket::IP required for IPv6";
return(undef)
}
$params{'Family'} = $AF_INET6
}
} else {
$LASTERROR = "Invalid family - $cfg{$_}";
return(undef)
}
} elsif (/^-?timeout$/i) {
if ($cfg{$_} =~ /^\d+$/) {
$params{'Timeout'} = $cfg{$_}
} else {
$LASTERROR = "Invalid timeout - $cfg{$_}";
return(undef)
}
}
}
}
if (my $udpserver = $class->SUPER::new(%params)) {
return bless {
%params, # merge user parameters
'_UDPSERVER_' => $udpserver
}, $class
} else {
$LASTERROR = "Error opening socket for listener: $@";
return(undef)
}
}
sub get_trap {
my $self = shift;
my $class = ref($self) || $self;
my $trap;
foreach my $key (keys(%{$self})) {
# everything but '_xxx_'
$key =~ /^\_.+\_$/ and next;
$trap->{$key} = $self->{$key}
}
my $datagramsize = SNMPTRAPD_MAX_SIZE;
if (@_ == 1) {
$LASTERROR = "Insufficient number of args: @_";
return(undef)
} else {
my %args = @_;
for (keys(%args)) {
# -maxsize
if (/^-?(?:max)?size$/i) {
if ($args{$_} =~ /^\d+$/) {
if (($args{$_} >= 1) && ($args{$_} <= SNMPTRAPD_MAX_SIZE)) {
$datagramsize = $args{$_}
}
} elsif ($args{$_} =~ /^rfc$/i) {
$datagramsize = SNMPTRAPD_RFC_SIZE
} elsif ($args{$_} =~ /^rec(?:ommend)?(?:ed)?$/i) {
$datagramsize = SNMPTRAPD_REC_SIZE
} else {
$LASTERROR = "Not a valid size: $args{$_}";
return(undef)
}
# -timeout
} elsif (/^-?timeout$/i) {
if ($args{$_} =~ /^\d+$/) {
$trap->{'Timeout'} = $args{$_}
} else {
$LASTERROR = "Invalid timeout - $args{$_}";
return(undef)
}
}
}
}
my $Timeout = $trap->{'Timeout'};
my $udpserver = $self->{'_UDPSERVER_'};
my $datagram;
if ($Timeout != 0) {
# vars for IO select
my ($rin, $rout, $ein, $eout) = ('', '', '', '');
vec($rin, fileno($udpserver), 1) = 1;
# check if a message is waiting
if (! select($rout=$rin, undef, $eout=$ein, $Timeout)) {
$LASTERROR = "Timed out waiting for datagram";
return(0)
}
}
# read the message
if ($udpserver->recv($datagram, $datagramsize)) {
$trap->{'_TRAP_'}{'PeerPort'} = $udpserver->SUPER::peerport;
$trap->{'_TRAP_'}{'PeerAddr'} = $udpserver->SUPER::peerhost;
$trap->{'_TRAP_'}{'datagram'} = $datagram;
return bless $trap, $class
}
$LASTERROR = sprintf "Socket RECV error: $!";
return(undef)
}
sub process_trap {
my $self = shift;
my $class = ref($self) || $self;
### Allow to be called as subroutine
# Net::SNMPTrapd->process_trap($data)
if (($self eq $class) && ($class eq __PACKAGE__)) {
my %th;
$self = \%th;
($self->{'_TRAP_'}{'datagram'}) = @_
}
# Net::SNMPTrapd::process_trap($data)
if ($class ne __PACKAGE__) {
my %th;
$self = \%th;
($self->{'_TRAP_'}{'datagram'}) = $class;
$class = __PACKAGE__
}
my $RESPONSE = 1; # Default is to send Response PDU for InformRequest
# If more than 1 argument, parse the options
if (@_ != 1) {
my %args = @_;
for (keys(%args)) {
# -datagram
if ((/^-?data(?:gram)?$/i) || (/^-?pdu$/i)) {
$self->{'_TRAP_'}{'datagram'} = $args{$_}
# -noresponse
} elsif (/^-?noresponse$/i) {
if (($args{$_} =~ /^\d+$/) && ($args{$_} > 0)) {
$RESPONSE = 0
}
}
}
}
my $trap;
if (!defined($trap = $snmpasn->decode($self->{'_TRAP_'}{'datagram'}))) {
$LASTERROR = sprintf "Error decoding PDU - %s", (defined($snmpasn->error) ? $snmpasn->error : "Unknown Convert::ASN1->decode() error. Consider $class dump()");
return(undef)
}
#DEBUG: use Data::Dumper; print Dumper \$trap;
# Only understand SNMPv1 (0) and v2c (1)
if ($trap->{'version'} > 1) {
$LASTERROR = sprintf "Unrecognized SNMP version - %i", $trap->{'version'};
return(undef)
}
# set PDU Type for later use
my $pdutype = sprintf "%s", keys(%{$trap->{'pdu_type'}});
### Assemble decoded trap object
# Common
$self->{'_TRAP_'}{'version'} = $trap->{'version'};
$self->{'_TRAP_'}{'community'} = $trap->{'community'};
if ($pdutype eq 'trap') {
$self->{'_TRAP_'}{'pdu_type'} = 4
} elsif ($pdutype eq 'inform_request') {
$self->{'_TRAP_'}{'pdu_type'} = 6;
# send response for InformRequest
if ($RESPONSE) {
if ((my $r = &_InformRequest_Response(\$self, $trap, $pdutype)) ne 'OK') {
$LASTERROR = sprintf "Error sending InformRequest Response - %s", $r;
return(undef)
}
}
} elsif ($pdutype eq 'snmpv2_trap') {
$self->{'_TRAP_'}{'pdu_type'} = 7
}
# v1
if ($trap->{'version'} == 0) {
$self->{'_TRAP_'}{'ent_oid'} = $trap->{'pdu_type'}->{$pdutype}->{'ent_oid'};
$self->{'_TRAP_'}{'agent_addr'} = inetNtoa($trap->{'pdu_type'}->{$pdutype}->{'agent_addr'});
$self->{'_TRAP_'}{'generic_trap'} = $trap->{'pdu_type'}->{$pdutype}->{'generic_trap'};
$self->{'_TRAP_'}{'specific_trap'} = $trap->{'pdu_type'}->{$pdutype}->{'specific_trap'};
$self->{'_TRAP_'}{'timeticks'} = $trap->{'pdu_type'}->{$pdutype}->{'timeticks'};
# v2c
} elsif ($trap->{'version'} == 1) {
$self->{'_TRAP_'}{'request_id'} = $trap->{'pdu_type'}->{$pdutype}->{'request_id'};
$self->{'_TRAP_'}{'error_status'} = $trap->{'pdu_type'}->{$pdutype}->{'error_status'};
$self->{'_TRAP_'}{'error_index'} = $trap->{'pdu_type'}->{$pdutype}->{'error_index'};
}
# varbinds
my @varbinds;
for my $i (0..$#{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}}) {
my %oidval;
for (keys(%{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'value'}})) {
# defined
if (defined($trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'value'}{$_})) {
# special cases: IP address, null
if ($_ eq 'ipaddr') {
$oidval{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'oid'}} = inetNtoa($trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'value'}{$_})
} elsif ($_ eq 'null') {
$oidval{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'oid'}} = '(NULL)'
# no special case: just assign it
} else {
$oidval{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'oid'}} = $trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'value'}{$_}
}
# not defined - ""
} else {
$oidval{$trap->{'pdu_type'}->{$pdutype}->{'varbindlist'}[$i]->{'oid'}} = ""
}
}
push @varbinds, \%oidval
}
$self->{'_TRAP_'}{'varbinds'} = \@varbinds;
return bless $self, $class
}
sub server {
my $self = shift;
return $self->{'_UDPSERVER_'}
}
sub datagram {
my ($self, $arg) = @_;
if (defined($arg) && ($arg >= 1)) {
return unpack ('H*', $self->{'_TRAP_'}{'datagram'})
} else {
return $self->{'_TRAP_'}{'datagram'}
}
}
sub remoteaddr {
my $self = shift;
return $self->{'_TRAP_'}{'PeerAddr'}
}
sub remoteport {
my $self = shift;
return $self->{'_TRAP_'}{'PeerPort'}
}
sub version {
my $self = shift;
return $self->{'_TRAP_'}{'version'} + 1
}
sub community {
my $self = shift;
return $self->{'_TRAP_'}{'community'}
}
sub pdu_type {
my ($self, $arg) = @_;
if (defined($arg) && ($arg >= 1)) {
return $self->{'_TRAP_'}{'pdu_type'}
} else {
return $PDUTYPES[$self->{'_TRAP_'}{'pdu_type'}]
}
}
sub ent_OID {
my $self = shift;
return $self->{'_TRAP_'}{'ent_oid'}
}
sub agentaddr {
my $self = shift;
return $self->{'_TRAP_'}{'agent_addr'}
}
sub generic_trap {
my ($self, $arg) = @_;
if (defined($arg) && ($arg >= 1)) {
return $self->{'_TRAP_'}{'generic_trap'}
} else {
return $TRAPTYPES[$self->{'_TRAP_'}{'generic_trap'}]
}
}
sub specific_trap {
my $self = shift;
return $self->{'_TRAP_'}{'specific_trap'}
}
sub timeticks {
my $self = shift;
return $self->{'_TRAP_'}{'timeticks'}
}
sub request_ID {
my $self = shift;
return $self->{'_TRAP_'}{'request_id'}
}
sub error_status {
my $self = shift;
return $self->{'_TRAP_'}{'error_status'}
}
sub error_index {
my $self = shift;
return $self->{'_TRAP_'}{'error_index'}
}
sub varbinds {
my $self = shift;
return $self->{'_TRAP_'}{'varbinds'}
}
sub error {
return($LASTERROR)
}
sub dump {
my $self = shift;
my $class = ref($self) || $self;
### Allow to be called as subroutine
# Net::SNMPTrapd->dump($datagram)
if (($self eq $class) && ($class eq __PACKAGE__)) {
my %th;
$self = \%th;
($self->{'_TRAP_'}{'datagram'}) = @_
}
# Net::SNMPTrapd::dump($datagram)
if ($class ne __PACKAGE__) {
my %th;
$self = \%th;
($self->{'_TRAP_'}{'datagram'}) = $class;
$class = __PACKAGE__
}
if (defined($self->{'_TRAP_'}{'datagram'})) {
Convert::ASN1::asn_dump($self->{'_TRAP_'}{'datagram'});
Convert::ASN1::asn_hexdump($self->{'_TRAP_'}{'datagram'});
} else {
$LASTERROR = "Missing datagram to dump";
return(undef)
}
return 1
}
########################################################
# End Public Module
########################################################
########################################################
# Start Private subs
########################################################
sub _InformRequest_Response {
my ($self, $trap, $pdutype) = @_;
my $class = ref($$self) || $$self;
# Change from request to response
$trap->{'pdu_type'}{'response'} = delete $trap->{'pdu_type'}{'inform_request'};
my $buffer = $snmpasn->encode($trap);
if (!defined($buffer)) {
return $snmpasn->error
}
#DEBUG print "BUFFER = $buffer\n";
if ($$self->{'_TRAP_'}->{'PeerAddr'} eq "") {
return "Peer Addr undefined"
}
if ($$self->{'_TRAP_'}->{'PeerPort'} == 0) {
return "Peer Port undefined"
}
my $socket = $class->SUPER::new(
Proto => "udp",
PeerAddr => $$self->{'_TRAP_'}->{'PeerAddr'},
PeerPort => $$self->{'_TRAP_'}->{'PeerPort'},
# LocalPort should be set, but creates error.
# Tried setting ReusePort on initial server,
# but not implemented on Windows. What to do?
#LocalPort => SNMPTRAPD_DEFAULT_PORT,
Family => $$self->{'Family'}
) || return "Can't create Response socket";
$socket->send($buffer);
close $socket;
# Change back to request from response
$trap->{'pdu_type'}{'inform_request'} = delete $trap->{'pdu_type'}{'response'};
return ("OK")
}
sub inetNtoa {
my ($addr) = @_;
if ($Socket::VERSION >= 1.94) {
my $name;
if (length($addr) == 4) {
$name = Socket::pack_sockaddr_in(0, $addr)
} else {
$name = Socket::pack_sockaddr_in6(0, $addr)
}
my ($err, $address) = Socket::getnameinfo($name, $NI_NUMERICHOST);
if (defined($address)) {
return $address
} else {
$LASTERROR = "getnameinfo($addr) failed - $err";
return undef
}
} else {
if (length($addr) == 4) {
return inet_ntoa($addr)
} else {
# Poor man's IPv6
return join ':', (unpack '(a4)*', unpack ('H*', $addr))
}
}
}
########################################################
# End Private subs
########################################################
1;
__END__
########################################################
# Start POD
########################################################
=head1 NAME
Net::SNMPTrapd - Perl implementation of SNMP Trap Listener
=head1 SYNOPSIS
use Net::SNMPTrapd;
my $snmptrapd = Net::SNMPTrapd->new()
or die "Error creating SNMPTrapd listener: ", Net::SNMPTrapd->error;
while (1) {
my $trap = $snmptrapd->get_trap();
if (!defined($trap)) {
printf "$0: %s\n", Net::SNMPTrapd->error;
exit 1
} elsif ($trap == 0) {
next
}
if (!defined($trap->process_trap())) {
printf "$0: %s\n", Net::SNMPTrapd->error
} else {
printf "%s\t%i\t%i\t%s\n",
$trap->remoteaddr,
$trap->remoteport,
$trap->version,
$trap->community
}
}
=head1 DESCRIPTION
Net::SNMPTrapd is a class implementing a simple SNMP Trap listener in
Perl. Net::SNMPTrapd will accept traps on the default SNMP Trap port
(UDP 162) and attempt to decode them. Net::SNMPTrapd supports SNMP v1
and v2c traps and SNMPv2 InformRequest and implements the Reponse.
Net::SNMPTrapd uses Convert::ASN1 by Graham Barr to do the decoding.
=head1 METHODS
=head2 new() - create a new Net::SNMPTrapd object
my $snmptrapd = Net::SNMPTrapd->new([OPTIONS]);
Create a new Net::SNMPTrapd object with OPTIONS as optional parameters.
Valid options are:
Option Description Default
------ ----------- -------
-Family Address family IPv4/IPv6 IPv4
Valid values for IPv4:
4, v4, ip4, ipv4, AF_INET (constant)
Valid values for IPv6:
6, v6, ip6, ipv6, AF_INET6 (constant)
-LocalAddr Interface to bind to any
-LocalPort Port to bind server to 162
-timeout Timeout in seconds for socket 10
operations and to wait for request
B<NOTE>: IPv6 requires B<IO::Socket::IP>. Failback is B<IO::Socket::INET>
and only IPv4 support.
Allows the following accessors to be called.
=head3 server() - return IO::Socket::IP object for server
$snmptrapd->server();
Return B<IO::Socket::IP> object for the created server.
All B<IO::Socket::IP> accessors can then be called.
=head2 get_trap() - listen for SNMP traps
my $trap = $snmptrapd->get_trap([OPTIONS]);
Listen for SNMP traps. Timeout after default or user specified
timeout set in C<new> method and return '0'. If trap is received
before timeout, return is defined. Return is not defined if error
encountered.
Valid options are:
Option Description Default
------ ----------- -------
-maxsize Max size in bytes of acceptable PDU. 65467
Value can be integer 1 <= # <= 65467.
Keywords: 'RFC' = 484
'recommended' = 1472
-timeout Timeout in seconds to wait for 10
request. Overrides value set with
new().
Allows the following accessors to be called.
=head3 remoteaddr() - return remote address from SNMP trap
$trap->remoteaddr();
Return remote address value from a received (C<get_trap()>)
SNMP trap. This is the address from the IP header on the UDP
datagram.
=head3 remoteport() - return remote port from SNMP trap
$trap->remoteport();
Return remote port value from a received (C<get_trap()>)
SNMP trap. This is the port from the IP header on the UDP
datagram.
=head3 datagram() - return datagram from SNMP trap
$trap->datagram([1]);
Return the raw datagram from a received (C<get_trap()>)
SNMP trap. This is ASN.1 encoded datagram. For a hex
dump, use the optional boolean argument.
=head2 process_trap() - process received SNMP trap
$trap->process_trap([OPTIONS]);
Process a received SNMP trap. Decodes the received (C<get_trap()>)
PDU. Varbinds are extracted and decoded. If PDU is SNMPv2
InformRequest, the Response PDU is generated and sent to IP
address and UDP port found in the original datagram header
(C<get_trap()> methods C<remoteaddr()> and C<remoteport()>).
Called with one argument, interpreted as the datagram to process.
Valid options are:
Option Description Default
------ ----------- -------
-datagram Datagram to process -Provided by
get_trap()-
-noresponse Binary switch (0|1) meaning 'Do not 0
send Response-PDU for InformRequest' -Send Response-
This can also be called as a procedure if one is inclined to write
their own UDP listener instead of using C<get_trap()>. For example:
$sock = IO::Socket::IP->new( blah blah blah );
$sock->recv($datagram, 1500);
# process the ASN.1 encoded datagram in $datagram variable
$trap = Net::SNMPTrapd->process_trap($datagram);
or
# process the ASN.1 encoded datagram in $datagram variable
# Do *NOT* send Response PDU if trap comes as InformRequest PDU
$trap = Net::SNMPTrapd->process_trap(
-datagram => $datagram,
-noresponse => 1
);
In any instantiation, allows the following accessors to be called.
=head3 version() - return version from SNMP trap
$trap->version();
Return SNMP Trap version from a received and processed
(C<process_trap()>) SNMP trap.
B<NOTE:> This module only supports SNMP v1 and v2c.
=head3 community() - return community from SNMP trap
$trap->community();
Return community string from a received and processed
(C<process_trap()>) SNMP trap.
=head3 pdu_type() - return PDU type from SNMP trap
$trap->pdu_type([1]);
Return PDU type from a received and processed (C<process_trap()>)
SNMP trap. This is the text representation of the PDU type.
For the raw number, use the optional boolean argument.
=head3 varbinds() - return varbinds from SNMP trap
$trap->varbinds();
Return varbinds from a received and processed
(C<process_trap()>) SNMP trap. This returns a pointer to an array
containing a hash as each array element. The key/value pairs of
each hash are the OID/value pairs for each varbind in the received
trap.
[{OID => value}]
[{OID => value}]
...
[{OID => value}]
An example extraction of the varbind data is provided:
for my $vals (@{$trap->varbinds}) {
for (keys(%{$vals})) {
$p .= sprintf "%s: %s; ", $_, $vals->{$_}
}
}
print "$p\n";
The above code will print the varbinds as:
OID: value; OID: value; OID: value; [...]
=head3 SNMP v1 SPECIFIC
The following methods are SNMP v1 trap specific.
=head3 ent_OID() - return enterprise OID from SNMP v1 trap
$trap->ent_OID();
Return enterprise OID from a received and processed
(C<process_trap()>) SNMP v1 trap.
=head3 agentaddr() - return agent address from SNMP v1 trap
$trap->agentaddr();
Return agent address from a received and processed
(C<process_trap()>) SNMP v1 trap.
=head3 generic_trap() - return generic trap from SNMP v1 trap
$trap->generic_trap([1]);
Return generic trap type from a received and processed
(C<process_trap()>) SNMP v1 trap. This is the text representation
of the generic trap type. For the raw number, use the optional
boolean argument.
=head3 specific_trap() - return specific trap from SNMP v1 trap
$trap->specific_trap();
Return specific trap type from a received and processed
(C<process_trap()>) SNMP v1 trap.
=head3 timeticks() - return timeticks from SNMP v1 trap
$trap->timeticks();
Return timeticks from a received and processed
(C<process_trap()>) SNMP v1 trap.
=head3 SNMP v2c SPECIFIC
The following methods are SNMP v2c trap specific.
=head3 request_ID() - return request ID from SNMP v2c trap
$trap->request_ID();
Return request ID from a received and processed
(C<process_trap()>) SNMP v2c trap.
=head3 error_status() - return error status from SNMP v2c trap
$trap->error_status();
Return error_status from a received and processed
(C<process_trap()>) SNMP v2c trap.
=head3 error_index() - return error index from SNMP v2c trap
$trap->error_index();
Return error index from a received and processed
(C<process_trap()>) SNMP v2c trap.
=head2 error() - return last error
printf "Error: %s\n", Net::SNMPTrapd->error;
Return last error.
=head2 dump() - Convert::ASN1 direct decode and hex dump
$trap->dump();
or
Net::SNMPTrapd->dump($datagram);
This does B<not> use any of the Net::SNMPTrapd ASN.1 structures;
rather, it uses the Convert::ASN1 module debug routines (C<asn_dump>
and C<asn_hexdump>) to attempt a decode and hex dump of the supplied
datagram. This is helpful to eliminate the entire Net::SNMPTrapd
module code when troubleshooting issues with decoding and focus solely
on the ASN.1 decode of the given datagram.
Called as a method, operates on the value returned from the
C<datagram()> method. Called as a subroutine, operates on the
value passed.
Output is printed directly to STDERR. Return is defined unless there
is an error encountered in getting the datagram to operate on.
=head1 EXPORT
None by default.
=head1 EXAMPLES
This distribution comes with several scripts (installed to the default
"bin" install directory) that not only demonstrate example uses but also
provide functional execution.
=head1 SEE ALSO
Convert::ASN1
=head1 LICENSE
This software is released under the same terms as Perl itself.
If you don't know what that means visit L<http://perl.com/>.
=head1 AUTHOR
Copyright (C) Michael Vincent 2010
L<http://www.VinsWorld.com>
All rights reserved
=cut