1#	$Id: snmpUtils.pm,v 1.4 2002/06/16 23:29:09 driehuis Exp $
2#	$Source: /cvsroot/cricket/cricket/lib/alternate/net-snmp/snmpUtils.pm,v $
3#
4# This is a simple wrapper for Net-SNMP. People who want to
5# use other SNMP libraries can hook the calls here by replacing this
6# copy of snmpUtils.pm with their own, which redirects the calls
7# to their own library.
8#
9# To use this one, mv the original snmpUtils.pm out of the way and
10# symlink this one in its place:
11#	cd ~cricket/lib
12#	mv snmpUtils.pm snmpUtils_SNMP_Session.pm
13#	ln -s alternate/net-snmp/snmpUtils.pm snmpUtils.pm
14#
15# TODO:
16#	Verify that traps are generated consistent with the main
17#	implementation.
18#	Provide an upper bound to the number of cached sessions,
19#	by implementing an LRU cache.
20
21package snmpUtils;
22
23use Common::Log;
24use SNMP;
25use Sys::Hostname;
26
27# this is the OID for enterprises.webtv.wtvOps.wtvOpsTraps
28my($trapoid) = ".1.3.6.1.4.1.2595.1.1";
29
30# Max number of times a device can fail to respond before we skip further
31# requests.  Adjust as needed. (This should probably be made a target
32# attribute in the config tree so it can be set on a per device basis.)
33my $MAXTRIES = 2;
34
35my %skipcnt;
36my %sessions;
37my @fifo;
38
39my $hostname = undef;
40
41sub init {
42    %skipcnt = ();
43    %sessions = ();
44}
45
46# Establish an SNMP session to the given host.
47
48sub opensnmp {
49    my($snmp) = @_;
50    if (defined $sessions{$snmp}) {      # If we already have a session...
51        if ($sessions{$snmp} != -1) {    # If it not blacklisted, return it.
52            return $sessions{$snmp};
53        } else {                         # Else blacklisted, return undef.
54            return undef;
55         }
56    }
57
58    my $snmp_url = $snmp;
59    $snmp =~ s#snmp://##;
60    my $istrap = 0;
61    $istrap = 1 if ($snmp =~ s#trap://##);
62    my ($comm, $rest) = split(/\@/, $snmp, 2);
63    if (!defined($rest)) {
64        $comm = undef;
65        $rest = $snmp;
66    }
67    my ($host, $port, $timeout, $retries, $backoff, $version) =
68                split(/\s*:\s*/, $rest);
69
70    $comm ||= 'public';
71    $port ||= 161 if !$istrap;
72    $port ||= 162 if $istrap;
73    $timeout ||= 2;
74    $retries ||= 5;
75    $backoff ||= 1;
76    $version ||= 1;
77
78    Info("Opening SNMP session to $host:$port/v$version");
79
80    my %session_opts = (Community    => $comm,
81                 DestHost     => $host,
82                 RemotePort   => $port,
83                 Timeout      => $timeout * 1000000,
84                 Retries      => $retries,
85                 Version      => $version,
86                 AuthProto    => 'MD5',
87                 PrivProto    => 'DES',
88                 AuthPass     => '',
89                 PrivPass     => '',
90                 Context      => 'default',
91                 SecName      => 'initial',
92                 SecLevel     => 'authNoPriv',
93                 UseNumeric   => 1,
94                 UseLongNames => 1);
95
96    my $session = new SNMP::Session(%session_opts) if !$istrap;
97    $session = new SNMP::TrapSession(%session_opts) if $istrap;
98
99    if (!defined($session)) {
100        Warn("Can't set up session to $snmp");
101        $sessions{$snmp} = -1;
102        return undef;
103    }
104
105    $sessions{$snmp_url} = $session;    # Save the session for future reference.
106    $skipcnt{$snmp_url} = $MAXTRIES;    # Init the blacklist counter.
107    push @fifo, $snmp_url;
108    if ($#fifo > 20) {
109        my $old_url = shift @fifo;
110        delete $sessions{$old_url};
111        # We keep the blacklist entry
112    }
113
114    return $session;
115}
116
117sub count_error {
118    my ($snmp, $session) = @_;
119    # Strip community name from error
120    my($ignore1, $ssnmp) = $snmp =~ /([^@]+@)?(.*)/;
121
122    my $errstr = $session->{"ErrorStr"};
123    Warn($errstr);
124
125    if ($errstr =~ /timeout/i) {
126        $skipcnt{$snmp}--;
127        Warn("Skip count now $skipcnt{$snmp} for $ssnmp");
128
129        if ($skipcnt{$snmp} <= 0) {
130            Warn("Blacklisting $ssnmp");
131            $sessions{$snmp} = -1;
132         }
133    }
134}
135
136sub get {
137    my ($snmp, @oids) = @_;
138    my $session = opensnmp($snmp);
139    return () unless defined($session);
140
141    my @vars;
142    foreach my $oid (@oids) {
143        my $var = new SNMP::Varbind([$oid]);
144        push @vars, $var;
145    }
146    my $varlist = new SNMP::VarList(@vars);
147    $session->get($varlist);
148    if ($session->{"ErrorNum"}) {
149        &count_error($snmp, $session);
150        return ();
151    }
152    my @return;
153    foreach my $var (@vars) {
154        push @return, $var->val;
155    }
156    return @return;
157}
158
159sub walk {
160    my ($snmp, $oid) = @_;
161    my $session = opensnmp($snmp);
162    return () unless defined($session);
163
164    my @return = ();
165    $oid = &SNMP::translateObj($oid) if $oid =~ /^[a-zA-Z]/;
166    $oid = ".$oid" unless substr($oid, 0, 1) eq ".";
167    my $var = new SNMP::Varbind([$oid]);
168    while (defined $session->getnext($var)) {
169        last if substr($var->tag, 0, length($oid)) ne $oid;
170        if (length($var->tag) > length($oid)) {
171            push @return, substr($var->tag, length($oid) + 1) . "." .
172                 $var->iid . ":" .  $var->val;
173        } else {
174            push @return, $var->iid . ":" .  $var->val;
175        }
176    }
177    return @return;
178}
179
180sub trap {
181    my($to, $spec, @data) = @_;
182
183    my @newdata = ();
184    my($ct) = 1;
185    foreach my $item (@data) {
186        push @newdata, ".$ct", "", $item;
187        $ct++;
188    }
189    &trap2($to, $spec, @newdata);
190}
191
192sub trap2 {
193    my($to, $spec, @data) = @_;
194
195    $to = "trap://$to" unless $to =~ /^trap/;
196    my $session = opensnmp($to);
197    return undef unless defined($session);
198
199    # this makes a oid->value map for the trap. Note that
200    # we just fake up simple one-level OID's... it suits our needs.
201    my($type, $item, @vars);
202    while (@data) {
203        $oid = shift @data;
204        shift @data;
205        $item = shift @data;
206        $type = OCTETSTR;
207        $type = UINTEGER if ($item =~ /^(\d+)$/);
208        $type = INTEGER if ($item =~ /^-(\d+)$/);
209
210        my $var = new SNMP::Varbind([$oid, undef, $item, $type]);
211        push @vars, $var;
212    }
213    my $varlist = new SNMP::VarList(@vars);
214    $hostname ||= hostname();
215    $session->trap(enterprise=>$trapoid, agent=>$hostname, specific=>$spec,
216                   $varlist);
217}
218
2191;
220
221# Local Variables:
222# mode: perl
223# indent-tabs-mode: nil
224# tab-width: 4
225# perl-indent-level: 4
226# End:
227