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