I'm modifying Cisco SNMP CDP Poll by fingers to grab the allowed VLANs on trunk ports using SNMP. The OID I'm using is 1.3.6.1.4.1.9.5.1.9.3.1.5. I can get the information, but I'm not entirely sure how to translate it.
The output for Get_SNMP_Info("1.3.6.1.4.1.9.5.1.9.3.1.5".$unique_oid) iswhich should translate into VLANs 0-1023 being allowed I believe, given the description of the OID below and a review of the switch's VLAN table from a term session.0xfffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffe
Cisco's description of the OID is as follows:
An indication of which Virtual LANs are allowed on this Inter-Switch Link. This is an octet string value with bits set to indicate allowed VLANs. It can be interpreted as a sum of f(x) as x goes from 0 to 1023, where f(x) = 0 for VLAN x not allowed and f(x) = exp(2, x) for VLAN x allowed.
Any thoughts how to write this formula in Perl?
Update: I did find this WSH script on The Moose and Squirrel Files
Fortunately large number arithmetic is not required to reveal the allowed vlans. Each pair of hexadecimal digits is converted to a binary string and concatenated to form the binary representation of the number. Each bit that is set corresponds to a vlan. The string is reversed using the strreverse() function and then searched for the character ‘1′ using the instr() function. The index value returned from instr is the vlan number + 1. The vlan is obtained by subtracting 1 from the returned index and the search repeated until no more matches are found in the string. More detail is available by referring to the enum_AllowedVLAN() and supporting functions in listing 1
Listing 1:const vlanPortIslVlansAllowed = " .1.3.6.1.4.1.9.5.1.9.3.1.5.1.1 " const SNMPGETCMD2 = "f:\usr\bin\snmpget.exe -Ov -v 2c -c " '********************************************************************* +*** 'FUNCTION: + * ' enum_AllowedVLAN(strAgent) + * 'Purpose: + * ' enumerate the vlans configured on the switch. + * ' + * 'Inputs: + * ' strAgent: management IP address of the switch + * ' + * 'Returns: + * ' Array with each element containing a vlan number + * ' + * 'Calls: + * ' SNMPGETCMD2 - constant defining the path to an external + * ' program and options used to perform an snmp get operation. + * ' fmtBinary - function to left pad a binary number with zeros + * ' ToBinary - function to convert an integer to a binary string + * ' + * 'Comments: + * ' CISCO-STACK-MIB is cisco specific. + * ' Reference Cisco SNMP Object Navigator viewed at + * ' http://tools.cisco.com/Support/SNMP/do/BrowseOID.do? + * ' objectInput=vlanPortIslVlansAllowed&translate=Translate& + * ' submitValue=SUBMIT&submitClicked=true + * ' on 16/11/2006 + * '********************************************************************* +*** function enum_AllowedVLAN(strAgent) dim WshShell, oExec dim re 'as regexp dim matches dim match, submatch dim tempstr, stroutput, index, vlans Set WshShell = CreateObject("WScript.Shell") Set oExec = WshShell.Exec(SNMPGETCMD2 & SNMPREAD & " " & _ strAgent & " " & vlanPortIslVlansAllowed) Do while Not oExec.StdOut.AtEndOfStream stroutput = oExec.StdOut.readall Loop Do While oExec.Status <> 1 WScript.Sleep 100 Loop tempstr = "" set re = new regexp re.global = True re.multiline = True 'Pattern to capture the hex digits representing the allowed vlans. re.pattern = "[0-9a-fA-F]{2}" vlans = "" if instr(1,stroutput, "Hex-STRING:") > 0 then set matches = re.execute(stroutput) for each match in matches tempstr = tempstr & fmtBinary(ToBinary(cint("&H" & match)), 8) next tempstr = strreverse(tempstr) index = 1 do index = instr(index, tempstr, "1") if index <> 0 then vlans = vlans & " " & index - 1 index = index + 1 end if loop until index = 0 end if enum_AllowedVLAN = split(trim(vlans)) end function '********************************************************************* +*** 'FUNCTION: + * ' fmtBinary(strNumber, intLength) + * 'PURPOSE: + * ' function to left pad a binary number with zeros + * ' + * 'INPUTS: + * ' strNumber: binary number to left pad with zeros + * ' intLength: The desired bit length of the binary number + * ' + * 'RETURNS: + * ' string containing the binary representation of the input. + * ' + * 'CALLS: + * ' Nothing + * ' + * 'COMMENTS: + * '********************************************************************* +*** function fmtBinary(strNumber, intLength) fmtBinary = string(intLength - len(strNumber), "0") & strNumber end function '********************************************************************* +*** 'FUNCTION: + * ' ToBinary(intNumber) + * ' + * 'PURPOSE: + * ' convert an integer number to binary. + * ' + * 'Inputs: + * ' intNumber: Number to convert to binary + * ' + * 'Returns: + * ' string containing the binary representation of the input. + * ' + * 'Calls: + * ' Nothing + * ' + * 'Comments: + * ' note the use of \ (integer division operator) rather than / + * '********************************************************************* +*** function ToBinary(intNumber) if intNumber > 0 then ToBinary = ToBinary(intNumber\2) & intNumber mod 2 end if end function
Update: Thanks for all the help! I ended up using a different OID. Cisco doesn't support the Stack MIBs on all devices. Here's the script with the allowed VLANs.
use strict; use Net::SNMP; use Cisco::CopyConfig; my($error,$session,$seed_oid,$oid_root,$community,$hostname,$seed_ip,$ +found, $htype,$tftpsvr,$line,$key,$value,$sysname,$ssn); my ($osysname,$ochassis,$ossn,$oifdesc,$ovlanallowed);#OID constants my(%done,%devices,%ssndone, %shapes); my(@todo, @lines, @hostinfo); #The sysObjectID OID returns a vendor system identifier OID #Read OIDs and descriptions from file to translate the OIDs open (FH, "c:/CiscoSysObjIDs.csv"); while (<FH>){ chomp($_); @lines = split ",", $_; if ($lines[0]){ $devices{$lines[0]} = $lines[1]; } } close FH; #Define OID constants $osysname = "1.3.6.1.2.1.1.5.0"; $ochassis = "1.3.6.1.2.1.1.2.0"; $ossn = "1.3.6.1.2.1.47.1.1.1.1.11.1"; $oifdesc = "1.3.6.1.2.1.2.2.1.2"; #Vlan allowed OID will show only the VLANs not pruned. #Administratively allowed VLANs is 1.3.6.1.4.1.9.5.1.9.3.1.5 $ovlanallowed = "1.3.6.1.4.1.9.9.46.1.6.1.1.4"; $done{"0.0.0.0"}=1; #We need a starting point, get an IP from command line die "usage: $0 seedip" unless ( @ARGV == 1 ); $seed_ip = $ARGV[0]; die "usage: $0 seedip" unless ($seed_ip =~ m{\d+\.\d+\.\d+\.\d+}); $tftpsvr = "xxx.xxx.xxx.xxx"; @todo=($seed_ip); #List of possible targets $oid_root = "1.3.6.1.4.1.9.9.23.1.2.1.1"; $seed_oid = ("$oid_root".".3"); print "Host Name,Host IP,Host Type,Local Interface,Neighbor IP,Neighbo +r Name,Remote Interface,Neighbor Type,Vlans Allowed to Neighbor\n"; while(@todo){ #Grab a target and go to work $hostname= shift(@todo); $community = "comm1"; unless(exists $done{$hostname}){ #Make sure we haven't done this +one yet $done{$hostname}=1; #Remember that we checked this IP + #Open SNMP session ($session,$error) = Net::SNMP->session(Hostname => $hostname, +Community => $community); return unless($session); $sysname = Get_SNMP_Info($osysname); unless($sysname){ $community = "comm2"; Net::SNMP->session(Hostname => $hostn +ame, Community => $community); $sysname = Get_SNMP_Info($osysname); } unless($sysname){ $community = "comm3"; Net::SNMP->session(Hostname => $hostn +ame, Community => $community); $sysname = Get_SNMP_Info($osysname); } unless($sysname){$sysname = "Unknown device name";} $htype =(Get_SNMP_Info($ochassis)); $htype =~ s/1\.3\.6\.1\.4\.1\.9\.1\.//; if ($htype){ for my $key ( keys %devices ) { if ($htype eq $key) { $htype = $devices{$key} ;last; } } } else{$htype = "Unkown device type";} $ssn = Get_SNMP_Info($ossn); unless ($ssn){$ssn = "UknownSSN$found.$sysname.$hostna +me"};#Some devices do not support the entPhysical Table $ssndone{$ssn}=1; #Remember that we checked this ssn unless(exists $done{$ssn}){ #Make sure we haven't don +e this one yet print "$sysname,$hostname,$htype\n"; $found++; Get_Config($ +hostname,$community,$tftpsvr,$sysname.".cfg"); get_oids($seed_oid);#Get the SNMP info for thi +s target } $session->close; } } print $found."devices found\n"; #---------------------------------------------------------- #This sub finds out how many neighbors the target has #and determines what oids we need to use to get the info that #we want, then calls other subs to get that info #---------------------------------------------------------- sub get_oids{ my($starting_oid , $new_oid , $unique_oid , $result , $crap, $inde +x,$if_oid); my($ip , $name , $port, $type); $starting_oid = $_[0]; $new_oid = $starting_oid ; while(Net::SNMP::oid_context_match($starting_oid,$new_oid)){ $result = $session->get_next_request(($new_oid)); return unless (defined $result); ($new_oid , $crap) = %$result; if (Net::SNMP::oid_context_match($starting_oid,$new_oid)){ $unique_oid = $new_oid; $unique_oid =~ s/$starting_oid//g; $ip = (Convert_IP(Get_SNMP_Info("$oid_root".".4"."$unique_ +oid"))); $name = (Get_SNMP_Info("$oid_root".".6"."$unique_oid")); $port = (Get_SNMP_Info("$oid_root".".7"."$unique_oid")); $type = (Get_SNMP_Info("$oid_root".".8"."$unique_oid")); $if_oid = $unique_oid; $if_oid =~ s/\.\d+$//; my $ifdescr = Get_SNMP_Info($oifdesc.$if_oid); my $vlanallowed = Get_SNMP_Info($ovlanallowed.$if_oid); if ($vlanallowed){ $vlanallowed = Vlans_Allowed_Format($vlanallowed);} else{$vlanallowed = "Unavailable";} unless (($type=~/phone/i)||($type=~/server/i)||($type=~/at +a/i)){@todo=(@todo,$ip)}; #Ignore phones, servers, and ATA devices unless (($type=~/phone/i)||($type=~/server/i)||($type=~/at +a/i)){print ",,,$ifdescr,$ip,$name,$port,$type,\"$vlanallowed\"\n"}; get_oids($new_oid); } } } sub Convert_IP{ #This sub converts a hex IP to standard xxx.xxx.xxx.xx +x format my($ip , $result , $crap); my($hex1 , $hex2 , $hex3 , $hex4); my($oct1 , $oct2 , $oct3 , $oct4); my($hex_ip) = $_[0] ; if ((substr($hex_ip,0,1) eq "")|| ($hex_ip !~ /0x/)){ $ip = "0.0.0.0"; } else{ $hex_ip =~ s/0x//g; $hex1 = (substr $hex_ip,0,2); $hex2 = (substr $hex_ip,2,2); $hex3 = (substr $hex_ip,4,2); $hex4 = (substr $hex_ip,6,2); $oct1 = hex($hex1); $oct2 = hex($hex2); $oct3 = hex($hex3); $oct4 = hex($hex4); $ip = "$oct1\.$oct2\.$oct3\.$oct4"; } return $ip; } sub Get_SNMP_Info{ #This sub gets the value of an oid my($crap , $value , $result); my($oid) = $_[0]; $result = $session->get_request("$oid"); return unless (defined $result); ($crap , $value) = %$result; return $value; } sub Get_Config{ my ($hostip, $community, $tftp, $cfg_file, $copy); $hostip = shift; $community = shift; $tftp = shift; $cfg_file = shift; unless ($cfg_file) {$cfg_file = $hostip}; $copy = Cisco::CopyConfig->new(Host => $hostip, Comm => $community, Tmout => 15, Retry => 2); $copy->copy($tftp, $cfg_file); if ($copy->error){print "Couldn't get config for $hostip : ".$copy +->error."\n"}; } sub Convert_Octet_String{ my $octet_str = shift; my $basePort = 0; my ($index,$octet); my @octet; $octet_str = substr $octet_str, 2; while ($octet_str) { my $octet = hex substr $octet_str, 0, 2, ''; my $index = 0; while ($octet) { next unless $octet & 0x80; push @octet, $basePort + $index; } continue { ++$index; $octet = ($octet << 1) & 0xff; } $basePort += 8; } return @octet; } sub Vlans_Allowed_Format{ my ($vlans,$octets,$bits,$return); $vlans = shift; $octets = From_Hex($vlans); $bits = Reverse_Bit_Order($octets); $return = Range_Format($bits); if($return eq "1-1023"){ $return = "1-4094";#Assume the extended VLANs are allo +wed as well return $return; } return $return; } sub From_Hex { my ($hex) = @_ ; $hex =~ s/^0x//i ; return pack('H*', $hex) ; } sub Reverse_Bit_Order { my ($octets) = @_ ; return pack('B*', unpack('b*', $octets)) ; } sub Range_Format{ my $bits = shift; my $r = undef; my @s = (); for my $vn (1..length($bits) * 8) { if (vec($bits, $vn, 1)) { if (!defined($r)) { push @s, "$vn" ; $r = 0 ; +} else { $r = $vn ; } ; } else { if (defined($r)) { if ($r) { $s[-1] .= "-$r" ; } ; $r = undef ; } ; } } return join(', ',@s); }
In reply to Allowed VLANs with SNMP by spivey49
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |