#! /usr/local/bin/perl -w # -*- mode: Perl -*- ############################################################################## # # genRtrConfig - Generate Cricket config tree for a Cisco router. # # See README.genRtrConfig for more info. # # Copyright (C) 2000 Mike Fisher and Tech Data Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # Created: 03/09/00 - Mike Fisher (no spam!) # Based on MRTG 2.8.8 cfgmaker by Tobias Oetiker # and Cricket 0.71 listInterface by Jeff Allen # # 03/21/00 - Mike Fisher # Misc bug fixes. Reroll for release. # ############################################################################## BEGIN { $gInstallRoot = (($0 =~ m:^(.*/):)[0] || "./") . ".."; } use lib "$gInstallRoot/lib"; use strict; no strict "refs"; use snmpUtils; use Common::Log; use Socket; my(%ifType_d)=('1'=>'Other', '2'=>'regular1822', '3'=>'hdh1822', '4'=>'ddnX25', '5'=>'rfc877x25', '6'=>'ethernetCsmacd', '7'=>'iso88023Csmacd', '8'=>'iso88024TokenBus', '9'=>'iso88025TokenRing', '10'=>'iso88026Man', '11'=>'starLan', '12'=>'proteon10Mbit', '13'=>'proteon80Mbit', '14'=>'hyperchannel', '15'=>'fddi', '16'=>'lapb', '17'=>'sdlc', '18'=>'ds1', '19'=>'e1', '20'=>'basicISDN', '21'=>'primaryISDN', '22'=>'propPointToPointSerial', '23'=>'ppp', '24'=>'softwareLoopback', '25'=>'eon', '26'=>'ethernet-3Mbit', '27'=>'nsip', '28'=>'slip', '29'=>'ultra', '30'=>'ds3', '31'=>'sip', '32'=>'frame-relay', '33'=>'rs232', '34'=>'para', '35'=>'arcnet', '36'=>'arcnetPlus', '37'=>'atm', '38'=>'miox25', '39'=>'sonet', '40'=>'x25ple', '41'=>'iso88022llc', '42'=>'localTalk', '43'=>'smdsDxi', '44'=>'frameRelayService', '45'=>'v35', '46'=>'hssi', '47'=>'hippi', '48'=>'modem', '49'=>'aal5', '50'=>'sonetPath', '51'=>'sonetVT', '52'=>'smdsIcip', '53'=>'propVirtual', '54'=>'propMultiplexor', '55'=>'100BaseVG', #### New IF Types added 9/24/98 by Russ Carleton #### (roccor@livenetworking.com) #### based on the IANA file updated at #### ftp://ftp.isi.edu/mib/ianaiftype.mib '56'=>'Fibre Channel', '57'=>'HIPPI Interface', '58'=>'Obsolete for FrameRelay', '59'=>'ATM Emulation of 802.3 LAN', '60'=>'ATM Emulation of 802.5 LAN', '61'=>'ATM Emulation of a Circuit', '62'=>'FastEthernet (100BaseT)', '63'=>'ISDN & X.25', '64'=>'CCITT V.11/X.21', '65'=>'CCITT V.36', '66'=>'CCITT G703 at 64Kbps', '67'=>'Obsolete G702 see DS1-MIB', '68'=>'SNA QLLC', '69'=>'Full Duplex Fast Ethernet (100BaseFX)', '70'=>'Channel', '71'=>'Radio Spread Spectrum (802.11)', '72'=>'IBM System 360/370 OEMI Channel', '73'=>'IBM Enterprise Systems Connection', '74'=>'Data Link Switching', '75'=>'ISDN S/T Interface', '76'=>'ISDN U Interface', '77'=>'Link Access Protocol D (LAPD)', '78'=>'IP Switching Opjects', '79'=>'Remote Source Route Bridging', '80'=>'ATM Logical Port', '81'=>'AT&T DS0 Point (64 Kbps)', '82'=>'AT&T Group of DS0 on a single DS1', '83'=>'BiSync Protocol (BSC)', '84'=>'Asynchronous Protocol', '85'=>'Combat Net Radio', '86'=>'ISO 802.5r DTR', '87'=>'Ext Pos Loc Report Sys', '88'=>'Apple Talk Remote Access Protocol', '89'=>'Proprietary Connectionless Protocol', '90'=>'CCITT-ITU X.29 PAD Protocol', '91'=>'CCITT-ITU X.3 PAD Facility', '92'=>'MultiProtocol Connection over Frame/Relay', '93'=>'CCITT-ITU X213', '94'=>'Asymetric Digitial Subscriber Loop (ADSL)', '95'=>'Rate-Adapt Digital Subscriber Loop (RDSL)', '96'=>'Symetric Digitial Subscriber Loop (SDSL)', '97'=>'Very High Speed Digitial Subscriber Loop (HDSL)', '98'=>'ISO 802.5 CRFP', '99'=>'Myricom Myrinet', '100'=>'Voice recEive and transMit (voiceEM)', '101'=>'Voice Foreign eXchange Office (voiceFXO)', '102'=>'Voice Foreign eXchange Station (voiceFXS)', '103'=>'Voice Encapulation', '104'=>'Voice Over IP Encapulation', '105'=>'ATM DXI', '106'=>'ATM FUNI', '107'=>'ATM IMA', '108'=>'PPP Multilink Bundle', '109'=>'IBM IP over CDLC', '110'=>'IBM Common Link Access to Workstation', '111'=>'IBM Stack to Stack', '112'=>'IBM Virtual IP Address (VIPA)', '113'=>'IBM Multi-Protocol Channel Support', '114'=>'IBM IP over ATM', '115'=>'ISO 802.5j Fiber Token Ring', '116'=>'IBM Twinaxial Data Link Control (TDLC)', '117'=>'Gigabit Ethernet', '118'=>'Higher Data Link Control (HDLC)', '119'=>'Link Access Protocol F (LAPF)', '120'=>'CCITT V.37', '121'=>'CCITT X.25 Multi-Link Protocol', '122'=>'CCITT X.25 Hunt Group', '123'=>'Transp HDLC', '124'=>'Interleave Channel', '125'=>'Fast Channel', '126'=>'IP (for APPN HPR in IP Networks)', '127'=>'CATV MAC Layer', '128'=>'CATV Downstream Interface', '129'=>'CATV Upstream Interface', '130'=>'Avalon Parallel Processor', '131'=>'Encapsulation Interface', '132'=>'Coffee Pot', '133'=>'Circuit Emulation Service', '134'=>'ATM Sub Interface', '135'=>'Layer 2 Virtual LAN using 802.1Q', '136'=>'Layer 3 Virtual LAN using IP', '137'=>'Layer 3 Virtual LAN using IPX', '138'=>'IP Over Power Lines', '139'=>'Multi-Media Mail over IP', '140'=>'Dynamic synchronous Transfer Mode (DTM)', '141'=>'Data Communications Network', '142'=>'IP Forwarding Interface' ); # Interface types that we don't deal with for one reason or another. Index # to the list ifType_d above. my @DONTCOLLECT = (1, 18, 24, 76, 77, 81); ############################################################################### ### MIB OID definitions. ############################################################################### my %OID = ( ### from mib-2.system. 'sysDescr' => '1.3.6.1.2.1.1.1.0', 'sysObjectID' => '1.3.6.1.2.1.1.2.0', 'sysContact' => '1.3.6.1.2.1.1.4.0', 'sysName' => "1.3.6.1.2.1.1.5.0", 'sysLocation' => "1.3.6.1.2.1.1.6.0", ### from mib-2.interfaces.ifTable.ifEntry 'ifDescr' => "1.3.6.1.2.1.2.2.1.2", 'ifType' => "1.3.6.1.2.1.2.2.1.3", 'ifSpeed' => "1.3.6.1.2.1.2.2.1.5", 'ifAdminStatus' => "1.3.6.1.2.1.2.2.1.7", 'ifOperStatus' => "1.3.6.1.2.1.2.2.1.8", ### from mib-2.ifMIB.ifMIBObjects.ifXTable.ifXEntry 'ifAlias' => '1.3.6.1.2.1.31.1.1.1.18', ### from mib-2.ip.ipAddrTable.ipAddrEntry 'ipAdEntAddr' => '1.3.6.1.2.1.4.20.1.1', 'ipAdEntIfIndex' => '1.3.6.1.2.1.4.20.1.2', ### from mib-2.entityMIB.entityMIBObjects.entityPhysical. ### entPhysicalTable.entPhysicalEntry 'entPhysicalDescr.1' => '1.3.6.1.2.1.47.1.1.1.1.2.1', ### from cisco.local.linterfaces.lifTable.lifEntry 'CiscolocIfDescr' => '1.3.6.1.4.1.9.2.2.1.1.28', ); ############################################################################### sub usage { #' print STDERR <>targets EOD #' exit(1); } ############################################################################### my($script) = $0 =~ /\/([^\/]+)$/; my $savedargs = join(" ", @ARGV); my $community = "public"; my $units = "bits"; my $namedonly = 0; my $addcomm = 0; my $verbose = 0; my $ciscobox = 1; my $nodupwarn = 0; my $chassisstats = 0; while ($ARGV[0] =~ /^-/) { my $arg = shift; if ($arg eq '--bytes') { $units = "bytes"; } elsif ($arg eq '-c') { $community = shift; } elsif ($arg eq '-C') { $community = shift; $addcomm = 1; } elsif ($arg eq '--chassis') { $chassisstats = 1; } elsif ($arg eq '-h') { usage(); } elsif ($arg eq '--namedonly') { $namedonly = 1; } elsif ($arg eq '--nodupwarn') { $nodupwarn = 1; } elsif ($arg eq '-v') { $verbose = 1; } else { print STDERR "Error: Unknown flag: $arg\n"; exit 1; } } usage() if (@ARGV != 1); my $router = $ARGV[0]; my $snmp = "$community\@$router"; my $file; ### Get all the data we need first... my($sysObjectID) = snmpUtils::get($snmp, $OID{'sysObjectID'}); if (!defined $sysObjectID) { print STDERR "ERROR: unable to retrieve sysObjectID from $router\n"; exit; } elsif ($sysObjectID !~ /^1\.3\.6\.1\.4\.1\.9/) { print STDERR "WARNING: $router doesn't appear to be a cisco box. ", "Results may vary...\n"; $ciscobox = 0; $chassisstats = 0; $namedonly = 0; } my($ciscoObjectID) = $sysObjectID !~ /^1\.3\.6\.1\.4\.1\.9\.(.*)/; ### Get basic system info. my($sysDescr) = snmpUtils::get($snmp, $OID{'sysDescr'}); my($sysContact) = snmpUtils::get($snmp, $OID{'sysContact'}); my($sysName) = snmpUtils::get($snmp, $OID{'sysName'}); my($sysLocation) = snmpUtils::get($snmp, $OID{'sysLocation'}); ### Get chassis info if it's a Cisco. my($chassisPhysicalDescr); if ($ciscobox) { ($chassisPhysicalDescr) = snmpUtils::get($snmp, $OID{'entPhysicalDescr.1'}); } ### Get address to interface mapping info. my %ipadentaddr = gettable('ipAdEntAddr'); my %ipadentif = gettable('ipAdEntIfIndex'); ### Get interface info. my %ifdescr = gettable('ifDescr'); my %iftype = gettable('ifType'); my %ifspeed = gettable('ifSpeed'); my %ifadminstatus = gettable('ifAdminStatus'); my %ifoperstatus = gettable('ifOperStatus'); ### May need the cisco IOS version number so we know which oid to use ### to get the cisco description. ### ### - mjd 2/5/98 (Mike Diehn) (mdiehn@mindspring.net) my ($cisco_ver, $cisco_descr_oid, %ciscodescr); if ($ciscobox) { ($cisco_ver) = ($sysDescr =~ m/Version\s+([\d\.]+)\(\d/o); $cisco_descr_oid = ($cisco_ver ge "11.2") ? "ifAlias" : "CiscolocIfDescr"; %ciscodescr = gettable($cisco_descr_oid); } ### Generate the config... my $sysDescr_orig = $sysDescr; $sysDescr_orig =~ s/[\r\n ]+/ /g; $sysDescr =~ s/[\r\n]+/
/g; # Change returns to
if (! -d $router) { mkdir($router,0755) || die "Can't mkdir $router"; } openfile($file = "$router/targets"); print $file < $router, 'directory-desc' => $sysLocation, ); push(@config, 'snmp-community' => $community) if ($addcomm); if ($chassisstats) { writetarget($file, '--default--', '', @config); ### Figure out what kind of box we're talking to and create a target ### to report chassis stats. my($model) = $sysDescr =~ /IOS\s+\(tm\)\s+(\S+)/; if ($model eq "7500") { @config = ('target-type' => 'Cisco-7500-Router'); } elsif ($model eq "7200") { @config = ('target-type' => 'Cisco-7200-Router'); } elsif ($model eq "7000") { @config = ('target-type' => 'Cisco-7000-Router'); } elsif ($model =~ /3600/) { @config = ('target-type' => 'Cisco-3600-Router'); } elsif ($model =~ /2600/) { @config = ('target-type' => 'Cisco-2600-Router'); } elsif ($model =~ /2500/) { @config = ('target-type' => 'Cisco-2500-Router'); } else { @config = ('target-type' => 'Cisco-Unknown-Router'); } my ($ldesc, $sdesc); if (defined $chassisPhysicalDescr) { $ldesc = "$chassisPhysicalDescr
$sysDescr"; ($sdesc) = $chassisPhysicalDescr =~ /^\s*([^,]+)/; } else { $ldesc = $sysDescr; $sdesc = "$model class chassis"; } push(@config, ('long-desc' => $ldesc, 'short-desc' => $sdesc, )); writetarget($file, 'Chassis', '', @config); @config = (); } ### Add to defaults for interfaces. push(@config, 'target-type' => 'standard-interface', 'inst' => 'map(interface-name)', 'long-desc' => '%short-desc%', 'display-name' => '%interface-name%', ); writetarget($file, '--default--', '', @config); ### Build an IP address/hostname to interface instance map. my($index, %ipaddr, %iphost); foreach $index (keys %ipadentaddr) { $ipaddr{$ipadentif{$index}} = $ipadentaddr{$index}; $iphost{$ipadentif{$index}} = gethostbyaddr(pack('C4',split(/\./, $ipaddr{$ipadentif{$index}})), AF_INET); if (!defined $iphost{$ipadentif{$index}} || ($iphost{$ipadentif{$index}} eq '')){ $iphost{$ipadentif{$index}} = '(no hostname)'; } } ### Do some preprocessing on the interface list. my %unique; my $unique = 1; foreach $index (keys %ifdescr) { if ($ciscobox && ($cisco_ver lt "11.2" && $iftype{$index} == '32') && !defined $ciscodescr{$index}) { ### This is a frame-relay sub-interface *and* the router ### is running an IOS older than 11.2. Therefore, we can ### get neither ifAlias nor ciscoLocIfDesc. Do something ### useful. $ciscodescr{$index} = "Cisco PVCs descriptions require IOS 11.2+."; } ### Figure out whether the interface description is unique or not. ### (if not, we'll add explicit inst numbers later...) if (defined $unique{$ifdescr{$index}}) { $unique{$ifdescr{$index}} = 0; $unique = 0; } else { $unique{$ifdescr{$index}} = 1; } } if (!$unique) { print $file < Warning: Duplicate interface descriptions have forced the use of hard coded instance numbers for interfaces marked with *. Config file should be regenerated after any hardware or interface config changes. EOD } ### Write out the interface targets. foreach $index ( sort { $a <=> $b } keys %ifdescr) { my $sdesc = ''; my $ldesc = ''; my $c = ''; my $b = ($units eq "bytes") ? 8 : 1; my $speed = int($ifspeed{$index} / $b); # bits to bytes my $speed_str = fmi($speed, $units); my $name = (defined $iphost{$index}) ? $iphost{$index} : ''; # $name = "$router.$index" if not $name or $name =~ /\s/; ### See if we need to comment this interface out for various reasons... if ($ifoperstatus{$index} == 3 || $ifadminstatus{$index} != 1) { print $file "### Interface administratively down or in test mode.\n#\n"; $c="# "; } elsif ($speed == 0) { print $file "### Interface has an insane speed.\n"; $c="# "; } elsif (grep(/^$iftype{$index}$/,@DONTCOLLECT)) { print $file "### Interface type is $ifType_d{$iftype{$index}}. ", "Can't deal with that.\n#\n"; $c="# "; } elsif ($namedonly && (!defined $ciscodescr{$index} || $ciscodescr{$index} eq "")) { print $file "#### Interface has no name and --namedonly specified.\n#\n"; $c="# "; } ### Build the target name and description strings. If the interface ### description is not unique, append the numeric interface type to ### the target name to make it unique. my($target) = $ifdescr{$index}; $target =~ s/[\/\s:]/\_/g; $target .= "_T$iftype{$index}" if (!$unique{$ifdescr{$index}}); $ldesc = ((defined $ciscodescr{$index}) ? $ciscodescr{$index} : ''); $ldesc .= "
$name" if ($name); $ldesc .= "
" . ((defined $ipaddr{$index}) ? $ipaddr{$index} : ''); $ldesc .= "
$speed_str $ifType_d{$iftype{$index}}"; if (defined $ciscodescr{$index}) { $sdesc = $ciscodescr{$index}; } elsif ($ipaddr{$index}) { $sdesc = $ipaddr{$index}; } else { $sdesc = "$speed_str $ifType_d{$iftype{$index}}"; } @config = (); ### Deal with non-unique interface descriptions by hard coding the ### instance number. if (!$unique{$ifdescr{$index}}) { push(@config, 'inst' => $index); push(@config, 'display-name' => "$ifdescr{$index} *" ) if (!$nodupwarn); } push(@config, 'interface-name' => $ifdescr{$index}, 'short-desc' => $sdesc, 'long-desc' => $ldesc, ); push(@config, 'inst' => $index) if (!$unique{$ifdescr{$index}}); push(@config, ### frame-relay 'target-type' => 'sub-interface') if ($iftype{$index} == 32); writetarget($file, $target, $c, @config); } close($file); exit; ############################################################################### sub fmi { my($number, $units) = @_; my @short; if ($units eq "bytes") { @short = ("Bytes/s","kBytes/s","MBytes/s","GBytes/s"); } else { @short = ("Bits/s","kBits/s","MBits/s","GBits/s"); } my $digits = length("".$number); my $divm = 0; while ($digits - $divm*3 > 4) { $divm++; } my $divnum = $number/10**($divm*3); return sprintf("%1.1f %s", $divnum, $short[$divm]); } ############################################################################### # # Open the named file and write a header comment. sub openfile { my($file) = @_; open($file, ">$file") || die "Can't open $file"; print($file "# Generated by $script\n", "# Date: ", scalar(localtime(time)), "\n", "# Args: $savedargs\n\n"); } ############################################################################### sub writepair { no strict 'refs'; my($file, $name, $value, $comment) = @_; $comment = '' if (!defined $comment); my $quote = ''; my $tabs = int(((24 - length($name))/8) + 0.5); if (defined($value)) { # quote empty (or white-space only) lines, or lines which # will have embedded spaces. $quote = '"' if ($value =~ /^\s*$/ || $value =~ /\s/); print($file "$comment $name", "\t"x$tabs, "= $quote$value$quote\n"); } } ############################################################################### sub writetarget { no strict 'refs'; my($file, $name, $comment, %value) = @_; my $key; print $file "${comment}target $name\n"; foreach $key (sort keys %value) { writepair($file, $key, $value{$key}, $comment); } print $file "\n"; } ############################################################################### sub gettable { my($name) = @_; if (!defined $OID{$name}) { print STDERR "ERROR: $name is undefined"; return undef; } print "Walking $OID{$name}... " if ($verbose); my @data = snmpUtils::walk($snmp,$OID{$name}); my @tmp = map({split(/:/,$_,2)} @data); print scalar(@tmp)/2,"\n" if ($verbose); return @tmp; } ###############################################################################