Tuesday, September 17, 2013

A Perl script to collect information from Cisco IOS routers/switches


This is a journal about creating a perl script that establishes SSH session to Cisco IOS routers/switches and collect a list of information.



I started with:

google: perl script to ssh to cisco router
found: http://forums.freebsd.org/showthread.php?t=31396 posted by 'Business_Woman'. 

As per the post, she needed advice to enter the privileged EXEC mode, after a successful SSH session login

I checked my perl setup:
Platform: Windows 7
Perl: ActivePerl by ActiveState
Version: v5.10.1 (command: perl -v)

My perl doesn't support 'Net::SSH2'. Then:

google: Net::SSH2 for windows
found: http://www.perlmonks.org/?node_id=949926

I chose to try 1st option (cpan install) and as my network behind a proxy, then:

google: perl cpan set http_proxy
found: http://abhijit.name/setting_cpan_for_proxy.html

Followed the instructions, but got:

error:  LWP failed with code[301] message[Moved Permanently]
Warning: no success downloading 'C:\Perl\cpan\sources\authors\01mailrc.txt.gz.tmp7232'. Giving up on it. at C:\Perl\lib/CPAN/Index.pm line 225

Next, I tried the 2nd option, which was using Strawberry Perl. I downloaded and installed Strawberry Perl, which is now v5.18.1 (http://strawberryperl.com/). Now that I've Net::SSH2 library on my perl, I prepared my script based on 'Business_Woman' post and saved it as cisco.pl as follow:

use Net::SSH2;
use warnings;
use strict;
my $host = "10.10.1.1";            ### 10.10.1.1 is my target device
my $user = "admin";                 ### my ssh login is admin/cisco
my $password = "cisco";
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
my $command = $channel->exec("sh ver") or die $_;
my $output;
my $len = $channel->read($output,2048);

Running it by the command: perl cisco.pl
Bingo! At this point, I got the same condition with 'Business_Woman'. The next step is to find way to enter the privileged EXEC mode:

google: perl script show running-config cisco ==>results: not on target
google: perl script cisco privilege mode ==>results: not on target
google: net::SSH2 documentation ==>results: not on target
google: perl net::ssh2 examples
found: http://www.perlmonks.org/?node_id=569657

On this URL, I came to the part '#to run multiple commands use a shell', then I modified my script as follow:

use Net::SSH2;
use warnings;
use strict;
my $host = "10.10.1.1";            ### 10.10.1.1 is my target device
my $user = "admin";                 ### my ssh login is admin/cisco
my $password = "cisco";
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "cisco\n";     ### enable password is cisco
print $channel "term len 0\n";
print "LINE : $_" while <$channel>;
print $channel "show clock\n";
print "LINE : $_" while <$channel>;
print $channel "dir\n";
print "LINE : $_" while <$channel>;

After few trials and error, my script (last 9 lines) became:

my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "cisco\n";     ### enable password is cisco
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
print "$_" while <$channel>;

At this point, it had achieved my target. To further shape the script:
1) remove the passwords from the scripts
2) write the output to a file instead of screen

google: perl input argument
found: http://stackoverflow.com/questions/361752/how-can-i-pass-command-line-arguments-to-a-perl-program

google: perl write to file
found: http://perl.about.com/od/perltutorials/a/readwritefiles_2.htm

From the found URLs, I made my target host as input variable, as well as the SSH password and enable secret. With that my script has the flexibility to be used for any devices with any password (not hardcoded on the script). Secondly, I wrote the show information results onto a file and I kept it as a log file. My script became:

use Net::SSH2;
use warnings;
use strict;
my $host = $ARGV[0];            ### target host as input string1
my $user = "admin";
my $password = $ARGV[1];        ### ssh password as input string2
my $secret = $ARGV[2];        ### enable password as input string3
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "$secret\n";     ### enable password from input string3
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
print $OUTPUTFILE "$_" while <$channel>;
close $OUTPUTFILE or die "$OUTPUTFILE: $!";

Running it by the command: perl cisco.pl 10.10.1.1 cisco cisco
(My target host is '10.10.1.1', ssh password is 'cisco', enable password is 'cisco'; generated output filename is '10.10.1.1.log')
At this point, the result written on the file contained many newlines between the messages, so:

google: perl trim empty lines  ==>results: not on target
google: perl remove newline from $_ ==>results: not on target
google: perl $_ condition
found: http://perlmaven.com/the-default-variable-of-perl

This URL shows the elegant way of using $_ (perl default variable) and from the example, it shows the usage of 'chomp' too. I modified my script (last 6 lines) as follow:

open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
while (<$channel>) {
chomp;
print $OUTPUTFILE "$_";
} close $OUTPUTFILE or die "$OUTPUTFILE: $!";

This is the end of the journal, my script is now looks like the following:
#
# usage: perl cisco.pl (target_host) (ssl_password) (enable_password)
#
use Net::SSH2;
use warnings;
use strict;
my $host = $ARGV[0];            ### target host as input string1
my $user = "admin";
my $password = $ARGV[1];        ### ssh password as input string2
my $secret = $ARGV[2];        ### enable password as input string3
my $ssh = Net::SSH2->new();
if(!$ssh->connect($host)){
        print("Connection Failed\n");
        exit(1);
}
if(!$ssh->auth_password($user,$password)){
        print("Authentication Failed");
        exit(1);
}
my $channel = $ssh->channel();
$channel->blocking(0);
$channel->shell();
print $channel "enable\n";
print $channel "$secret\n";     ### enable password from input string3
print $channel "term len 0\n";
print $channel "show clock\n";
print $channel "dir\n";
open (my $OUTPUTFILE, ">$host.log") or die "Can't open $host.log: $!";   ### target host as filename
while (<$channel>) {
chomp;
print $OUTPUTFILE "$_";
}
 
close $OUTPUTFILE or die "$OUTPUTFILE: $!";