File Coverage

blib/lib/AnyEvent/SNMP/TrapReceiver.pm
Criterion Covered Total %
statement 68 69 98.5
branch 9 10 90.0
condition 1 3 33.3
subroutine 13 13 100.0
pod 0 2 0.0
total 91 97 93.8


line stmt bran cond sub pod time code
1             package AnyEvent::SNMP::TrapReceiver;
2 2     2   95391 use 5.010;
  2         15  
3 2     2   10 use strict;
  2         3  
  2         33  
4 2     2   6 use warnings;
  2         4  
  2         35  
5 2     2   849 use AnyEvent;
  2         4359  
  2         52  
6 2     2   485 use AnyEvent::Socket;
  2         20607  
  2         179  
7 2     2   841 use AnyEvent::Handle::UDP;
  2         7828  
  2         56  
8 2     2   756 use Convert::ASN1;
  2         53519  
  2         84  
9 2     2   15 use Carp;
  2         5  
  2         116  
10              
11             our $VERSION = "0.15";
12              
13             ########################################################
14             # Start Variables
15             ########################################################
16 2     2   12 use constant SNMPTRAPD_DEFAULT_PORT => 162;
  2         4  
  2         1006  
17              
18             my @TRAPTYPES = qw(COLDSTART WARMSTART LINKDOWN LINKUP AUTHFAIL EGPNEIGHBORLOSS ENTERPRISESPECIFIC);
19             our $LASTERROR;
20              
21             my $asn = Convert::ASN1->new;
22             $asn->prepare( "
23             PDU ::= SEQUENCE {
24             version INTEGER,
25             community STRING,
26             pdu_type PDUs
27             }
28             PDUs ::= CHOICE {
29             response Response_PDU,
30             trap Trap_PDU,
31             inform_request InformRequest_PDU,
32             snmpv2_trap SNMPv2_Trap_PDU
33             }
34             Response_PDU ::= [2] IMPLICIT PDUv2
35             Trap_PDU ::= [4] IMPLICIT PDUv1
36             InformRequest_PDU ::= [6] IMPLICIT PDUv2
37             SNMPv2_Trap_PDU ::= [7] IMPLICIT PDUv2
38              
39             IPAddress ::= [APPLICATION 0] STRING
40             Counter32 ::= [APPLICATION 1] INTEGER
41             Guage32 ::= [APPLICATION 2] INTEGER
42             TimeTicks ::= [APPLICATION 3] INTEGER
43             Opaque ::= [APPLICATION 4] STRING
44             Counter64 ::= [APPLICATION 6] INTEGER
45              
46             PDUv1 ::= SEQUENCE {
47             ent_oid OBJECT IDENTIFIER,
48             agent_addr IPAddress,
49             generic_trap INTEGER,
50             specific_trap INTEGER,
51             timeticks TimeTicks,
52             varbindlist VARBINDS
53             }
54             PDUv2 ::= SEQUENCE {
55             request_id INTEGER,
56             error_status INTEGER,
57             error_index INTEGER,
58             varbindlist VARBINDS
59             }
60             VARBINDS ::= SEQUENCE OF SEQUENCE {
61             oid OBJECT IDENTIFIER,
62             value CHOICE {
63             integer INTEGER,
64             string STRING,
65             oid OBJECT IDENTIFIER,
66             ipaddr IPAddress,
67             counter32 Counter32,
68             guage32 Guage32,
69             timeticks TimeTicks,
70             opaque Opaque,
71             counter64 Counter64,
72             null NULL
73             }
74             }
75             " );
76             my $snmpasn = $asn->find('PDU');
77             ########################################################
78             # End Variables
79             ########################################################
80              
81             sub new {
82 2     2 0 4798 my ( $class, %args ) = @_;
83 2   33     10 my $self = bless { cb => $args{cb} || croak('cb not given'), }, $class;
84              
85 2         11 my $bindTo;
86 2 50       10 if ( exists $args{bind} ) {
87 2         5 $bindTo = $args{bind};
88             } else {
89 0         0 $bindTo = [ '0.0.0.0', SNMPTRAPD_DEFAULT_PORT ],;
90             }
91              
92             $self->{server} = AnyEvent::Handle::UDP->new(
93             bind => $bindTo,
94             on_recv => sub {
95 2     2   1294 my $trap = _handle_trap(@_);
96              
97 2         7 $self->format($trap);
98              
99 2         3 &{ $self->{cb} }($trap);
  2         6  
100             }
101 2         16 );
102              
103 2         958 return $self;
104             } ## end sub new
105              
106             sub _handle_trap {
107 2     2   6 my ( $data, $ae_handle, $client_addr ) = @_;
108              
109 2         10 my $trap = $snmpasn->decode($data);
110              
111             # get the sender IP
112 2         1513 my ( $port, $addr ) = AnyEvent::Socket::unpack_sockaddr($client_addr);
113              
114             # humanize
115 2         25 $trap->{remoteaddr} = format_address($addr);
116 2         21 $trap->{remoteport} = $port;
117              
118 2         5 return $trap;
119             } ## end sub _handle_trap
120              
121             sub format {
122 2     2 0 5 my ( $self, $trap ) = @_;
123              
124             # version starts at '1'
125 2         4 $trap->{version} += 1;
126              
127             # unify v1 and v2c traps
128 2 100       6 if ( exists $trap->{pdu_type}{snmpv2_trap} ) {
129 1         1 %$trap = ( %{ delete $trap->{pdu_type}{snmpv2_trap} }, %$trap );
  1         9  
130             } else {
131 1         2 %$trap = ( %{ delete $trap->{pdu_type}{trap} }, %$trap );
  1         9  
132             }
133 2         8 delete $trap->{pdu_type};
134              
135 2 100       7 if ( exists $trap->{agent_addr} ) {
136 1         4 $trap->{agent_addr} = format_address( $trap->{agent_addr} );
137             }
138              
139 2 100       11 if ( exists $trap->{generic_trap} ) {
140 1         7 $trap->{generic_trap} = $TRAPTYPES[ $trap->{generic_trap} ];
141             }
142              
143             # uptime
144 2 100       4 if ( exists $trap->{timeticks} ) {
145 1         4 my $timeticks = 0xffffffff & $trap->{timeticks};
146 1         1 $trap->{timeticks} = $timeticks;
147 1         8 my @uptime = (
148             int( $timeticks / 8640000 ), # days
149             int( ( $timeticks % 8640000 ) / 360000 ), # hours
150             int( ( $timeticks % 360000 ) / 6000 ), # minutes
151             int( ( $timeticks % 6000 ) / 100 ), # seconds
152             );
153 1         2 $trap->{uptime} = \@uptime;
154             } ## end if ( exists $trap->{timeticks...})
155              
156             # convert varbindlist to key->value
157 2         3 foreach my $var ( @{ $trap->{varbindlist} } ) {
  2         5  
158 3         5 my $oid = $var->{oid};
159 3         5 my $value = $var->{value};
160 3         5 $trap->{oid}{$oid} = ( values( %{$value} ) )[0];
  3         9  
161             }
162              
163 2         7 delete $trap->{varbindlist};
164              
165 2         3 return $trap;
166             } ## end sub format
167              
168             1;
169             __END__